Skip to content

Commit

Permalink
Merge pull request #60 from trailofbits/hanging-gorouting-fixes
Browse files Browse the repository at this point in the history
Hanging gorouting fixes
  • Loading branch information
mschwager authored Jun 6, 2024
2 parents c81344f + b1e14cc commit a685fac
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 45 deletions.
102 changes: 98 additions & 4 deletions go/hanging-goroutine.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ var (
)

func main() {
req1(1)
time.Sleep(time.Second * 5)
fmt.Println(result)
fmt.Println(runtime.NumGoroutine())
req6(1, false)
fmt.Println("Result: ", result)
fmt.Println("Goroutines (must be 1 for FPs):", runtime.NumGoroutine())
}

func req1(timeout time.Duration) string {
Expand Down Expand Up @@ -85,6 +84,101 @@ func req2_FP(timeout time.Duration) string {
}
}

func req3(timeout time.Duration) {
ch := make(chan string)
// ruleid: hanging-goroutine
for i := 0; i < 3; i++ {
go func() {
newData := test()
ch <- newData // block
}()
}
result = <- ch
fmt.Println("finished req3")
}

func req3_FP(timeout time.Duration) {
ch := make(chan string, 3)
// ok: hanging-goroutine
for i := 0; i < 3; i++ {
go func() {
newData := test()
ch <- newData // block
}()
}
result = <- ch
fmt.Println("finished req3")
}

func req4_FP(timeout time.Duration) string {
ch := make(chan string)
// ok: hanging-goroutine
go func() {
newData := test()
ch <- newData // block
}()
select {
case result = <- ch:
fmt.Println("case result")
return result
case <- time.After(timeout):
result = <- ch
fmt.Println("case time.Afer")
return ""
}
}

func req5_FP(timeout time.Duration) {
ch := make(chan string)
tick := time.Tick(100 * time.Millisecond)
quit := time.After(2 * time.Second)
// ok: hanging-goroutine
go func() {
newData := test()
ch <- newData // block
}()
for {
select {
case <-tick:
fmt.Print("|")
case <-quit:
result = <- ch
fmt.Println("\nquit")
return
default:
fmt.Print(".")
time.Sleep(50 * time.Millisecond)
}
}
}

func req6(timeout time.Duration, doclose bool) {
ch := make(chan string)
tick := time.Tick(100 * time.Millisecond)
quit := time.After(2 * time.Second)
// todoruleid: hanging-goroutine
go func() {
newData := test()
ch <- newData // block
}()
for {
select {
case <-tick:
fmt.Print("|")
case <-quit:
if doclose {
result = <- ch
}
fmt.Println("\nquit")
return
default:
fmt.Print(".")
time.Sleep(50 * time.Millisecond)
}
}
}


func test() string {
time.Sleep(time.Second * 2)
return "very important data"
Expand Down
58 changes: 17 additions & 41 deletions go/hanging-goroutine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,6 @@ rules:
...
$Y = <- $CHANNEL
...
- pattern: |
for ... {
...
go func(...) {
...
$CHANNEL <- $VAL
...
}(...)
}
...
$Y := <- $CHANNEL
...
- pattern: |
for ... {
...
Expand Down Expand Up @@ -81,18 +69,6 @@ rules:
case $Y = <- $CHANNEL:
...
}
- pattern: |
go func(...){
...
$CHANNEL <- $X
...
}(...)
...
select {
case ...
case $Y := <- $CHANNEL:
...
}
- pattern: |
go func(...){
...
Expand All @@ -117,31 +93,34 @@ rules:
case $Y <- $CHANNEL:
...
}
- pattern-not: |
for ... {
...
go func(...) {
...
$CHANNEL <- $VAL
...
}(...)
}
...
$Y = <- $CHANNEL
...
- pattern-inside: |
$CHANNEL := make(...)
...
- pattern-not-inside: |
$CHANNEL := make(..., $T)
...
# heuristics to limit FPs, we may miss some leaks because of these
- pattern-not: |
go func(...){
...
$CHANNEL <- $X
...
}(...)
...
select {
case ...
case ...:
...
... =<- $CHANNEL
... = <- $CHANNEL
...
}
- pattern-not-inside: |
- pattern-not: |
go func(...){
...
$CHANNEL <- $X
...
}(...)
...
select {
case ...
Expand All @@ -150,6 +129,3 @@ rules:
<-$CHANNEL
...
}
- pattern-not-inside: |
$CHANNEL := make(..., $T)
...

0 comments on commit a685fac

Please sign in to comment.