Menu

#844 Stack-based buffer overflow in literal_for_diagnostic() function

GC 3.2
closed
7
2022-07-14
2022-07-14
No

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
1 Attachments

Related

Commit: [r5280]

Discussion

  • Simon Sobisch

    Simon Sobisch - 2022-07-14
    • labels: buffer overflow --> buffer overflow, cobc
    • status: open --> closed
    • assigned_to: Simon Sobisch
    • Priority: 5 - default --> 7
     
  • Simon Sobisch

    Simon Sobisch - 2022-07-14

    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:

    $ ./pre-inst-env cobc crash.cob
    crash.cob:1: warning: line not terminated by a newline [-Wmissing-newline]
    crash.cob:1: error: invalid literal: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ...'
    *** stack smashing detected ***: terminated
    Aborted
    $ ./pre-inst-env valgrind cobc/.libs/cobc crash.cob
    ==8544== Memcheck, a memory error detector
    ==8544== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==8544== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
    ==8544== Command: cobc/.libs/cobc crash.cob
    ==8544==
    crash.cob:1: warning: line not terminated by a newline [-Wmissing-newline]
    crash.cob:1: error: invalid literal: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ...'
    *** stack smashing detected ***: terminated
    ==8544==
    ==8544== Process terminating with default action of signal 6 (SIGABRT)
    ==8544==    at 0x48F8CE1: raise (raise.c:51)
    ==8544==    by 0x48E2536: abort (abort.c:79)
    ==8544==    by 0x493B767: __libc_message (libc_fatal.c:155)
    ==8544==    by 0x49CA721: __fortify_fail (fortify_fail.c:26)
    ==8544==    by 0x49CA6FF: __stack_chk_fail (stack_chk_fail.c:24)
    ==8544==    by 0x15C17F: error_literal (scanner.l:1261)
    ==8544==    by 0x15E3B0: read_literal (scanner.l:1321)
    ==8544==    by 0x160F4A: yylex (scanner.l:352)
    ==8544==    by 0x146584: yyparse (parser.c:13787)
    ==8544==    by 0x12A9D7: process_translate (cobc.c:7556)
    ==8544==    by 0x12A9D7: process_file (cobc.c:8691)
    ==8544==    by 0x12A9D7: main (cobc.c:8887)
    ==8544==
    ==8544== HEAP SUMMARY:
    ==8544==     in use at exit: 153,106 bytes in 267 blocks
    ==8544==   total heap usage: 366 allocs, 99 frees, 298,630 bytes allocated
    ==8544==
    ==8544== LEAK SUMMARY:
    ==8544==    definitely lost: 0 bytes in 0 blocks
    ==8544==    indirectly lost: 0 bytes in 0 blocks
    ==8544==      possibly lost: 0 bytes in 0 blocks
    ==8544==    still reachable: 153,106 bytes in 267 blocks
    ==8544==         suppressed: 0 bytes in 0 blocks
    ==8544== Rerun with --leak-check=full to see details of leaked memory
    ==8544==
    ==8544== For lists of detected and suppressed errors, rerun with: -s
    ==8544== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    

    So reproduced and verified to be applicable to BGGP3, too.
    After applying the patch:

    $ ./pre-inst-env valgrind cobc/.libs/cobc crash.cob
    ==8682== Memcheck, a memory error detector
    ==8682== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==8682== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
    ==8682== Command: cobc/.libs/cobc crash.cob
    ==8682==
    crash.cob:1: warning: line not terminated by a newline [-Wmissing-newline]
    crash.cob:1: error: invalid literal: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ...'
    crash.cob:1: error: missing terminating " character
    crash.cob:1: error: PROGRAM-ID header missing
    crash.cob:1: error: PROCEDURE DIVISION header missing
    crash.cob:1: error: syntax error, unexpected Literal
    ==8682==
    ==8682== HEAP SUMMARY:
    ==8682==     in use at exit: 0 bytes in 0 blocks
    ==8682==   total heap usage: 460 allocs, 460 frees, 306,210 bytes allocated
    ==8682==
    ==8682== All heap blocks were freed -- no leaks are possible
    ==8682==
    ==8682== For lists of detected and suppressed errors, rerun with: -s
    ==8682== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    

    And make checkall still passes, too.

    So scoring:

    • base points 4096
    • 40+ (size of file in bytes)
    • 1024+ for the well writeup
    • 4096+ for the patch, that was included with [r4656]

    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]


Log in to post a comment.