From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id 778B8384842A; Thu, 12 Aug 2021 21:10:33 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 778B8384842A From: "simark at simark dot ca" To: gdb-prs@sourceware.org Subject: [Bug gdb/28224] New: Use-after-free when calling a GNU ifunc Date: Thu, 12 Aug 2021 21:10:33 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: new X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gdb X-Bugzilla-Component: gdb X-Bugzilla-Version: HEAD X-Bugzilla-Keywords: X-Bugzilla-Severity: normal X-Bugzilla-Who: simark at simark dot ca X-Bugzilla-Status: NEW X-Bugzilla-Resolution: X-Bugzilla-Priority: P2 X-Bugzilla-Assigned-To: unassigned at sourceware dot org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: bug_id short_desc product version bug_status bug_severity priority component assigned_to reporter target_milestone Message-ID: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://sourceware.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 X-BeenThere: gdb-prs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-prs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 12 Aug 2021 21:10:33 -0000 https://sourceware.org/bugzilla/show_bug.cgi?id=3D28224 Bug ID: 28224 Summary: Use-after-free when calling a GNU ifunc Product: gdb Version: HEAD Status: NEW Severity: normal Priority: P2 Component: gdb Assignee: unassigned at sourceware dot org Reporter: simark at simark dot ca Target Milestone: --- This was initially reported by "randomuser" on IRC. With the test program: --- #include int main(int argc, char ** argv) { const char * a =3D "abcd"; return strlen(argv[0]); } --- This crashes GDB: $ ./gdb -q -nx --data-directory=3Ddata-directory -batch a.out -ex start -ex= n -ex 'p (int)strlen(a)' =3D=3D620033=3D=3DERROR: AddressSanitizer: heap-use-after-free on address 0x62100320f270 at pc 0x55c7a9f79c10 bp 0x7ffef4d555b0 sp 0x7ffef4d555a0=20= =20=20=20=20=20=20=20=20 READ of size 8 at 0x62100320f270 thread T0=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20 #0 0x55c7a9f79c0f in get_frame_arch(frame_info*) /home/smarchi/src/binutils-gdb/gdb/frame.c:2841=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #1 0x55c7a9f7a084 in get_frame_sp(frame_info*) /home/smarchi/src/binutils-gdb/gdb/frame.c:2929=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #2 0x55c7aa0d259b in call_function_by_hand_dummy(value*, type*, gdb::array_view, void (*)(void*, int), void*) /home/smarchi/src/binutils-gdb/gdb/infcall.c:845=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #3 0x55c7aa0d1e53 in call_function_by_hand(value*, type*, gdb::array_view) /home/smarchi/src/binutils-gdb/gdb/infcall.c:742= =20=20=20=20=20=20=20 #4 0x55c7a9eedb32 in evaluate_subexp_do_call(expression*, noside, value= *, gdb::array_view, char const*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:674=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #5 0x55c7a9eee0d8 in expr::operation::evaluate_funcall(type*, expressio= n*, noside, char const*, std::__debug::vector >, s td::allocator > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:702=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #6 0x55c7a96ce44c in expr::var_msym_value_operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector >, std::allocator > > > const&) /home/smarchi/src/binutils-gdb/gdb/expop.h:739=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #7 0x55c7a99bb864 in expr::funcall_operation::evaluate(type*, expressio= n*, noside) /home/smarchi/src/binutils-gdb/gdb/expop.h:2178=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #8 0x55c7a9eeb4c5 in expression::evaluate(type*, noside) /home/smarchi/src/binutils-gdb/gdb/eval.c:101=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #9 0x55c7a9eeb614 in evaluate_expression(expression*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:115=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #10 0x55c7aa4a6b3c in process_print_command_args /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1312=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #11 0x55c7aa4a6d14 in print_command_1 /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1325=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #12 0x55c7aa4a7884 in print_command /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1458=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #13 0x55c7a9a73f73 in do_simple_func /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:97=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #14 0x55c7a9a7f0e1 in cmd_func(cmd_list_element*, char const*, int) /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:2141=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #15 0x55c7aa9f7ef9 in execute_command(char const*, int) /home/smarchi/src/binutils-gdb/gdb/top.c:674=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20 #16 0x55c7aa2c630d in catch_command_errors /home/smarchi/src/binutils-gdb/gdb/main.c:523=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #17 0x55c7aa2c69df in execute_cmdargs /home/smarchi/src/binutils-gdb/gdb/main.c:618=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #18 0x55c7aa2c9f9d in captured_main_1 /home/smarchi/src/binutils-gdb/gdb/main.c:1322=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #19 0x55c7aa2ca679 in captured_main /home/smarchi/src/binutils-gdb/gdb/main.c:1343=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #20 0x55c7aa2ca71a in gdb_main(captured_main_args*) /home/smarchi/src/binutils-gdb/gdb/main.c:1368=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #21 0x55c7a96259d1 in main /home/smarchi/src/binutils-gdb/gdb/gdb.c:32= =20=20=20=20=20=20 #22 0x7f52a85bf0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20 #23 0x55c7a96257ad in _start (/home/smarchi/build/binutils-gdb/gdb/gdb+0x5417ad)=20=20=20 0x62100320f270 is located 368 bytes inside of 4064-byte region [0x62100320f100,0x6210032100e0)=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20 freed by thread T0 here:=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20 #0 0x7f52a95b57cf in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10d7cf)=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #1 0x55c7ab16e1fb in rpl_free /home/smarchi/src/binutils-gdb/gnulib/import/free.c:40=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #2 0x55c7a9f7ad93 in xfree /home/smarchi/src/binutils-gdb/gdb/../gdbsupport/common-utils.h:66=20=20=20= =20=20=20=20=20=20=20=20=20=20=20 #3 0x55c7ab120684 in call_freefun /home/smarchi/src/binutils-gdb/libiberty/obstack.c:103 #4 0x55c7ab1210c3 in _obstack_free /home/smarchi/src/binutils-gdb/libiberty/obstack.c:280 #5 0x55c7a9f75b2b in reinit_frame_cache() /home/smarchi/src/binutils-gdb/gdb/frame.c:1999 #6 0x55c7aa66ea21 in regcache_write_pc(regcache*, unsigned long) /home/smarchi/src/binutils-gdb/gdb/regcache.c:1372 #7 0x55c7aa124729 in proceed(unsigned long, gdb_signal) /home/smarchi/src/binutils-gdb/gdb/infrun.c:3075 #8 0x55c7aa0d114d in run_inferior_call /home/smarchi/src/binutils-gdb/gdb/infcall.c:611 #9 0x55c7aa0d4d3c in call_function_by_hand_dummy(value*, type*, gdb::array_view, void (*)(void*, int), void*) /home/smarchi/src/binutils-gdb/gdb/infcall.c:1277 #10 0x55c7aa0d1e53 in call_function_by_hand(value*, type*, gdb::array_view) /home/smarchi/src/binutils-gdb/gdb/infcall.c:742 #11 0x55c7a9ee0a4d in elf_gnu_ifunc_resolve_addr /home/smarchi/src/binutils-gdb/gdb/elfread.c:917 #12 0x55c7aa0cfb66 in find_function_addr(value*, type**, type**) /home/smarchi/src/binutils-gdb/gdb/infcall.c:284 #13 0x55c7aa0d2394 in call_function_by_hand_dummy(value*, type*, gdb::array_view, void (*)(void*, int), void*) /home/smarchi/src/binutils-gdb/gdb/infcall.c:814 #14 0x55c7aa0d1e53 in call_function_by_hand(value*, type*, gdb::array_view) /home/smarchi/src/binutils-gdb/gdb/infcall.c:742 #15 0x55c7a9eedb32 in evaluate_subexp_do_call(expression*, noside, valu= e*, gdb::array_view, char const*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:674 #16 0x55c7a9eee0d8 in expr::operation::evaluate_funcall(type*, expressi= on*, noside, char const*, std::__debug::vector >,=20 std::allocator > > > const&) /home/smarchi/src/binutils-gdb/gdb/eval.c:702 #17 0x55c7a96ce44c in expr::var_msym_value_operation::evaluate_funcall(type*, expression*, noside, std::__debug::vector > , std::allocator > > > const&) /home/smarchi/src/binutils-gdb/gdb/expop.h:739 #18 0x55c7a99bb864 in expr::funcall_operation::evaluate(type*, expressi= on*, noside) /home/smarchi/src/binutils-gdb/gdb/expop.h:2178 #19 0x55c7a9eeb4c5 in expression::evaluate(type*, noside) /home/smarchi/src/binutils-gdb/gdb/eval.c:101 #20 0x55c7a9eeb614 in evaluate_expression(expression*, type*) /home/smarchi/src/binutils-gdb/gdb/eval.c:115 #21 0x55c7aa4a6b3c in process_print_command_args /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1312 #22 0x55c7aa4a6d14 in print_command_1 /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1325 #23 0x55c7aa4a7884 in print_command /home/smarchi/src/binutils-gdb/gdb/printcmd.c:1458 #24 0x55c7a9a73f73 in do_simple_func /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:97 #25 0x55c7a9a7f0e1 in cmd_func(cmd_list_element*, char const*, int) /home/smarchi/src/binutils-gdb/gdb/cli/cli-decode.c:2141 #26 0x55c7aa9f7ef9 in execute_command(char const*, int) /home/smarchi/src/binutils-gdb/gdb/top.c:674 #27 0x55c7aa2c630d in catch_command_errors /home/smarchi/src/binutils-gdb/gdb/main.c:523 #28 0x55c7aa2c69df in execute_cmdargs /home/smarchi/src/binutils-gdb/gdb/main.c:618 #29 0x55c7aa2c9f9d in captured_main_1 /home/smarchi/src/binutils-gdb/gdb/main.c:1322 previously allocated by thread T0 here:=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20 #0 0x7f52a95b5bc8 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 #1 0x55c7a976c1de in xmalloc /home/smarchi/src/binutils-gdb/gdb/alloc.c= :60 #2 0x55c7ab12058e in call_chunkfun /home/smarchi/src/binutils-gdb/libiberty/obstack.c:94 #3 0x55c7ab120741 in _obstack_begin_worker /home/smarchi/src/binutils-gdb/libiberty/obstack.c:141 #4 0x55c7ab1209f8 in _obstack_begin /home/smarchi/src/binutils-gdb/libiberty/obstack.c:164 #5 0x55c7a9f75b52 in reinit_frame_cache() /home/smarchi/src/binutils-gdb/gdb/frame.c:2000 #6 0x55c7aa9caf4d in switch_to_thread(thread_info*) /home/smarchi/src/binutils-gdb/gdb/thread.c:1319 #7 0x55c7aa9cb158 in scoped_restore_current_thread::restore() /home/smarchi/src/binutils-gdb/gdb/thread.c:1345 #8 0x55c7aa9cb3ae in scoped_restore_current_thread::~scoped_restore_current_thread() /home/smarchi/src/binutils-gdb/gdb/thread.c:1365 #9 0x55c7aa134b5c in stop_all_threads() /home/smarchi/src/binutils-gdb/gdb/infrun.c:4893 #10 0x55c7aa146ed2 in stop_waiting /home/smarchi/src/binutils-gdb/gdb/infrun.c:7937 #11 0x55c7aa148117 in end_stepping_range /home/smarchi/src/binutils-gdb/gdb/infrun.c:8104 #12 0x55c7aa141837 in process_event_stop_test /home/smarchi/src/binutils-gdb/gdb/infrun.c:7192 #13 0x55c7aa13ca5f in handle_signal_stop /home/smarchi/src/binutils-gdb/gdb/infrun.c:6402 #14 0x55c7aa13830b in handle_inferior_event /home/smarchi/src/binutils-gdb/gdb/infrun.c:5663 #15 0x55c7aa12c931 in fetch_inferior_event() /home/smarchi/src/binutils-gdb/gdb/infrun.c:4059=20 #16 0x55c7aa0cb8f4 in inferior_event_handler(inferior_event_type) /home/smarchi/src/binutils-gdb/gdb/inf-loop.c:41 #17 0x55c7aa246b61 in handle_target_event /home/smarchi/src/binutils-gdb/gdb/linux-nat.c:4208=20 #18 0x55c7ab08c5d6 in handle_file_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:575 #19 0x55c7ab08ce17 in gdb_wait_for_event /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:701 #20 0x55c7ab08ab38 in gdb_do_one_event() /home/smarchi/src/binutils-gdb/gdbsupport/event-loop.cc:212 #21 0x55c7aa9f6e8f in wait_sync_command_done() /home/smarchi/src/binutils-gdb/gdb/top.c:528 #22 0x55c7aa9f705c in maybe_wait_sync_command_done(int) /home/smarchi/src/binutils-gdb/gdb/top.c:545 #23 0x55c7aa9f7f06 in execute_command(char const*, int) /home/smarchi/src/binutils-gdb/gdb/top.c:676 #24 0x55c7aa2c630d in catch_command_errors /home/smarchi/src/binutils-gdb/gdb/main.c:523 #25 0x55c7aa2c69df in execute_cmdargs /home/smarchi/src/binutils-gdb/gdb/main.c:618 #26 0x55c7aa2c9f9d in captured_main_1 /home/smarchi/src/binutils-gdb/gdb/main.c:1322 #27 0x55c7aa2ca679 in captured_main /home/smarchi/src/binutils-gdb/gdb/main.c:1343 #28 0x55c7aa2ca71a in gdb_main(captured_main_args*) /home/smarchi/src/binutils-gdb/gdb/main.c:1368 #29 0x55c7a96259d1 in main /home/smarchi/src/binutils-gdb/gdb/gdb.c:32 If we look above where the memory was freed, we see two nested call_function_by_hand_dummy calls. That is because the strlen symbol is an ifunc. It's a function that returns a pointer to the right strlen implementation to use. So to call the real strlen function, GDB first need= s to determine its address, and for that calls the strlen ifunc. The pointer to= the current pointer that it got in the outer call_function_by_hand_dummy call g= ets invalidated when the inner call_function_by_hand_dummy runs. A fix is to re-read the current frame after the find_function_addr call. This gives the same frame as before, but a fresh / non-stale frame_info object. >>From c36675ffcd23a6e5988998bd74d5e79d1638fd13 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Thu, 12 Aug 2021 16:07:55 -0400 Subject: [PATCH] fix Change-Id: Id4e9abe3db1436bb429bdc8136a64f184f7170d5 --- gdb/infcall.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gdb/infcall.c b/gdb/infcall.c index 40298fb1318..f0bbac347c8 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -813,6 +813,8 @@ call_function_by_hand_dummy (struct value *function, type *values_type; CORE_ADDR funaddr =3D find_function_addr (function, &values_type, &ftype= ); + frame =3D get_current_frame (); + if (values_type =3D=3D NULL) values_type =3D default_return_type; if (values_type =3D=3D NULL) --=20 2.32.0 Testing-wise, I was surprised that this isn't caught by gdb.base/gnu-ifunc.= exp, which is quite extensive. This is because it does multiple consecutive infcalls, and starts with some invalid ones. For example, it starts with: gdb_test "p gnu_ifunc()" "Too few arguments in function call\\." This calls the ifunc successfully, and the ifunc result gets cached (elf_gnu_ifunc_record_cache). But since the number of arguments don't matc= h, the infcall is aborted and we don't reach where the crash would happen. Wh= en the test then does a second infcall with the proper arguments: gdb_test "p gnu_ifunc (3)" " =3D 4" The inner infcall to resolve the ifunc isn't done, as the value is cached. = So the frame_info pointer in call_function_by_hand_dummy does not get invalida= ted. The test can be modified to see the crash like this: diff --git a/gdb/testsuite/gdb.base/gnu-ifunc.exp b/gdb/testsuite/gdb.base/gnu-ifunc.exp index 4ec529130ce9..4ce5f4e2dbb8 100644 --- a/gdb/testsuite/gdb.base/gnu-ifunc.exp +++ b/gdb/testsuite/gdb.base/gnu-ifunc.exp @@ -246,13 +246,8 @@ proc misc_tests {resolver_attr resolver_debug final_de= bug} { # Test GDB will automatically indirect the call. if {!$resolver_debug && !$final_debug} { - gdb_test "p gnu_ifunc()" \ - "'${dot}final' has unknown return type; cast the call to its declared return type" - gdb_test "p gnu_ifunc (3)" \ - "'${dot}final' has unknown return type; cast the call to its declared return type" gdb_test "p (int) gnu_ifunc (3)" " =3D 4" } else { - gdb_test "p gnu_ifunc()" "Too few arguments in function call\\." gdb_test "p gnu_ifunc (3)" " =3D 4" } So, the test would need to be modified to have better coverage. Either by restarting GDB between each inferior call attempt (that would increase the running time of the test, but correctness is more important) or by having a= way to clear the ifunc cache. --=20 You are receiving this mail because: You are on the CC list for the bug.=