Created attachment 9356 [details] poc command ./busybox_unstripped bc text.txt bc 1.33.1 Adapted from https://github.com/gavinhoward/bc Original code (c) 2018 Gavin D. Howard and contributors ================================================================= ==556554==ERROR: AddressSanitizer: heap-use-after-free on address 0x6030000002e0 at pc 0x7f0419528d4d bp 0x7ffd18813e60 sp 0x7ffd18813608 READ of size 2 at 0x6030000002e0 thread T0 #0 0x7f0419528d4c (/lib/x86_64-linux-gnu/libasan.so.5+0x73d4c) #1 0x564f7483d148 in bc_error_at miscutils/bc.c:988 0x6030000002e0 is located 0 bytes inside of 32-byte region [0x6030000002e0,0x603000000300) freed by thread T0 here: #0 0x7f04195c2ffe in __interceptor_realloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe) #1 0x564f747fd10c in xrealloc libbb/xfuncs_printf.c:61 previously allocated by thread T0 here: #0 0x7f04195c2bc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8) #1 0x564f747fd074 in xmalloc libbb/xfuncs_printf.c:50 SUMMARY: AddressSanitizer: heap-use-after-free (/lib/x86_64-linux-gnu/libasan.so.5+0x73d4c) Shadow bytes around the buggy address: 0x0c067fff8000: fa fa 00 00 07 fa fa fa 00 00 06 fa fa fa 00 00 0x0c067fff8010: 07 fa fa fa 00 00 00 fa fa fa 00 00 06 fa fa fa 0x0c067fff8020: 00 00 00 01 fa fa 00 00 00 02 fa fa 00 00 00 01 0x0c067fff8030: fa fa 00 00 07 fa fa fa 00 00 04 fa fa fa 00 00 0x0c067fff8040: 00 01 fa fa 00 00 00 fa fa fa 00 00 00 fa fa fa =>0x0c067fff8050: 00 00 00 00 fa fa 00 00 00 00 fa fa[fd]fd fd fd 0x0c067fff8060: fa fa 00 00 00 00 fa fa fa fa fa fa fa fa fa fa 0x0c067fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c067fff80a0: 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 ==556554==ABORTING
Debug with gdb, print stack ========================================================================== Adapted from https://github.com/gavinhoward/bc Original code (c) 2018 Gavin D. Howard and contributors Breakpoint 1, bc_vec_grow (v=v@entry=0x69b160, n=n@entry=1) at miscutils/bc.c:1095 1095 { (gdb) bt #0 bc_vec_grow (v=v@entry=0x69b160, n=n@entry=1) at miscutils/bc.c:1095 #1 0x000000000040adf4 in bc_vec_npush (v=v@entry=0x69b160, n=n@entry=1, data=data@entry=0x7fffffffda9f) at miscutils/bc.c:1149 #2 0x000000000040ae2b in bc_vec_push (v=v@entry=0x69b160, data=data@entry=0x7fffffffda9f) at miscutils/bc.c:1157 #3 0x000000000040cfe2 in bc_vec_pushByte (data=94 '^', v=0x69b160) at miscutils/bc.c:1182 #4 xc_read_line (vec=vec@entry=0x69b160, fp=0x69f6b0) at miscutils/bc.c:2654 #5 0x000000000040d03f in peek_inbuf () at miscutils/bc.c:2893 #6 0x000000000040d0d2 in zxc_lex_number (last=52 '4') at miscutils/bc.c:2969 #7 0x000000000040d6be in zbc_lex_token () at miscutils/bc.c:3343 #8 zxc_lex_next () at miscutils/bc.c:3057 #9 0x000000000040e2c3 in zbc_parse_name (type=type@entry=0x7fffffffdbec, flags=flags@entry=2 '\002') at miscutils/bc.c:3961 #10 0x000000000040df58 in zbc_parse_expr (flags=flags@entry=2 '\002') at miscutils/bc.c:4908 #11 0x000000000040e5bf in zbc_parse_stmt_possibly_auto (auto_allowed=auto_allowed@entry=false) at miscutils/bc.c:4707 #12 0x000000000040ed83 in zbc_parse_stmt () at miscutils/bc.c:3818 #13 zbc_parse_stmt_or_funcdef () at miscutils/bc.c:4782 #14 zxc_vm_process (text=text@entry=0x497b06 "") at miscutils/bc.c:6959 #15 0x00000000004104de in zxc_vm_execute_FILE (fp=fp@entry=0x69f6b0, filename=filename@entry=0x7fffffffe586 "new_txt") at miscutils/bc.c:7060 #16 0x00000000004107e3 in zxc_vm_file (file=0x7fffffffe586 "new_txt") at miscutils/bc.c:7077 #17 zxc_vm_exec () at miscutils/bc.c:7347 #18 xc_vm_run () at miscutils/bc.c:7508 #19 bc_main (argc=<optimized out>, argv=<optimized out>) at miscutils/bc.c:7541 #20 0x000000000040784d in run_applet_no_and_exit (applet_no=applet_no@entry=10, name=name@entry=0x7fffffffe583 "bc", argv=argv@entry=0x7fffffffe1b0) at libbb/appletlib.c:1023 #21 0x0000000000407bd2 in run_applet_and_exit (name=0x7fffffffe583 "bc", argv=argv@entry=0x7fffffffe1b0) at libbb/appletlib.c:1046 #22 0x0000000000407b23 in busybox_main (argv=0x7fffffffe1b0) at libbb/appletlib.c:973 #23 run_applet_and_exit (name=<optimized out>, argv=argv@entry=0x7fffffffe1a8) at libbb/appletlib.c:1035 #24 0x0000000000407c62 in main (argc=<optimized out>, argv=0x7fffffffe1a8) at libbb/appletlib.c:1180 ===================================================================== In zxc_lex_next(): p->lex_next_at = p->lex_inbuf; then, zbc_len_token->...->bc_vec_grow will realloc lex_inbuf, The reallocation is done by either: a) expanding or contracting the existing area pointed to by ptr, if possible. The contents of the area remain unchanged up to the lesser of the new and old sizes. If the area is expanded, the contents of the new part of the array are undefined. b) allocating a new memory block of size new_size bytes, copying memory area with size equal the lesser of the new and the old sizes, and freeing the old block. In the b scenario, continuing to use lex_next_at leads to use-after-free. In in bc_error_at miscutils/bc.c:988 leads to use-after-free static ERRORFUNC int bc_error_at(const char *msg) { const char *err_at = G.prs.lex_next_at; if (err_at) { IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt( "%s at '%.*s'", msg, (int)(strchrnul(err_at, '\n') - err_at), err_at ); } ..... }
Possible fix: diff --git a/miscutils/bc.c b/miscutils/bc.c index ab785bbc8..44e55eeea 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -3048,16 +3048,16 @@ static BC_STATUS zxc_lex_next(void) if (peek_inbuf() == '\0') RETURN_STATUS(BC_STATUS_SUCCESS); } - p->lex_next_at = p->lex_inbuf; - dbg_lex("next string to parse:'%.*s'", - (int)(strchrnul(p->lex_next_at, '\n') - p->lex_next_at), - p->lex_next_at - ); if (IS_BC) { IF_BC(s = zbc_lex_token()); } else { IF_DC(s = zdc_lex_token()); } + p->lex_next_at = p->lex_inbuf; + dbg_lex("next string to parse:'%.*s'", + (int)(strchrnul(p->lex_next_at, '\n') - p->lex_next_at), + p->lex_next_at + ); } while (!s && p->lex == XC_LEX_WHITESPACE); dbg_lex("p->lex from string:%d", p->lex);
I can't reproduce it. Does this fix work for you? --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -2892,6 +2892,8 @@ static char peek_inbuf(void) ) { xc_read_line(&G.input_buffer, G.prs.lex_input_fp); G.prs.lex_inbuf = G.input_buffer.v; + /* lex_next_at may point to now-freed data, update it */ + G.prs.lex_next_at = G.prs.lex_inbuf; if (G.input_buffer.len <= 1) // on EOF, len is 1 (NUL byte) G.prs.lex_input_fp = NULL; }
yes, fix it