report_jctest_asan.md
test_testmain Binary Log Output IssueRepository:
/Users/mathiaswesterdahl/work/defold
Target module:
/Users/mathiaswesterdahl/work/defold/engine/testmain
Test binary:
/Users/mathiaswesterdahl/work/defold/engine/testmain/build/src/test/test_testmain
The user is investigating a CI issue involving test_testmain on macOS with ASAN enabled. The important distinction is that the test process itself does not currently fail locally. The reproduced local symptom is that the test writes embedded NUL bytes (0x00) to stdout, which can make CI/log tooling treat the output as binary or hide useful log text.
The test_testmain executable exits successfully with status 0.
Running it with ASAN options does not produce an AddressSanitizer report:
ASAN_OPTIONS=halt_on_error=1:abort_on_error=1:symbolize=1 ./build/src/test/test_testmain
This is expected because the issue is not memory corruption. The issue is that jc_test explicitly writes C string terminators into its output buffer and then flushes those bytes to stdout.
Build or reuse the x86_64 macOS ASAN test binary, then capture stdout and inspect the bytes:
cd /Users/mathiaswesterdahl/work/defold/engine/testmain
./build/src/test/test_testmain > /tmp/test_testmain.out
xxd -g 1 -c 32 /tmp/test_testmain.out
Observed output still contains embedded 00 bytes. Example:
00000000: 64 6d 54 65 73 74 4d 61 69 6e 0a 50 6c 61 74 66 6f 72 6d 49 6e 69 74 0a 00 44 65 62 75 67 67 65 dmTestMain.PlatformInit..Debugge
00000020: 72 41 74 74 61 63 68 65 64 3a 20 30 50 6c 61 74 66 6f 72 6d 49 6e 69 74 20 50 41 53 53 20 28 38 rAttached: 0PlatformInit PASS (8
00000040: 20 c2 b5 73 29 0a 00 64 6d 54 65 73 74 4d 61 69 6e 20 74 6f 6f 6b 20 31 33 30 20 c2 b5 73 0a 52 ..s)..dmTestMain took 130 ..s.R
The key bytes are:
... 0a 00 44 ...
... 0a 00 64 ...
Those 00 bytes are the reproduced issue.
The nightly macOS ASAN workflow uses x86_64-macos, not the default host target. On an Apple Silicon machine, a command without --platform is likely to build arm64-macos.
Closest local build command:
cd /Users/mathiaswesterdahl/work/defold
./scripts/build.py shell
cd engine/testmain
waf --prefix=$DYNAMO_HOME distclean configure build --platform=x86_64-macos --opt-level=0 --with-asan
Confirmed binary properties from the previous investigation:
Mach-O 64-bit executable x86_64
The binary links ASAN:
@rpath/libclang_rt.asan_osx_dynamic.dylib
The root cause is in jc_test.h, specifically jc_buffered_string::Append(const char*).
Problematic code:
void Append(const char* str)
{
Append(str, strlen(str)+1);
}
This copies the terminating NUL byte into the buffered output. Later, jc_test_print_logger::FlushBuffer() writes str->size bytes to stdout, so the embedded terminator is emitted as an actual 0x00 byte.
Installed header path currently checked:
/Users/mathiaswesterdahl/work/defold/tmp/dynamo_home/ext/include/jc_test/jc_test.h
The line still exists there:
Append(str, strlen(str)+1);
Git status showed that the package version has been bumped:
D packages/jctest-0.13-common.tar.gz
A packages/jctest-0.14-common.tar.gz
M scripts/build.py
M share/ext/jctest/build_jctest.sh
Observed version updates:
- "jctest-0.13",
+ "jctest-0.14",
-readonly VERSION=0.13
+readonly VERSION=0.14
However, extracting the header from the new package still shows the same problematic line:
tar -xOzf packages/jctest-0.14-common.tar.gz include/jc_test/jc_test.h | rg -n "Append\\(str, strlen\\(str\\)"
Observed result:
973: Append(str, strlen(str)+1);
Therefore the current jctest-0.14-common.tar.gz package does not yet fix the issue.
Change:
void Append(const char* str)
{
Append(str, strlen(str)+1);
}
to:
void Append(const char* str)
{
Append(str, strlen(str));
}
The package must then be rebuilt or repacked so that this fixed header is inside:
/Users/mathiaswesterdahl/work/defold/packages/jctest-0.14-common.tar.gz
After that, refresh the installed externals so this installed copy is updated too:
/Users/mathiaswesterdahl/work/defold/tmp/dynamo_home/ext/include/jc_test/jc_test.h
After fixing and repacking jctest-0.14-common.tar.gz, refresh externals:
cd /Users/mathiaswesterdahl/work/defold
./scripts/build.py install_ext
Rebuild the test:
cd /Users/mathiaswesterdahl/work/defold/engine/testmain
waf --prefix=$DYNAMO_HOME distclean configure build --platform=x86_64-macos --opt-level=0 --with-asan
Capture and inspect output:
./build/src/test/test_testmain > /tmp/test_testmain.out
xxd -g 1 -c 32 /tmp/test_testmain.out
Expected result:
No 00 bytes appear in the test output stream.
Automated pass/fail check:
./build/src/test/test_testmain > /tmp/test_testmain.out
LC_ALL=C tr -d '\000' < /tmp/test_testmain.out | cmp -s /tmp/test_testmain.out - \
&& echo "OK: no NUL bytes" \
|| echo "FAIL: NUL bytes reproduced"
Expected fixed result:
OK: no NUL bytes
ASAN is not expected to fail for this bug. It is an output formatting/logging bug, not a memory safety bug.
The test named test_testmain is relevant because it is one of the first tests to run in CI. Its output can introduce NUL bytes early in the CI log, making subsequent log handling confusing.
The stale CMake build tree under:
/Users/mathiaswesterdahl/work/defold/engine/build/arm64-macos
references engine/testmain/CMakeLists.txt, but that file is not tracked on the current dev checkout. Do not use that stale CMake build tree as the primary signal for this investigation. The current testmain module is built via Waf.