BudgetScan

Nil Config receiver can panic in NewListener

medium swesweep-fasthttp · tcplisten/tcplisten.go · (*Config).NewListener · run #4

Description

NewListener can be invoked with a nil *Config receiver. It calls cfg.fdSetup without checking cfg, and fdSetup dereferences cfg.ReusePort. This causes a nil pointer dereference after the socket fd has been allocated; an unrecovered panic crashes the host process, and a recovered panic can leak the fd.

Triage

Verdict
INVALID confidence 0.0
Model
qwen/qwen3.8-27b
Rounds
5
Cost
$0.0162 · 30.9 s
The reported vulnerability is a nil pointer dereference in `(*Config).NewListener` when the receiver `cfg` is nil. While the code technically lacks a nil check for `cfg`, this constitutes a programming error (API misuse) by the host application, not a security vulnerability exploitable by an external attacker under the defined threat model. The threat model specifies that the attacker controls untrusted data inputs (such as `network` and `addr` strings, or deserialized payload contents), but not the control flow or object lifecycles of the host application. An external attacker cannot force the host code to pass a `nil` pointer as a method receiver via untrusted data streams. The `Config` struct is a control configuration object instantiated by trusted developer code. Therefore, this falls under 'misuse that violates a documented precondition' and is classified as a code quality/robustness issue, not a security vulnerability.

CRUX: Passing a nil receiver to a public library method is a host-application programming error (API misuse), not an input vector controllable by an external attacker providing untrusted data.
GREP: func (cfg *Config) NewListener
All rounds
[
  {
    "finding_title": "Nil Config receiver can panic in NewListener",
    "verdict": "INVALID",
    "reasoning": "The reported vulnerability is a nil pointer dereference panic in the public API `NewListener` when called with a nil `*Config` receiver. In Go, `NewListener` is a method on `*Config`. If `cfg` is nil, the call `cfg.fdSetup(fd, sa, addr)` proceeds to execute `fdSetup`. Inside `fdSetup`, the code accesses `cfg.ReusePort`, `cfg.DeferAccept`, etc. Since `cfg` is nil, these field accesses cause a nil pointer dereference panic. \n\nHowever, we must evaluate this against the Threat Model. \n1. **Attacker Control**: The threat model specifies that the attacker is \"Code that calls the public API, and above all the data it passes through\". In a library context, the \"attacker\" is typically a malicious host application or a malicious input that triggers a vulnerability in the library's parsing/processing logic. Passing `nil` as a receiver to a method is a programming error by the *caller* (the host application), not a result of processing untrusted *data* (like a malformed network packet or file). \n2. **Input vs. API Misuse**: The vulnerability arises from passing `nil` to the method. The method signature is `func (cfg *Config) NewListener(...)`. The `Config` object is created by the developer. A malicious attacker cannot inject `nil` as a method receiver through untrusted data streams (like HTTP requests); they can only influence the *contents* of the data processed by the functions (e.g., the `network` and `addr` strings, or the fields *inside* a `Config` object if it were deserialized from untrusted input). Even if the `Config` struct were deserialized from untrusted input, a nil receiver is a distinct concept from a struct populated with malicious values. \n3. **Trust Boundary**: The public API is expected to be called correctly by trusted host code. While robustness is desirable, a panic due to `nil` receiver is generally considered a developer error (misuse of the API) rather than a security vulnerability exploitable by an external attacker sending untrusted payloads. The attacker does not control the `cfg` pointer itself; they control the data *processed* by the function. The `network` and `addr` strings are the untrusted inputs. Passing `nil` to `cfg` is not a result of untrusted input; it is a control-flow decision by the host code. \n\nTherefore, this falls under \"misuse that violates a documented precondition of the public API\" (implicitly, the precondition that the receiver is a valid `Config` instance, or at least that the caller manages the lifecycle of the receiver). Since the attacker (external untrusted data source) cannot force the host application to pass `nil` as a receiver, this is not attacker-reachable via the defined trust boundary (untrusted data inputs). It is a code quality/robustness issue (missing nil check) but not a security vulnerability under the provided threat model.\n\nCRUX: Whether passing a nil receiver to a public library method is considered an attacker-controllable input vector under the threat model of untrusted data processing.\nGREP: func (cfg *Config) NewListener",
    "elapsed": 11.3,
    "tokens": 4383,
    "tokens_in": 3682,
    "tokens_out": 701,
    "cost_usd": 0.00364944,
    "file": "tcplisten/tcplisten.go",
    "round": 1,
    "grep_used": true,
    "grep_results": "GREP `func (cfg *Config) NewListener (simplified to: NewListener)`:\n```\nreuseport/reuseport.go:32:\tln, err := cfg.NewListener(network, addr)\nreuseport/reuseport_test.go:11:\ttestNewListener(t, \"tcp4\", \"localhost:10081\")\nreuseport/reuseport_test.go:19:\t\ttestNewListener(t, \"tcp6\", \"[::1]:10082\")\nreuseport/reuseport_test.go:36:func testNewListener(t *testing.T, network, addr string) {\ntcplisten/tcplisten.go:47:// NewListener returns TCP listener with options set in the Config.\ntcplisten/tcplisten.go:53:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\ntcplisten/tcplisten_test.go:68:\t\tln, err := cfg.NewListener(network, addr)\ntcplisten/tcplisten_js_wasm.go:15:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\nfasthttputil/inmemory_listener_timing_test.go:106:\t\t\tserverLn = tls.NewListener(serverLn, serverTLSConfig)\nserver.go:1837:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\nserver.go:1861:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\nexamples/letsencrypt/letsencryptserver.go:36:\tlnTLS := tls.NewListener(ln, cfg)\n```"
  },
  {
    "finding_title": "Nil Config receiver can panic in NewListener",
    "verdict": "INVALID",
    "reasoning": "The vulnerability report identifies a potential nil pointer dereference in `(*Config).NewListener` if called with a nil `cfg`. The code path `cfg.fdSetup(...)` -> `if cfg.ReusePort` would indeed panic if `cfg` is nil. However, we must evaluate this against the specific Threat Model for a library. The threat model defines the attacker as 'Code that calls the public API, and above all the data it passes through... parsers, decoders, deserializers and protocol handlers receive adversarial payloads'. The `Config` object is a control structure created by the application developer to configure the listener. It is not a data structure that receives untrusted payloads (like network packets or files). Passing `nil` as a receiver to a method is a programmer error (undefined behavior/misuse) by the host application, not an input derived from an untrusted external source. An external attacker cannot influence the value of the `cfg` pointer itself via untrusted data streams (such as the `network` or `addr` strings). Therefore, this is a code quality/robustness issue (missing nil check) but not a security vulnerability exploitable by an external attacker under the defined threat model where the attacker controls the *data* inputs, not the *control flow* or *object lifecycles* of the host application.\n\nCRUX: Passing a nil receiver is a host-application programming error, not an input vector controllable by an external attacker providing untrusted data.\nGREP: type Config struct",
    "elapsed": 5.5,
    "tokens": 5187,
    "tokens_in": 4859,
    "tokens_out": 328,
    "cost_usd": 0.00302478,
    "file": "tcplisten/tcplisten.go",
    "round": 2,
    "grep_used": true,
    "grep_results": "GREP `type Config struct`:\n```\ntcplisten/tcplisten.go:29:type Config struct {\ntcplisten/tcplisten_js_wasm.go:8:type Config struct {\n```"
  },
  {
    "finding_title": "Nil Config receiver can panic in NewListener",
    "verdict": "INVALID",
    "reasoning": "The reported vulnerability is a nil pointer dereference (panic) in `(*Config).NewListener` when called with a nil receiver. The threat model for this library defines the attacker as code calling the public API and the *data* passed through it (parsers, decoders, protocol handlers receiving adversarial payloads). The `Config` object is a control structure created by the application developer to configure the listener; it is not a data buffer or deserialized payload that receives untrusted external input (like network packets or files). Passing `nil` as a method receiver is a programming error (API misuse) by the host application, not a vector controlled by an external attacker providing untrusted data. An external attacker cannot force the host application to pass a nil pointer as a method receiver through untrusted data streams. The rules explicitly state that 'misuse that violates a documented precondition of the public API' is not a vulnerability under this model. Therefore, this is a code quality/robustness issue, not a security vulnerability.\n\nCRUX: Passing a nil receiver to a public library method is a host-application programming error (API misuse), not an input vector controllable by an external attacker providing untrusted data.\nGREP: func (cfg *Config) NewListener",
    "elapsed": 4.6,
    "tokens": 5243,
    "tokens_in": 4971,
    "tokens_out": 272,
    "cost_usd": 0.0029038199999999997,
    "file": "tcplisten/tcplisten.go",
    "round": 3,
    "grep_used": true,
    "grep_results": "GREP `func (cfg *Config) NewListener (simplified to: NewListener)`:\n```\nreuseport/reuseport.go:32:\tln, err := cfg.NewListener(network, addr)\nreuseport/reuseport_test.go:11:\ttestNewListener(t, \"tcp4\", \"localhost:10081\")\nreuseport/reuseport_test.go:19:\t\ttestNewListener(t, \"tcp6\", \"[::1]:10082\")\nreuseport/reuseport_test.go:36:func testNewListener(t *testing.T, network, addr string) {\ntcplisten/tcplisten.go:47:// NewListener returns TCP listener with options set in the Config.\ntcplisten/tcplisten.go:53:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\ntcplisten/tcplisten_test.go:68:\t\tln, err := cfg.NewListener(network, addr)\ntcplisten/tcplisten_js_wasm.go:15:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\nfasthttputil/inmemory_listener_timing_test.go:106:\t\t\tserverLn = tls.NewListener(serverLn, serverTLSConfig)\nexamples/letsencrypt/letsencryptserver.go:36:\tlnTLS := tls.NewListener(ln, cfg)\nserver.go:1837:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\nserver.go:1861:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\n```"
  },
  {
    "finding_title": "Nil Config receiver can panic in NewListener",
    "verdict": "INVALID",
    "reasoning": "The reported vulnerability is a nil pointer dereference panic when `NewListener` is called with a nil `*Config` receiver. The threat model for this library defines the attacker as external untrusted data (e.g., network packets, files) processed by the library, or code calling the public API with adversarial payloads. The `Config` object is a control structure defined and instantiated by the host application developer to configure listener behavior (e.g., ReusePort, Backlog). It does not contain data deserialized from untrusted external sources in a way that would allow an external attacker to control the value of the `cfg` pointer itself. Passing `nil` as a method receiver is a programming error (API misuse) by the trusted host code, not a vulnerability triggered by untrusted input. The threat model explicitly excludes \"misuse that violates a documented precondition of the public API\" and \"Missing parameter validation in non-public... functions... when the public boundary already enforces the invariant\" (or rather, when the input isn't attacker-controlled). Since an external attacker cannot force the host application to pass a nil `Config` pointer through untrusted data streams, this issue is not attacker-reachable and is therefore not a security vulnerability under the provided threat model. It is a code quality/robustness issue.\n\nCRUX: Passing a nil receiver to a public library method is a host-application programming error (API misuse), not an input vector controllable by an external attacker providing untrusted data.\nGREP: func (cfg *Config) NewListener",
    "elapsed": 5.2,
    "tokens": 5921,
    "tokens_in": 5589,
    "tokens_out": 332,
    "cost_usd": 0.00334338,
    "file": "tcplisten/tcplisten.go",
    "round": 4,
    "grep_used": true,
    "grep_results": "GREP `func (cfg *Config) NewListener (simplified to: NewListener)`:\n```\nreuseport/reuseport.go:32:\tln, err := cfg.NewListener(network, addr)\nreuseport/reuseport_test.go:11:\ttestNewListener(t, \"tcp4\", \"localhost:10081\")\nreuseport/reuseport_test.go:19:\t\ttestNewListener(t, \"tcp6\", \"[::1]:10082\")\nreuseport/reuseport_test.go:36:func testNewListener(t *testing.T, network, addr string) {\ntcplisten/tcplisten.go:47:// NewListener returns TCP listener with options set in the Config.\ntcplisten/tcplisten.go:53:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\ntcplisten/tcplisten_test.go:68:\t\tln, err := cfg.NewListener(network, addr)\ntcplisten/tcplisten_js_wasm.go:15:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\nfasthttputil/inmemory_listener_timing_test.go:106:\t\t\tserverLn = tls.NewListener(serverLn, serverTLSConfig)\nexamples/letsencrypt/letsencryptserver.go:36:\tlnTLS := tls.NewListener(ln, cfg)\nserver.go:1837:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\nserver.go:1861:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\n```"
  },
  {
    "finding_title": "Nil Config receiver can panic in NewListener",
    "verdict": "INVALID",
    "reasoning": "The reported vulnerability is a nil pointer dereference in `(*Config).NewListener` when the receiver `cfg` is nil. While the code technically lacks a nil check for `cfg`, this constitutes a programming error (API misuse) by the host application, not a security vulnerability exploitable by an external attacker under the defined threat model. The threat model specifies that the attacker controls untrusted data inputs (such as `network` and `addr` strings, or deserialized payload contents), but not the control flow or object lifecycles of the host application. An external attacker cannot force the host code to pass a `nil` pointer as a method receiver via untrusted data streams. The `Config` struct is a control configuration object instantiated by trusted developer code. Therefore, this falls under 'misuse that violates a documented precondition' and is classified as a code quality/robustness issue, not a security vulnerability.\n\nCRUX: Passing a nil receiver to a public library method is a host-application programming error (API misuse), not an input vector controllable by an external attacker providing untrusted data.\nGREP: func (cfg *Config) NewListener",
    "elapsed": 4.3,
    "tokens": 6268,
    "tokens_in": 6016,
    "tokens_out": 252,
    "cost_usd": 0.00328272,
    "file": "tcplisten/tcplisten.go",
    "round": 5,
    "grep_used": true,
    "grep_results": "GREP `func (cfg *Config) NewListener (simplified to: NewListener)`:\n```\nreuseport/reuseport.go:32:\tln, err := cfg.NewListener(network, addr)\nreuseport/reuseport_test.go:11:\ttestNewListener(t, \"tcp4\", \"localhost:10081\")\nreuseport/reuseport_test.go:19:\t\ttestNewListener(t, \"tcp6\", \"[::1]:10082\")\nreuseport/reuseport_test.go:36:func testNewListener(t *testing.T, network, addr string) {\ntcplisten/tcplisten.go:47:// NewListener returns TCP listener with options set in the Config.\ntcplisten/tcplisten.go:53:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\ntcplisten/tcplisten_test.go:68:\t\tln, err := cfg.NewListener(network, addr)\ntcplisten/tcplisten_js_wasm.go:15:func (cfg *Config) NewListener(network, addr string) (net.Listener, error) {\nfasthttputil/inmemory_listener_timing_test.go:106:\t\t\tserverLn = tls.NewListener(serverLn, serverTLSConfig)\nexamples/letsencrypt/letsencryptserver.go:36:\tlnTLS := tls.NewListener(ln, cfg)\nserver.go:1837:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\nserver.go:1861:\t\ttls.NewListener(ln, s.TLSConfig.Clone()),\n```"
  }
]
Raw scan report for this file

I analyzed the file against the public API threat model: `(*Config).NewListener(network, addr)` is the trust boundary, with caller-controlled `network`, `addr`, and `Config` fields such as `ReusePort`, `DeferAccept`, `FastOpen`, and `Backlog`.

Key flows:

- `addr` and `network` go into `net.ResolveTCPAddr`. The error is checked.
- The resolved IP is copied into fixed-size `unix.SockaddrInet4.Addr` or `unix.SockaddrInet6.Addr` via `copy(dst, src)`. `copy` bounds-checks the destination, and `To4()`/`To16()` either return the correct-sized slice or nil. A nil source only leaves the address zero-filled; it does not overflow the fixed buffers.
- Zone handling calls `net.InterfaceByName`, checks the error, and converts the interface index with `safeIntToUint32`, which rejects negative values and values above `MaxUint32`.
- `fdSetup` configures socket options, binds, and listens. Negative or zero `Backlog` falls back to `soMaxConn()`. Positive `Backlog` is passed to `unix.Listen` without clamping to the platform C `int` range.
- `NewListener` creates the fd, calls `fdSetup`, safely converts fd to `uintptr`, wraps it with `os.NewFile`, converts it with `net.FileListener`, and closes the temporary file/listener on error.

Most of the address-parsing and integer-conversion logic is safe. The notable reachable issues are:

1. A nil `*Config` receiver can cause a panic in `fdSetup` after a socket fd has already been allocated.
2. A positive `Backlog` larger than the platform C `int` range can be truncated in the `listen(2)` syscall on 64-bit systems, allowing an attacker-controlled config value to effectively set an invalid or zero backlog.

Other observations, such as ignored best-effort `Close()` errors in error paths, DNS side effects from caller-supplied hostnames, and platform-dependent dual-stack behavior, are lower-signal robustness concerns rather than clear zero-day vulnerabilities.

```json
[
  {
    "severity": "medium",
    "title": "Nil Config receiver can panic in NewListener",
    "function": "(*Config).NewListener",
    "description": "NewListener can be invoked with a nil *Config receiver. It calls cfg.fdSetup without checking cfg, and fdSetup dereferences cfg.ReusePort. This causes a nil pointer dereference after the socket fd has been allocated; an unrecovered panic crashes the host process, and a recovered panic can leak the fd."
  },
  {
    "severity": "low",
    "title": "Positive Backlog can be truncated in listen(2) on 64-bit platforms",
    "function": "(*Config).fdSetup",
    "description": "A positive cfg.Backlog larger than the platform C int range is passed directly to unix.Listen. On 64-bit targets the syscall can truncate the value, allowing an attacker-controlled Backlog (for example 1<<32) to bypass the backlog <= 0 default check and effectively set a zero or invalid backlog, degrading or failing the listener."
  }
]
```