Attacker-controlled ctx objects retained by bounded queue
low swesweep-fasthttp · stackless/func.go · NewFunc · run #4
Description
The returned wrapper accepts any ctx value and enqueues it in funcWorkCh. While queued or running, ctx and all objects it references are retained. The channel capacity is runtime.GOMAXPROCS(-1)*2048, and GOMAXPROCS can be large. If ctx carries large attacker-controlled payloads and many calls are accepted before workers drain the queue, memory usage can grow substantially. There is no per-ctx size limit, no queue occupancy accounting beyond the channel buffer, and no way to cancel queued work.
Triage
Not triaged.
Raw scan report for this file
Step-by-step security trace:
- `NewFunc` validates that `f` is not nil, creates a bounded worker channel, and lazily starts `runtime.GOMAXPROCS(-1)` worker goroutines. The returned closure is intended to be callable concurrently.
- The returned closure gets a pooled `funcWork`, stores `ctx`, tries to enqueue the work, and blocks on `fw.done` before reusing/returning the pooled object.
- `funcWorker` consumes `funcWork` items, calls `f(fw.ctx)`, then signals completion via `fw.done`.
- `getFuncWork` uses `sync.Pool` and type-asserts `v.(*funcWork)`. This is currently safe because the pool is package-private and only receives `*funcWork` values.
- `putFuncWork` clears `ctx` and returns the object to the pool.
There is no direct memory-corruption bug in this file: no unchecked buffer copies, no slice bounds, no unsafe usage, and no obvious data race on the shared `funcWork` state under normal operation. The main risks are lifecycle, concurrency, and availability issues.
```json
[
{
"severity": "medium",
"title": "Permanent worker goroutines and channel per NewFunc instance",
"function": "NewFunc",
"description": "Each NewFunc call creates funcWorkCh and, on first use, starts runtime.GOMAXPROCS(-1) funcWorker goroutines that range over the channel until it is closed. The channel is never closed and the package provides no Close/Stop API. If callers create many wrappers, for example per tenant, per connection, or under attacker influence, the goroutines, channel buffers, captured f, and any state closed over by f are retained for the lifetime of the process. Dropping the returned wrapper does not terminate the workers. This can lead to goroutine and memory exhaustion, causing denial of service."
},
{
"severity": "medium",
"title": "Hidden worker can be terminated by f panic or runtime.Goexit",
"function": "funcWorker",
"description": "funcWorker calls f(fw.ctx) in a separate goroutine and then sends on fw.done. If malformed ctx causes f to panic, an unrecovered panic terminates the host process. If f calls runtime.Goexit, the worker goroutine exits without sending fw.done, leaving the caller blocked forever on <-fw.done. Because execution is moved off the caller's goroutine, panics that might otherwise be recoverable if f were invoked synchronously cannot be recovered by the caller. Untrusted data reaching f can therefore turn a bad input into a process crash or hang."
},
{
"severity": "low",
"title": "No timeout, cancellation, or execution bound for f",
"function": "funcWorker",
"description": "The wrapper blocks on <-fw.done until f returns. If f blocks on channels, I/O, or re-enters a stackless wrapper whose worker pool is saturated, workers can remain busy and callers can hang. There is no maximum execution time, timeout, or cancellation mechanism. The documentation suggests f should not block, but malformed input that causes f to block can still become an availability issue."
},
{
"severity": "low",
"title": "Attacker-controlled ctx objects retained by bounded queue",
"function": "NewFunc",
"description": "The returned wrapper accepts any ctx value and enqueues it in funcWorkCh. While queued or running, ctx and all objects it references are retained. The channel capacity is runtime.GOMAXPROCS(-1)*2048, and GOMAXPROCS can be large. If ctx carries large attacker-controlled payloads and many calls are accepted before workers drain the queue, memory usage can grow substantially. There is no per-ctx size limit, no queue occupancy accounting beyond the channel buffer, and no way to cancel queued work."
}
]
```