Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory Corruption Bugs in Liblisp through commit 4c65969 #1

Open
Halcy0nic opened this issue Nov 6, 2023 · 1 comment
Open

Memory Corruption Bugs in Liblisp through commit 4c65969 #1

Halcy0nic opened this issue Nov 6, 2023 · 1 comment

Comments

@Halcy0nic
Copy link

Overview

Hi there @howerj! I love the library and per usual I ran my fuzz tests in the background and found just a couple of memory corruption bugs in liblisp through 4c65969. I have included all of the files necessary for reproducing each bug. If I get some time I'll work on submitting a pull request.

  • Use-after-free in void hash_destroy(hash_table_t *h) at hash.c, lines 70-84.
  • Out-of-bounds read in unsigned get_length(lisp_cell_t * x) at eval.c, line 272.

Use-after-free

There exist a use-after-free bug in void hash_destroy(hash_table_t *h) at hash.c, lines 70-84. I believe a potential fix for this would be to set the pointer 'hash_table_t *h' to null after passing it to 'free'.

Source Code

liblisp/src/hash.c

Lines 70 to 85 in 4c65969

void hash_destroy(hash_table_t * h) {
if (!h)
return;
for (size_t i = 0; i < h->len; i++)
if (h->table[i]) {
hash_entry_t *prev = NULL;
for (hash_entry_t *cur = h->table[i]; cur; prev = cur, cur = cur->next) {
h->free_key(cur->key);
h->free_val(cur->val);
free(prev);
}
free(prev);
}
free(h->table);
free(h);
}

File for reproduction

uaf-liblisp.zip

Address Sanitizer Output

=================================================================
==134443==ERROR: AddressSanitizer: heap-use-after-free on address 0x6080000006a8 at pc 0x7f9da3ee019b bp 0x7ffce0e7abe0 sp 0x7ffce0e7abd8
READ of size 8 at 0x6080000006a8 thread T0
    #0 0x7f9da3ee019a in hash_destroy src/hash.c:73
    #1 0x7f9da3ee019a in hash_destroy src/hash.c:70
    #2 0x7f9da3edf78f in gc_free src/gc.c:50
    #3 0x7f9da3edf78f in lisp_gc_sweep_only src/gc.c:124
    #4 0x7f9da3ee4011 in lisp_destroy src/lisp.c:80
    #5 0x7f9da3ee871e in main_lisp_env src/repl.c:236
    #6 0x7f9da36456c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x7f9da3645784 in __libc_start_main_impl ../csu/libc-start.c:360
    #8 0x5615bae755b0 in _start (/home/kali/projects/fuzzing/liblisp/lisp+0x25b0) (BuildId: 0827e7ee54f24ed60d4875e0b6fa13ce80a7782b)

0x6080000006a8 is located 8 bytes inside of 96-byte region [0x6080000006a0,0x608000000700)
freed by thread T0 here:
    #0 0x7f9da38d7298 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x7f9da3ee750e in read_hash src/read.c:211
    #2 0x7f9da3ee750e in reader src/read.c:279

previously allocated by thread T0 here:
    #0 0x7f9da38d7fa7 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
    #1 0x7f9da3edfe97 in hash_create_custom src/hash.c:57

SUMMARY: AddressSanitizer: heap-use-after-free src/hash.c:73 in hash_destroy
Shadow bytes around the buggy address:
  0x608000000400: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 07 fa
  0x608000000480: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 00
  0x608000000500: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 07 fa
  0x608000000580: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 07 fa
  0x608000000600: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 07 fa
=>0x608000000680: fa fa fa fa fd[fd]fd fd fd fd fd fd fd fd fd fd
  0x608000000700: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
  0x608000000780: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x608000000800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x608000000880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x608000000900: 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
==134443==ABORTING

Out-of-bounds Read

There exist an out-of-bounds read in unsigned get_length(lisp_cell_t * x) at eval.c, line 272. The OOB read comes from the statement 'return (uintptr_t)(x->p[1].v);' when processing a malformed symbol. A potential fix for this would be to add check ensuring 'x->p[1].v' is within the bounds of the array.

Source Code

liblisp/src/eval.c

Lines 271 to 272 in 4c65969

case SYMBOL:
return (uintptr_t)(x->p[1].v);

File for reproduction

oob-read-liblisp.zip

Address Sanitizer Output

=================================================================
==134237==ERROR: AddressSanitizer: global-buffer-overflow on address 0x7f76159f9430 at pc 0x7f76159c98d4 bp 0x7ffe27551db0 sp 0x7ffe27551da8
READ of size 8 at 0x7f76159f9430 thread T0
    #0 0x7f76159c98d3 in get_length src/eval.c:272
    #1 0x7f76159c98d3 in get_length src/eval.c:264
    #2 0x7f76159d6abf in subr_greater src/subr.c:325
    #3 0x7f76159cbecc in eval src/eval.c:707
    #4 0x7f76159d4c85 in lisp_repl src/repl.c:149
    #5 0x7f76159d4fb8 in main_lisp_env src/repl.c:212
    #6 0x7f76157f66c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x7f76157f6784 in __libc_start_main_impl ../csu/libc-start.c:360
    #8 0x563c29c2b5b0 in _start (/home/kali/projects/fuzzing/liblisp/lisp+0x25b0) (BuildId: 0827e7ee54f24ed60d4875e0b6fa13ce80a7782b)

0x7f76159f9430 is located 48 bytes before global variable '_nil' defined in 'src/subr.c:137:1' (0x7f76159f9460) of size 16
0x7f76159f9430 is located 0 bytes after global variable '_tee' defined in 'src/subr.c:137:1' (0x7f76159f9420) of size 16
SUMMARY: AddressSanitizer: global-buffer-overflow src/eval.c:272 in get_length
Shadow bytes around the buggy address:
  0x7f76159f9180: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 f9 f9
  0x7f76159f9200: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 f9 f9
  0x7f76159f9280: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 f9 f9
  0x7f76159f9300: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 f9 f9
  0x7f76159f9380: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 f9 f9
=>0x7f76159f9400: f9 f9 f9 f9 00 00[f9]f9 f9 f9 f9 f9 00 00 f9 f9
  0x7f76159f9480: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f76159f9500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f76159f9580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f76159f9600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f76159f9680: 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
==134237==ABORTING
=================================================================

Thank you!
@Halcy0nic

@howerj
Copy link
Owner

howerj commented Nov 14, 2023

Thanks for the work, I've got other things to do at the moment that are more pressing, but if you manage to make a pull request that fixes these issues I'll merge it. I still haven't got around to looking at the libforth problems just yet.

howerj added a commit that referenced this issue Feb 28, 2024
* Some spurious warnings have been fixed about code falling through
before a long-jump.
* Issue #2 has been fixed, in the `io_sin` function.
* A partial fix to one of the issues raised in issue #1 has been
applied.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants