trailofbits.go.waitgroup-wait-inside-loop.waitgroup-wait-inside-loop

profile photo of trailofbitstrailofbits
Author
232
Download Count*

Calling $WG.Wait() inside a loop blocks the call to $WG.Done()

Run Locally

Run in CI

Defintion

rules:
  - id: waitgroup-wait-inside-loop
    message: Calling `$WG.Wait()` inside a loop blocks the call to `$WG.Done()`
    languages:
      - go
    severity: WARNING
    metadata:
      category: security
      cwe: "CWE-667: Improper Locking"
      subcategory:
        - vuln
      confidence: MEDIUM
      likelihood: MEDIUM
      impact: MEDIUM
      technology:
        - --no-technology--
      description: Calls to `sync.WaitGroup.Wait` inside a loop
      references:
        - https://go101.org/article/concurrent-common-mistakes.html
      license: AGPL-3.0 license
      vulnerability_class:
        - Other
    patterns:
      - pattern-either:
          - pattern: |
              var $WG sync.WaitGroup
              ...
              for ... {
                ...
                go func(...){
                  ...
                  defer $WG.Done()
                  ...
                }()
                ...
                $WG.Wait()
                ...
              }
          - pattern: |
              $WG := &sync.WaitGroup{}
              ...
              for ... {
                ...
                go func(...){
                  ...
                  defer $WG.Done()
                  ...
                }()
                ...
                $WG.Wait()
                ...
              }
          - pattern: |
              var $WG sync.WaitGroup
              ...
              for ... {
                ...
                go func(...){
                  ...
                  $WG.Done()
                  ...
                }()
                ...
                $WG.Wait()
                ...
              }
          - pattern: |
              $WG := &sync.WaitGroup{}
              ...
              for ... {
                ...
                go func(...){
                  ...
                  $WG.Done()
                  ...
                }()
                ...
                $WG.Wait()
                ...
              }

Examples

waitgroup-wait-inside-loop.go

// Code from https://go101.org/article/concurrent-common-mistakes.html

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {
	test1()
	test2()
	test3()
}

func test1() {
	// ruleid: waitgroup-wait-inside-loop
	var wg sync.WaitGroup
	var x int32 = 0
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			atomic.AddInt32(&x, 1)
		}()
		wg.Wait()
	}

	fmt.Println("Wait ...")
	fmt.Println(atomic.LoadInt32(&x))
}

func test2() {
	// ruleid: waitgroup-wait-inside-loop
	var wg sync.WaitGroup
	var x int32 = 0
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			atomic.AddInt32(&x, 1)
			wg.Done()
		}()
		wg.Wait()
	}

	fmt.Println("Wait ...")
	fmt.Println(atomic.LoadInt32(&x))
}


func test3() {
	// ok: waitgroup-wait-inside-loop
	var wg sync.WaitGroup
	var x int32 = 0
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			atomic.AddInt32(&x, 1)
		}()
	}
	
	fmt.Println("Wait ...")
	wg.Wait()
	fmt.Println(atomic.LoadInt32(&x))
}