Hello,
I have identified a 5-byte stack based overflow in GnuCOBOL 3.1.2 in the literal_for_diagnostic() function of cobc/tree.c which occurs when a \x00
is exactly aligned with the length of CB_ERR_LITMAX (38).
Attached is a functional testcase "crash.cob" which triggers the stack protector and SIGABRT which can be reproduced with:
cobc crash.cob
Lines 491-501 of cobc/tree.c
if (strlen (literal_data) > CB_ERR_LITMAX) {
char *long_pos = buff + CB_ERR_LITMAX - 4;
if (!bad_pos
|| bad_pos > long_pos) {
bad_pos = long_pos;
}
}
if (bad_pos) {
strcpy (bad_pos, " ...");
}
When using the supplied testcase, line 500 of cobc/tree.c
is called with strcpy("\n", " ...")
, which is the source cause of the stack based overflow.
Given my understanding of the problem, line 491 should be updated from:
if (strlen (literal_data) > CB_ERR_LITMAX) {
to
if (strlen (literal_data) >= CB_ERR_LITMAX) {
Rebuilding GnuCOBOL 3.1.2 with this change no longer results in the testcase crashing. make check
and make test
continue to pass. Re-Running a fuzzer targeting this section of code no longer finds new crashes.
C is not my most knowledgeable language. This crash and proposed patch was part of a finding while participating in Binary Golf Grand Prix (https://tmpout.sh/bggp/3/), for which an objective is to find and submit a patch for a crash. The scope of changes required for a proposed patch inspired me to pursue this crash as it was minimal and I believe I correctly understand it enough to contribute to a version of a tool directly available through apt install gnucobol3
.
Below is an AddressSanitizer trace of the crashing testcase.
root@476d2db17e6e:/testcases# cobc crash.cob
crash.cob:1: warning: line not terminated by a newline [-Wothers]
=================================================================
==5682==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffce47 at pc 0x7ffff76092c3 bp 0x7fffffffcda0 sp 0x7fffffffc548
WRITE of size 5 at 0x7fffffffce47 thread T0
#0 0x7ffff76092c2 in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827
#1 0x5555556de1e4 in literal_for_diagnostic /gnucobol-3.1.2/cobc/tree.c:500
#2 0x5555556c6bc6 in error_literal /gnucobol-3.1.2/cobc/scanner.l:1189
#3 0x5555556c71a1 in read_literal /gnucobol-3.1.2/cobc/scanner.l:1271
#4 0x5555556b6dc3 in yylex /gnucobol-3.1.2/cobc/scanner.l:340
#5 0x555555675177 in yyparse /gnucobol-3.1.2/cobc/parser.c:12149
#6 0x55555564a09b in process_translate /gnucobol-3.1.2/cobc/cobc.c:7490
#7 0x55555564d663 in process_file /gnucobol-3.1.2/cobc/cobc.c:8619
#8 0x55555564e107 in main /gnucobol-3.1.2/cobc/cobc.c:8813
#9 0x7ffff6f85d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)
#10 0x7ffff6f85e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f)
#11 0x55555562cec4 in _start (/usr/local/bin/cobc+0xd8ec4)
Address 0x7fffffffce47 is located in stack of thread T0 at offset 87 in frame
#0 0x5555556c6aff in error_literal /gnucobol-3.1.2/cobc/scanner.l:1184
This frame has 1 object(s):
[48, 87) 'lit_out' (line 1186) <== Memory access at offset 87 overflows 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.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:
0x10007fff7970: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff7980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff7990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff79a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff79b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
=>0x10007fff79c0: f1 f1 f1 f1 00 00 00 00[07]f3 f3 f3 f3 f3 00 00
0x10007fff79d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff79e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10007fff79f0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 f2 f2 f2
0x10007fff7a00: 00 f2 f2 f2 00 f2 f2 f2 00 f2 f2 f2 03 f2 03 f2
0x10007fff7a10: 00 02 f3 f3 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
Shadow gap: cc
==5682==ABORTING
Kudo's for the check and well bug report, that looks nice...
I've thought that this possibly was solved already, but after using a fresh checkout of GnuCOBOL 3.2 and running
configure --enable-cobc-internal-checks --enable-debug --enable-hardening && make
:So reproduced and verified to be applicable to BGGP3, too.
After applying the patch:
And
make checkall
still passes, too.So scoring:
9176 points, well done!
The question is: Are you have to find more issues in GnuCOBOL, possibly with a higher score (lower scores would still be nice)?
:-)
Related
Commit: [r4656]