Describe the bug
UtAssert_StringBufCompare() copies caller-controlled bytes into fixed 256-byte stack buffers (ScrubbedString1[256], ScrubbedString2[256]) without clamping the length first. When a fixed-length buffer longer than 255 bytes contains no NUL within the requested bound, FormatLen1/FormatLen2 are set directly from the caller-supplied String1Max/String2Max, and memcpy writes past the end of the stack buffers, triggering a stack-buffer-overflow.
To Reproduce
- Build a test binary that calls
UtAssert_StringBufCompare() (or UtAssert_STRINGBUF_EQ()) with a fixed-length buffer of more than 255 bytes that contains no NUL terminator within the supplied bound.
- Compile with AddressSanitizer (
-fsanitize=address).
- Run the test binary.
- Observe
AddressSanitizer: stack-buffer-overflow on the memcpy at utassert.c:765.
Expected behavior
The scrub copy should be clamped to sizeof(ScrubbedString1) - 1 bytes. Buffers longer than 255 bytes should be safely truncated for diagnostic formatting purposes without overflowing the stack.
Code snips
ut_assert/src/utassert.c (commit c7f57b75b8bc40e06793d39d7fb537ff59e5bd2a):
// line 642-643
char ScrubbedString1[256];
char ScrubbedString2[256];
// line 677 — no NUL found, length is unclamped
FormatLen1 = String1Max;
// line 765-767 — unbounded copy + OOB terminator write
memcpy(ScrubbedString1, String1, FormatLen1);
ScrubbedString1[FormatLen1] = 0;
Suggested fix:
size_t ScrubLen1 = FormatLen1 < sizeof(ScrubbedString1) - 1
? FormatLen1 : sizeof(ScrubbedString1) - 1;
memcpy(ScrubbedString1, String1, ScrubLen1);
ScrubbedString1[ScrubLen1] = 0;
System observed on:
- Hardware: x86-64 (Linux)
- OS: Linux (WSL2, Ubuntu)
- Versions: OSAL dev/main, cFS current submodule, commit
c7f57b75b8bc40e06793d39d7fb537ff59e5bd2a
Additional context
This affects the unit-test assertion framework only (ut_assert). It is not a flight-runtime issue. Impact is limited to sanitizer-enabled or unprotected test binaries that compare attacker-influenced or fuzz-generated fixed-length buffers via UtAssert_STRINGBUF_EQ(). A 384-byte non-NUL fixed buffer passed with String1Max=384 is sufficient to reproduce the overflow.
poc
candidate=CAND-OSAL-UTASSERT-STRINGBUF-SCRUB-STACK-BOF
source_version=NASA OSAL dev/main as bundled by cFS current submodule
source_commit=c7f57b75b8bc40e06793d39d7fb537ff59e5bd2a
source_path=/mnt/c/Users/JUN/Desktop/target/nasa/src/extra/osal/ut_assert/src/utassert.c
source_sha256=342ec3c28a7404b6cc48187ad6ae8f3588420f507a93e90590fb405a4e0f48dc
header_path=/mnt/c/Users/JUN/Desktop/target/nasa/src/extra/osal/ut_assert/inc/utassert.h
header_sha256=420a0dab6b19d214a1c90c30f93d604b04b870e329246abc95660b9634972f38
source_chain_confirmed=true
header_contract_confirmed=true
compile_returncode=0
fixture_executed=true
fixture_returncode=1
AddressSanitizer=true
stack-buffer-overflow=true
asan_stack_buffer_overflow=true
platform=Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.39
non_weaponized=true
safety=local ASAN model only; no shell payload; no network; no privileged paths
compile_cmd=cc -O0 -g -fsanitize=address -fno-omit-frame-pointer -Wall -Wextra /tmp/cand-osal-utassert-4c314b0k/utassert_stringbuf_scrub_fixture.c -o /tmp/cand-osal-utassert-4c314b0k/utassert_stringbuf_scrub_fixture
fixture_stderr_begin
=================================================================
==32393==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7d8acca00120 at pc 0x7d8aceafb303 bp 0x7fff9f1e2c90 sp 0x7fff9f1e2438
WRITE of size 384 at 0x7d8acca00120 thread T0
#0 0x7d8aceafb302 in memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
#1 0x583f955cf49f in modeled_utassert_stringbuf_compare /tmp/cand-osal-utassert-4c314b0k/utassert_stringbuf_scrub_fixture.c:47
#2 0x583f955cf805 in main /tmp/cand-osal-utassert-4c314b0k/utassert_stringbuf_scrub_fixture.c:82
#3 0x7d8ace62a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#4 0x7d8ace62a28a in __libc_start_main_impl ../csu/libc-start.c:360
#5 0x583f955cf204 in _start (/tmp/cand-osal-utassert-4c314b0k/utassert_stringbuf_scrub_fixture+0x1204) (BuildId: 01fc1be84cfa4d76ed4e51cab4ee5611c6875274)
Address 0x7d8acca00120 is located in stack of thread T0 at offset 288 in frame
#0 0x583f955cf2d8 in modeled_utassert_stringbuf_compare /tmp/cand-osal-utassert-4c314b0k/utassert_stringbuf_scrub_fixture.c:12
This frame has 2 object(s):
[32, 288) 'ScrubbedString1' (line 13)
[352, 608) 'ScrubbedString2' (line 14) <== Memory access at offset 288 partially underflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115 in memcpy
Shadow bytes around the buggy address:
0x7d8acc9ffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acc9fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acc9fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acca00000: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acca00080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7d8acca00100: 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 00 00 00 00
0x7d8acca00180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acca00200: 00 00 00 00 00 00 00 00 00 00 00 00 f3 f3 f3 f3
0x7d8acca00280: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acca00300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7d8acca00380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==32393==ABORTING
fixture_stderr_end
Reporter Info
junfuture1103
Describe the bug
UtAssert_StringBufCompare()copies caller-controlled bytes into fixed 256-byte stack buffers (ScrubbedString1[256],ScrubbedString2[256]) without clamping the length first. When a fixed-length buffer longer than 255 bytes contains no NUL within the requested bound,FormatLen1/FormatLen2are set directly from the caller-suppliedString1Max/String2Max, andmemcpywrites past the end of the stack buffers, triggering a stack-buffer-overflow.To Reproduce
UtAssert_StringBufCompare()(orUtAssert_STRINGBUF_EQ()) with a fixed-length buffer of more than 255 bytes that contains no NUL terminator within the supplied bound.-fsanitize=address).AddressSanitizer: stack-buffer-overflowon thememcpyatutassert.c:765.Expected behavior
The scrub copy should be clamped to
sizeof(ScrubbedString1) - 1bytes. Buffers longer than 255 bytes should be safely truncated for diagnostic formatting purposes without overflowing the stack.Code snips
ut_assert/src/utassert.c(commitc7f57b75b8bc40e06793d39d7fb537ff59e5bd2a):Suggested fix:
System observed on:
c7f57b75b8bc40e06793d39d7fb537ff59e5bd2aAdditional context
This affects the unit-test assertion framework only (
ut_assert). It is not a flight-runtime issue. Impact is limited to sanitizer-enabled or unprotected test binaries that compare attacker-influenced or fuzz-generated fixed-length buffers viaUtAssert_STRINGBUF_EQ(). A 384-byte non-NUL fixed buffer passed withString1Max=384is sufficient to reproduce the overflow.poc
Reporter Info
junfuture1103