Skip to content

ls-config: bound the sinp scratch buffer in sscanf() calls - #299

Merged
hyperrealm merged 1 commit into
hyperrealm:masterfrom
afonsojanu:fix/ls-config-sinp-overflow
Sep 28, 2026
Merged

hyperrealm merged 1 commit into
hyperrealm:masterfrom
afonsojanu:fix/ls-config-sinp-overflow

Conversation

@afonsojanu

Copy link
Copy Markdown
Contributor

Fixes #289.

contrib/ls-config copies each command line argument value (-s/-g/-d/-p/-f, and the matching long options) into a 256-byte heap buffer called sinp before duplicating it into its own allocation. All ten of those copies go through sscanf(optarg, "%s", sinp) or sscanf(optarg, "%[^\n]s", sinp), and neither conversion carries a field width, so any argument longer than the buffer writes straight past the end of it.

Built with -fsanitize=address and run as:

ls-config -s "$(python3 -c 'print("A"*500)')" -f /dev/null

this aborts immediately with a heap-buffer-overflow write of 501 bytes into the 256-byte sinp allocation, right at the sscanf() call on the -s path (ls-config.c:1249 in the version I started from). Every other option that reads into sinp has the identical problem, since they all share the same buffer and the same unbounded format strings.

The fix adds a field width of 255 (leaving room for the NUL) to all ten sscanf() calls, driven from one SINP_MAXLEN definition placed next to where the buffer is sized, so the two can't silently drift apart again in a future edit.

I added contrib/ls-config/src/test_sinp_overflow.c, which pulls in the actual ls-config.c (with main renamed out of the way via a macro) and drives it with an oversized --set argument, the same way the shell repro above does. Under ASan it aborts against the unpatched file and completes cleanly against the fix; I verified both directions by building it against a stashed copy of the original code before restoring the patch. contrib/ls-config doesn't currently have any test wiring of its own (it's a standalone tool built via its own makefile, separate from the library's CMake/tinytest setup), so this is a standalone regression test rather than something hooked into make check — happy to adjust if there's a preferred place for it.

Tested on macOS with clang, linking against a locally-built libconfig.a and Homebrew's gettext for libintl.h (not available on macOS by default).

contrib/ls-config copies each command line argument value (-s/-g/-d/-p/-f
and their long-option equivalents) into a 256-byte heap buffer called
sinp before duplicating it into its own allocation. Every one of those
copies went through sscanf(optarg, "%s", sinp) or
sscanf(optarg, "%[^\n]s", sinp), neither of which carries a field width,
so an argument of a few hundred bytes writes straight past the end of
the buffer.

Built with -fsanitize=address and run as:

  ls-config -s "$(python3 -c 'print("A"*500)')" -f /dev/null

this aborts with a heap-buffer-overflow write of 501 bytes into the
256-byte sinp allocation, right at the sscanf() call on the -s path.

The fix gives every one of the ten call sites a field width of 255
(leaving room for the terminating NUL), pulled from one SINP_MAXLEN
definition next to the buffer's size so they can't drift apart again.

Added contrib/ls-config/src/test_sinp_overflow.c, which pulls in the
real ls-config.c (with main renamed out of the way) and drives it with
an oversized --set argument. Checked that it aborts under ASan against
the unpatched file and completes cleanly against the fix.
@hyperrealm
hyperrealm merged commit 19de5b2 into hyperrealm:master Sep 28, 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.

Heap-based Buffer Overflow Vulnerability Caused by sscanf function in the ls-config.c

2 participants