Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions analyzers/context_propagation.go
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,22 @@ func isCancelCalled(cancelValue ssa.Value, allFuncs []*ssa.Function) bool {
return true
}
}
// Cancel stored into a slice/array element (e.g.
// `defers = append(defers, cancel)`, which lowers to a Store
// into the variadic array). The cancel escapes into a
// collection whose iteration sites are not reliably traceable
// in SSA — treat as a transfer of responsibility, mirroring
// the global/returned-field handling above.
if _, ok := r.Addr.(*ssa.IndexAddr); ok {
return true
}
queue = append(queue, r.Addr)
case *ssa.MapUpdate:
// Cancel stored as a map value (e.g.
// `cleanups[key] = cancel`). Same reasoning as IndexAddr.
if r.Value == current {
return true
}
case *ssa.UnOp:
if r.Op == token.MUL && r.X == current {
queue = append(queue, r)
Expand Down
65 changes: 65 additions & 0 deletions testutils/g118_samples.go
Original file line number Diff line number Diff line change
Expand Up @@ -1871,5 +1871,70 @@ func setup() {
defer cancel()
}()
}
`}, 0, gosec.NewConfig()},

// Safe: cancel appended to a cleanup slice that is iterated by a
// deferred closure (issue #1668)
{[]string{`
package main

import (
"context"
"time"
)

func main() {
defers := make([]func(), 0, 1)
defer func() {
for _, fn := range defers {
fn()
}
}()

_, cancel := context.WithTimeout(context.Background(), time.Second)
defers = append(defers, cancel)
}
`}, 0, gosec.NewConfig()},

// Safe: cancel appended to a slice that is returned to the caller
{[]string{`
package main

import "context"

func cleanups(ctx context.Context) []func() {
var fns []func()
_, cancel := context.WithCancel(ctx)
fns = append(fns, cancel)
return fns
}
`}, 0, gosec.NewConfig()},

// Safe: cancel stored as a map value (tests MapUpdate handling)
{[]string{`
package main

import "context"

func register(ctx context.Context, key string) map[string]context.CancelFunc {
m := make(map[string]context.CancelFunc)
_, cancel := context.WithCancel(ctx)
m[key] = cancel
return m
}
`}, 0, gosec.NewConfig()},

// Safe: cancel placed into a fixed-size array element
{[]string{`
package main

import "context"

func arrayStore(ctx context.Context) [1]context.CancelFunc {
var arr [1]context.CancelFunc
_, cancel := context.WithCancel(ctx)
arr[0] = cancel
return arr
}
`}, 0, gosec.NewConfig()},
}
Loading