Setsockopt errors can be silently discarded
low swesweep-fasthttp · reuseport/reuseport_aix.go · listenConfig.Control · run #4
Description
The Control closure uses a named return variable err and returns the result of c.Control(...). If unix.SetsockoptInt fails inside the callback but c.Control returns nil, the return statement assigns c.Control's nil result to the named err variable, clearing the setsockopt error. As a result, Listen may return a listener without SO_REUSEADDR or SO_REUSEPORT actually enabled, violating the documented behavior and hiding platform or permission failures. The issue is not directly memory-safe or remotely exploitable, but an attacker who can influence the network/addr values or the host environment could cause incorrect listener configuration or availability issues.
Triage
Not triaged.
Raw scan report for this file
The file has a very small attack surface. The only public entry point is `Listen(network, addr string)`. The `network` and `addr` values can be attacker-influenced if the host application passes untrusted configuration, CLI arguments, environment variables, or protocol data to this library function. They flow into `net.ListenConfig.Listen`, and the `Control` callback receives a kernel-created socket file descriptor, not attacker-controlled bytes.
There are no direct memory-safety issues in this file:
- No fixed-size buffers are used.
- No `memcpy`, `sprintf`, or manual string manipulation is performed.
- No attacker-controlled integer is used as a length, offset, size, or index.
- No tagged union / variant type is dereferenced without a discriminator.
- No goroutines are started, so there is no goroutine leak or deadlock introduced here.
- The global `listenConfig` value is stateless except for the `Control` closure, and that closure does not share mutable state across calls.
The main issue is error handling in the `Control` closure:
```go
Control: func(network, address string, c syscall.RawConn) (err error) {
return c.Control(func(fd uintptr) {
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
if err == nil {
err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
}
})
},
```
The named return variable `err` is assigned inside the nested `Control` callback. However, the outer function then returns the result of `c.Control(...)`. In Go, when a return statement contains an expression, that expression value is assigned to the named result variable before returning. Therefore, if `unix.SetsockoptInt` fails but `c.Control` itself returns `nil`, the failure set in `err` can be overwritten with `nil`.
This means `Listen` may return a listener even though `SO_REUSEADDR` and/or `SO_REUSEPORT` were not successfully set. This is primarily a correctness and availability issue rather than a classic remotely exploitable zero-day, but it can violate the documented behavior of the package and hide platform, permission, or unsupported-option failures.
Security impact is limited because:
- The bug does not cause out-of-bounds access, use-after-free, type confusion, or memory corruption.
- The attacker does not directly control the socket file descriptor.
- The failure paths are unlikely to be triggered by remote network packets.
- The impact is that the caller may believe reuseport/reuseaddr semantics are active when they are not.
A more robust implementation would capture the `SetsockoptInt` error in a local variable and return it explicitly if `c.Control` succeeds.
```json
[
{
"severity": "low",
"title": "Setsockopt errors can be silently discarded",
"function": "listenConfig.Control",
"description": "The Control closure uses a named return variable err and returns the result of c.Control(...). If unix.SetsockoptInt fails inside the callback but c.Control returns nil, the return statement assigns c.Control's nil result to the named err variable, clearing the setsockopt error. As a result, Listen may return a listener without SO_REUSEADDR or SO_REUSEPORT actually enabled, violating the documented behavior and hiding platform or permission failures. The issue is not directly memory-safe or remotely exploitable, but an attacker who can influence the network/addr values or the host environment could cause incorrect listener configuration or availability issues."
}
]
```