Unchecked CloseOnExec error can leave listener fd inheritable across exec
low swesweep-fasthttp · tcplisten/socket.go · newSocketCloexecOld · run #4
Description
unix.CloseOnExec(fd) is called without checking the returned error. If it fails, the function still returns the fd successfully, so the socket may not be marked close-on-exec. A later fork/exec by the host process could inherit the listening socket fd, potentially exposing it to child processes. The issue is low severity because CloseOnExec failure is unlikely and the code does not expose memory corruption, but the error should be checked and the fd closed if the flag cannot be set.
Triage
Not triaged.
Raw scan report for this file
This file contains a Unix-only socket-creation helper in the `tcplisten` package. It is not a parser or protocol handler, so the main input surface is the `domain`, `typ`, and `proto` integers passed in by the caller. If the public API in the same package exposes those values to untrusted input, they flow directly into `unix.Socket`. If the caller instead uses fixed constants such as `AF_INET`, `SOCK_STREAM`, and `IPPROTO_TCP`, the input is not attacker-controlled.
Function: `newSocketCloexecOld`
- It takes `domain`, `typ`, and `proto` as ordinary integers.
- It calls `syscall.ForkLock.RLock()` to reduce the chance that a `fork`/`exec` occurs between socket creation and setting the close-on-exec flag.
- It calls `unix.Socket(domain, typ, proto)`.
- If socket creation succeeds, it calls `unix.CloseOnExec(fd)` and ignores the returned error.
- It releases the lock.
- If `unix.Socket` failed, it returns an error.
- It then calls `unix.SetNonblock(fd, true)`.
- If that fails, it closes the fd and returns an error.
- Otherwise it returns the fd.
Security observations:
1. No memory corruption is possible in this file.
Go is memory-safe, and this code does not use `unsafe`, raw pointers, manual buffer copying, or fixed-size stack buffers. There is no attacker-controlled length copied into a fixed-size buffer.
2. No integer overflow or wrap issue affects size or index calculations.
The integer values are passed to a syscall. They are not used to compute buffer sizes, slice bounds, or array indices in this file.
3. No tagged union or variant-type confusion exists.
There are no unions, enum discriminators, or interface-like payloads in this function.
4. No goroutine leak or deadlock is introduced here.
The function does not start goroutines. `syscall.ForkLock.RLock()` is used to serialize against `exec`-style operations, which is appropriate for this purpose.
5. Invalid `domain`, `typ`, or `proto` values are mostly rejected by the kernel.
If an attacker can control these values, they can attempt to create sockets of unexpected families, types, or protocols. However, the `socket(2)` syscall does not expose memory corruption through malformed arguments in the normal case. Invalid values return errors such as `EINVAL`, `EAFNOSUPPORT`, `EPROTONOSUPPORT`, etc. Privileged socket types may fail if the process lacks capabilities. This is therefore not a credible zero-day memory-safety bug in this file.
6. The main robustness/security issue is that `unix.CloseOnExec(fd)` is called without checking its error.
If `CloseOnExec` fails, the code still returns the fd as successful. On a platform where the close-on-exec flag could fail to be set, the listening socket fd may later be inherited by a child process after `fork`/`exec`. That could expose the listener fd to an unintended process, depending on how the host application manages child processes. The impact is limited because the failure mode is unlikely and the code is memory-safe, but the error should be checked and the fd closed if the flag cannot be set.
7. The `unix.Close(fd)` call on the `SetNonblock` failure path also ignores the close error.
This is a very low-probability issue. If `SetNonblock` fails and `Close` also fails, the fd could leak. This is not realistically attacker-triggerable and is primarily a resource-hygiene issue.
Overall, there is no high-severity or critical zero-day candidate in this file. The only meaningful finding is an unchecked error from `unix.CloseOnExec`, which can weaken the close-on-exec invariant for the created listening socket.
```json
[
{
"severity": "low",
"title": "Unchecked CloseOnExec error can leave listener fd inheritable across exec",
"function": "newSocketCloexecOld",
"description": "unix.CloseOnExec(fd) is called without checking the returned error. If it fails, the function still returns the fd successfully, so the socket may not be marked close-on-exec. A later fork/exec by the host process could inherit the listening socket fd, potentially exposing it to child processes. The issue is low severity because CloseOnExec failure is unlikely and the code does not expose memory corruption, but the error should be checked and the fd closed if the flag cannot be set."
}
]
```