HexBrain

curl · Report #3400831

CURLX_SET_BINMODE(NULL) can call fileno(NULL) and cause undefined behavior / crash

N/Astatus
highseverity
13upvotes
27 Oct 2025disclosed

How it was found

Summary

Calling the CURLX_SET_BINMODE(stream) macro with stream == NULL leads to an unguarded call to fileno(NULL) in tool_binmode.h, which is undefined behavior and may crash the process. This is a robustness/UB issue and should be corrected by guarding against NULL streams before calling fileno().

Reproducer

A minimal test program is included below to demonstrate the issue. When built against the current header, running the program results in a crash (SIGSEGV) or other undefined behavior on many platforms.

Impact

Non-security robustness defect: an accidental NULL stream passed to the macro can crash the process. This can surface during CLI tool execution or in tests and is easy to trigger accidentally.

Fix

Add a simple NULL-check wrapper (inline function) that returns early when stream == NULL, then call _setmode/setmode inside the wrapper. I included a suggested patch below and a unit test to prevent regressions.

Notes

This is non-security; public PR is appropriate. I recommend adding the unit test to CI (and an ASAN job) so regressions are caught.

(safe, reproducible)

Save as repro_binmode_null.c at the repository root (adjust include path if needed):

/* repro_binmode_null.c *

  • Minimal PoC demonstrating that calling CURLX_SET_BINMODE(NULL)
  • results in an unguarded fileno(NULL) invocation (undefined behavior).
  • Build:
  • gcc -Wall -Wextra -g -I./include repro_binmode_null.c -o repro_binmode_null
  • If you want a clearer diagnostic, build+run under ASAN:
  • gcc -fsanitize=address,undefined -g -I./include repro_binmode_null.c -o repro_binmode_null_asan
  • Run:
  • ./repro_binmode_null
  • ./repro_binmode_null_asan
  • Note: This program intentionally triggers the problematic call for repro purposes. */

#include <stdio.h> /* Adjust the path below if your include tree is located elsewhere */ #include "tool_binmode.h"

int main(void) { /* Intentionally pass NULL to reproduce the issue. The current macro expands to fileno(NULL), which is UB. */ CURLX_SET_BINMODE(NULL);

/* If the program continues without crashing, print a confirmation */
printf("CURLX_SET_BINMODE(NULL) returned — either implementation avoided fileno(NULL) or UB did not manifest.\n");
return 0;

}

Build & run instructions :-

From the repo root (assuming include/tool_binmode.h exists):

Compile (regular):

gcc -Wall -Wextra -g -I./include repro_binmode_null.c -o repro_binmode_null ./repro_binmode_null

Compile & run with sanitizers (recommended):

gcc -fsanitize=address,undefined -g -I./include repro_binmode_null.c -o repro_binmode_null_asan ./repro_binmode_null_asan

Expected ASAN/UBSAN output (example)

You will likely see an error such as:

runtime error: null pointer passed to function 'fileno' SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ...

or a segmentation fault (SIGSEGV) depending on platform and libc.

Impact

The PoC intentionally triggers UB so it may crash; that is the point — to show behavior and allow maintainers to fix it.

For Windows cross-build or non-POSIX setups, behavior varies; running under ASAN/UBSAN is recommended to obtain a reliable diagnostic.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original