Skip to content

Use O_CLOEXEC to avoid race conditions#10162

Open
embhorn wants to merge 6 commits intowolfSSL:masterfrom
embhorn:gh9753
Open

Use O_CLOEXEC to avoid race conditions#10162
embhorn wants to merge 6 commits intowolfSSL:masterfrom
embhorn:gh9753

Conversation

@embhorn
Copy link
Copy Markdown
Member

@embhorn embhorn commented Apr 8, 2026

Description

Use the "close-on-exec" flag at file creation time to harden multithread use cases against race conditions.

Fixes #9753

Testing

build testing

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

@embhorn embhorn self-assigned this Apr 8, 2026
@embhorn embhorn added the Not For This Release Not for release 5.9.1 label Apr 8, 2026
Copilot AI review requested due to automatic review settings April 8, 2026 12:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Hardens file-descriptor creation against FD leaks across fork()+exec() in multithreaded programs by using close-on-exec flags (e.g., O_CLOEXEC, SOCK_CLOEXEC, EFD_CLOEXEC) at creation time where possible.

Changes:

  • Introduces portable WC_*_CLOEXEC macros that fall back to no-ops on unsupported platforms.
  • Applies WC_CLOEXEC / WC_SOCK_CLOEXEC / WC_EFD_CLOEXEC to various open(), socket(), and eventfd() call sites.
  • Uses accept4(..., SOCK_CLOEXEC) for AF_ALG where available and passes close-on-exec flag to perf_event_open.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
wolfssl/wolfcrypt/wc_port.h Adds portable close-on-exec flag macros for FD-creating syscalls.
wolfcrypt/src/random.c Opens RNG-related device FDs with O_CLOEXEC where available.
wolfcrypt/src/port/intel/quickassist_mem.c Opens QAT memory device with O_CLOEXEC.
wolfcrypt/src/port/devcrypto/wc_devcrypto.c Opens /dev/crypto with O_CLOEXEC.
wolfcrypt/src/port/caam/wolfcaam_qnx.c Opens CAAM device with O_CLOEXEC.
wolfcrypt/src/port/af_alg/wc_afalg.c Uses accept4(..., SOCK_CLOEXEC) when available; creates AF_ALG sockets with SOCK_CLOEXEC.
wolfcrypt/src/port/af_alg/afalg_hash.c Uses accept4(..., SOCK_CLOEXEC) when available for duplicated AF_ALG FDs.
wolfcrypt/benchmark/benchmark.c Adds PERF_FLAG_FD_CLOEXEC to perf_event_open flags.
src/wolfio.c Creates TCP sockets with SOCK_CLOEXEC; sets FD_CLOEXEC after accept().
src/ssl.c Creates EGD AF_UNIX socket with SOCK_CLOEXEC.
src/crl.c Uses CLOEXEC flags for eventfd/inotify/open() in CRL monitor paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings April 8, 2026 13:31
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@embhorn embhorn assigned wolfSSL-Bot and unassigned embhorn Apr 9, 2026
Copy link
Copy Markdown
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐺 Skoll Code Review

Overall recommendation: REQUEST_CHANGES
Findings: 5 total — 4 posted, 1 skipped

Posted findings

  • [Low] crl.c kqueue path: F_SETFD without read-modify-writesrc/crl.c:1719
  • [Low] wolfio.c: defined(SOCK_CLOEXEC) guard in accept4 path is tautologically truesrc/wolfio.c:1642-1643
  • [Medium] afalg_hash.c: accept4 used without _GNU_SOURCE but guarded by system SOCK_CLOEXECwolfcrypt/src/port/af_alg/afalg_hash.c:226-232
  • [High] wolfio.c: _GNU_SOURCE placement may conflict if libwolfssl_sources.h pulls in system headerssrc/wolfio.c:31-36
Skipped findings
  • [Medium] wc_afalg.c: SOCK_CLOEXEC fallback defeats accept4 guard — missing _GNU_SOURCE

Review generated by Skoll via openclaw

src/crl.c Outdated
return NULL;
}
#ifdef FD_CLOEXEC
(void)fcntl(crl->mfd, F_SETFD, FD_CLOEXEC);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] crl.c kqueue path: F_SETFD without read-modify-write
🔧 NIT convention

The kqueue code path sets FD_CLOEXEC directly:

(void)fcntl(crl->mfd, F_SETFD, FD_CLOEXEC);

This overwrites any existing fd flags rather than ORing FD_CLOEXEC into the current flags. Elsewhere in this same PR (inotify path at line 1866-1868, benchmark.c line 1599-1601, wolfio.c line 1649-1651), the read-modify-write pattern is correctly used:

int fdFlags = fcntl(fd, F_GETFD);
if (fdFlags >= 0)
    (void)fcntl(fd, F_SETFD, fdFlags | FD_CLOEXEC);

In practice, kqueue fds have no other fd flags, so this is harmless. But the inconsistency could mislead future developers who copy the pattern.

Suggestion:

Suggested change
(void)fcntl(crl->mfd, F_SETFD, FD_CLOEXEC);
```c
#ifdef FD_CLOEXEC
{
int fdFlags = fcntl(crl->mfd, F_GETFD);
if (fdFlags >= 0)
(void)fcntl(crl->mfd, F_SETFD, fdFlags | FD_CLOEXEC);
}
#endif

{
return (int)accept(sockfd, peer_addr, peer_len);
int fd;
#if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_ZEPHYR) && \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] wolfio.c: defined(SOCK_CLOEXEC) guard in accept4 path is tautologically true
🔧 NIT style

The fallback #define SOCK_CLOEXEC 0 at line 56-57 makes defined(SOCK_CLOEXEC) in the accept4 guard at line 1643 always true. The code works correctly because _GNU_SOURCE is defined at the top of the file ensuring accept4 is declared, and SOCK_CLOEXEC=0 is a no-op flag. However, the defined(SOCK_CLOEXEC) check is misleading — it appears to test for platform support but can never be false. A reader might incorrectly think removing the fallback define would enable the else branch.

Suggestion:

Suggested change
#if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_ZEPHYR) && \
Consider removing `defined(SOCK_CLOEXEC)` from the guard since it's always true, or add a comment noting the fallback makes it tautological:
```c
#if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_ZEPHYR) && \
(defined(__linux__) || defined(__ANDROID__))
fd = (int)accept4(sockfd, peer_addr, peer_len, SOCK_CLOEXEC);


dst->rdFd = accept(src->rdFd, NULL, 0);
dst->alFd = accept(src->alFd, NULL, 0);
#if defined(__linux__) && defined(SOCK_CLOEXEC)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] afalg_hash.c: accept4 used without _GNU_SOURCE but guarded by system SOCK_CLOEXEC
💡 SUGGEST bug

afalg_hash.c uses accept4 guarded by #if defined(__linux__) && defined(SOCK_CLOEXEC). Unlike wc_afalg.c, it does NOT define a SOCK_CLOEXEC fallback, so the guard correctly tests system support (if the system defines SOCK_CLOEXEC, accept4 should also be declared). This is the correct pattern.

However, afalg_hash.c doesn't define _GNU_SOURCE and doesn't include <sys/socket.h> directly — it gets it through wc_afalg.h. On glibc 2.14–2.18 without _GNU_SOURCE, the system SOCK_CLOEXEC is not defined in the headers, so the code correctly falls through to accept(). This works, but adding _GNU_SOURCE (like wolfio.c does) would allow accept4 to work on those older systems too.

Recommendation: Consider adding _GNU_SOURCE before system headers in afalg_hash.c (and transitively wc_afalg.h), matching the wolfio.c pattern, so accept4 is available on all glibc-based Linux systems.

* before any system headers are included. Excluded for Zephyr and other
* embedded RTOSes whose libc layers conflict with glibc-style definitions
* (e.g., Zephyr's socket_select.h vs. glibc's fd_set). */
#if (defined(__linux__) || defined(__ANDROID__)) && \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [High] wolfio.c: _GNU_SOURCE placement may conflict if libwolfssl_sources.h pulls in system headers
🚫 BLOCK bug

_GNU_SOURCE must be defined before any system headers are included. In wolfio.c, it's defined at line 33, then libwolfssl_sources.h is included at line 36. libwolfssl_sources.h includes config.h, then types.h, error-crypt.h, and logging.h. The types.h header includes <stdint.h> and potentially other system headers. On glibc, once any system header is included, defining _GNU_SOURCE after the fact has no effect — feature test macros are evaluated on the first inclusion of <features.h>, which is triggered by the first system header.

If types.h (via <stdint.h><features.h>) is processed before _GNU_SOURCE is checked, the accept4 declaration won't be exposed when <sys/socket.h> is later included, even though _GNU_SOURCE is now defined.

Note: in practice, config.h from autotools may define _GNU_SOURCE itself (when --enable-asyncthreads is used), and modern glibc >= 2.19 exposes accept4 by default. But for correctness on all supported configurations, _GNU_SOURCE should be defined before libwolfssl_sources.h.

Suggestion:

Suggested change
#if (defined(__linux__) || defined(__ANDROID__)) && \
The `_GNU_SOURCE` define is already before `libwolfssl_sources.h`. The concern is whether `_GNU_SOURCE` itself can be tested before `config.h` is included. It should work because the `#if !defined(_GNU_SOURCE)` check only tests whether `_GNU_SOURCE` was defined by the build system (e.g., `-D_GNU_SOURCE` on the command line). If autotools already set it, we skip the define; if not, we set it before any headers. **However**, verify that no header included by `libwolfssl_sources.h` pulls in `<features.h>` before our `_GNU_SOURCE` takes effect. On glibc, `<stdint.h>` does include `<features.h>`, but since `_GNU_SOURCE` is defined before that include, it should be visible. This is correct order.

Copilot AI review requested due to automatic review settings April 10, 2026 18:33
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 12 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@embhorn embhorn removed the Not For This Release Not for release 5.9.1 label Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: many open() without O_CLOEXEC

4 participants