Skip to content

stack-buffer-overflow in UtAssert_StringBufCompare() scrub buffers #1550

Description

@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/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

  1. 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.
  2. Compile with AddressSanitizer (-fsanitize=address).
  3. Run the test binary.
  4. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions