trailofbits.go.waitgroup-add-called-inside-goroutine.waitgroup-add-called-inside-goroutine

profile photo of trailofbitstrailofbits
Author
232
Download Count*

Calling $WG.Add inside of an anonymous goroutine may result in $WG.Wait waiting for more or less calls to $WG.Done() than expected

Run Locally

Run in CI

Defintion

rules:
  - id: waitgroup-add-called-inside-goroutine
    message: >
      Calling `$WG.Add` inside of an anonymous goroutine may result in
      `$WG.Wait`

      waiting for more or less calls to `$WG.Done()` than expected
    languages:
      - go
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-667: Improper Locking"
      subcategory:
        - vuln
      confidence: MEDIUM
      likelihood: MEDIUM
      impact: MEDIUM
      technology:
        - --no-technology--
      description: Calls to `sync.WaitGroup.Add` inside of anonymous goroutines
      references:
        - https://go101.org/article/concurrent-common-mistakes.html
      license: AGPL-3.0 license
      vulnerability_class:
        - Other
    patterns:
      - pattern-either:
          - pattern: |
              $WG := &sync.WaitGroup{}
              ...
              go func(...) {
                ...
                $WG.Add(...)
                ...
              }(...)
              ...
              $WG.Wait()
          - pattern: |
              var $WG sync.WaitGroup
              ...
              go func(...) {
                ...
                $WG.Add(...)
                ...
              }(...)
              ...
              $WG.Wait()
      - pattern-not-inside: |
          for ... {
            ...
            $WG.Add(...)
            ...
          }

Examples

waitgroup-add-called-inside-goroutine.go

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

package main

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

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


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

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


func test2() {
	// ok: waitgroup-add-called-inside-goroutine
	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()
		}()
	}

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