OS: 6.12.1-artix1-1 Gdb: 16.0.50.20241206-git Python: 3.12.7 related: 1. https://sourceware.org/bugzilla/show_bug.cgi?id=12802 2. https://sourceware.org/gdb/current/onlinedocs/gdb.html/Breakpoints-In-Python.html#Breakpoints-In-Python > You should not alter the execution state of the inferior (i.e., step, next, etc.), > alter the current frame context (i.e., change the current active frame), or alter, > add or delete any breakpoint. As a general rule, you should not alter any data > within GDB or the inferior at this time. That being said, a Use-After-Free should probably be avoided. # Reproduction: repro.py ```python import gdb class StuffBreak(gdb.Breakpoint): def __init__(self): super().__init__("stuff") print("stuff break set") def stop(self): EvilBreak() class EvilBreak(gdb.FinishBreakpoint): def __init__(self): super().__init__() print("finish break set") def stop(self): print("break hit") self.delete() return False StuffBreak() print("Loaded.") ``` repro.c ```c void stuff(){ // i dont do anything :7 } int main() { for( int i = 0; i < 1000000000; ++i){ stuff(); } } ``` ~> gcc repro.c -g -o repro ~> gdb -nx -ex "source repro.py" -ex "run" ./repro [..snip] [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Temporary breakpoint 2 at 0x55555555513b: file minimal.c, line 6. finish break set break hit Fatal signal: Segmentation fault ----- Backtrace ----- 0x61e37e5df56d gdb_internal_backtrace_1 ~/binutils-gdb/gdb/bt-utils.c:121 0x61e37e5df64c _Z22gdb_internal_backtracev ~/binutils-gdb/gdb/bt-utils.c:182 0x61e37e81a3ae handle_fatal_signal ~/binutils-gdb/gdb/event-top.c:1018 0x61e37e81a53d handle_sigsegv ~/binutils-gdb/gdb/event-top.c:1089 0x7bb728c4eb7f ??? 0x61e37e5b6931 _Z18disable_breakpointP10breakpoint ~/binutils-gdb/gdb/breakpoint.c:13674 0x61e37ea6d690 _Z25bpfinishpy_post_stop_hookP23gdbpy_breakpoint_object python/py-finishbreakpoint.c:153 0x61e37ea5a128 _Z31gdbpy_breakpoint_cond_says_stopPK23extension_language_defnP10breakpoint python/py-breakpoint.c:1196 0x61e37e824b5b _Z34breakpoint_ext_lang_cond_says_stopP10breakpoint ~/binutils-gdb/gdb/extension.c:621 0x61e37e59d631 bpstat_check_breakpoint_conditions ~/binutils-gdb/gdb/breakpoint.c:5684 0x61e37e59e19b _Z18bpstat_stop_statusPK13address_spacemP11thread_infoRK17target_waitstatusP6bpstat ~/binutils-gdb/gdb/breakpoint.c:5938 0x61e37e90be91 handle_signal_stop ~/binutils-gdb/gdb/infrun.c:7103 0x61e37e90a3dc handle_inferior_event ~/binutils-gdb/gdb/infrun.c:6556 0x61e37e904fb0 _Z20fetch_inferior_eventv ~/binutils-gdb/gdb/infrun.c:4698 0x61e37e8da6e6 _Z22inferior_event_handler19inferior_event_type ~/binutils-gdb/gdb/inf-loop.c:42 0x61e37e962e9a handle_target_event ~/binutils-gdb/gdb/linux-nat.c:4440 0x61e37ef1d9ba handle_file_event ~/binutils-gdb/gdbsupport/event-loop.cc:551 0x61e37ef1e037 gdb_wait_for_event ~/binutils-gdb/gdbsupport/event-loop.cc:672 0x61e37ef1ccfe _Z16gdb_do_one_eventi ~/binutils-gdb/gdbsupport/event-loop.cc:216 0x61e37ec4e2f0 _Z22wait_sync_command_donev ~/binutils-gdb/gdb/top.c:422 0x61e37ec4e3a4 _Z28maybe_wait_sync_command_donei ~/binutils-gdb/gdb/top.c:439 0x61e37ec4ea75 _Z15execute_commandPKci ~/binutils-gdb/gdb/top.c:572 0x61e37e998a73 catch_command_errors ~/binutils-gdb/gdb/main.c:508 0x61e37e998ca7 execute_cmdargs ~/binutils-gdb/gdb/main.c:607 0x61e37e99a324 captured_main_1 ~/binutils-gdb/gdb/main.c:1305 0x61e37e99a581 captured_main ~/binutils-gdb/gdb/main.c:1330 0x61e37e99a620 _Z8gdb_mainP18captured_main_args ~/binutils-gdb/gdb/main.c:1359 0x61e37e49d91a main ~/binutils-gdb/gdb/gdb.c:38 --------------------- A fatal error internal to GDB has been detected, further debugging is not possible. GDB will now terminate. This is a bug, please report it. For instructions, see: <https://www.gnu.org/software/gdb/bugs/>. fish: Job 1, 'gdb -nx -ex "source minimal.py"…' terminated by signal SIGSEGV (Address boundary error) ~> # Cause: Here is the backtrace of the breakpoint being deleted: ``` #0 delete_breakpoint (bpt=0x555557b91e30) at breakpoint.c:12591 #1 0x0000555555ba2aba in bppy_delete_breakpoint (self=0x7fffdfa5de50, args=0x0) at python/py-breakpoint.c:442 #2 0x00007ffff77b5724 in ?? () from target:/usr/lib/libpython3.12.so.1.0 #3 0x00007ffff779f984 in PyObject_Vectorcall () from target:/usr/lib/libpython3.12.so.1.0 #4 0x00007ffff778607f in _PyEval_EvalFrameDefault () from target:/usr/lib/libpython3.12.so.1.0 #5 0x00007ffff77dbfeb in ?? () from target:/usr/lib/libpython3.12.so.1.0 #6 0x00007ffff77dbaf9 in ?? () from target:/usr/lib/libpython3.12.so.1.0 #7 0x00007ffff7777aa4 in ?? () from target:/usr/lib/libpython3.12.so.1.0 #8 0x00007ffff78a7a4e in _PyObject_CallMethod_SizeT () from target:/usr/lib/libpython3.12.so.1.0 #9 0x0000555555ba72f6 in gdb_PyObject_CallMethod<>(PyObject *, const char *, const char *) (o=0x7fffdd6089d0, method=0x5555560dc3a4 <stop_func> "stop", format=0x0) at python/python-internal.h:149 #10 0x0000555555ba51d1 in gdbpy_breakpoint_cond_says_stop (extlang=0x555556442b80 <extension_language_python>, b=0x555557598590) at python/py-breakpoint.c:1177 #11 0x00005555559851b9 in breakpoint_ext_lang_cond_says_stop (b=0x555557598590) at extension.c:628 #12 0x0000555555733cfa in bpstat_check_breakpoint_conditions (bs=0x555557f161d0, thread=0x5555571f9240) at breakpoint.c:5610 #13 0x0000555555734864 in bpstat_stop_status (aspace=0x555556594670, bp_addr=93824992415523, thread=0x5555571f9240, ws=..., stop_chain=0x555557597810) at breakpoint.c:5864 #14 0x0000555555a63c88 in handle_signal_stop (ecs=0x7fffffffdc40) at infrun.c:7102 #15 0x0000555555a621d3 in handle_inferior_event (ecs=0x7fffffffdc40) at infrun.c:6555 #16 0x0000555555a5cda7 in fetch_inferior_event () at infrun.c:4697 #17 0x0000555555a32175 in inferior_event_handler (event_type=INF_REG_EVENT) at inf-loop.c:41 #18 0x0000555555ab58db in handle_target_event (error=0, client_data=0x0) at linux-nat.c:4440 #19 0x0000555555fd7fd9 in handle_file_event (file_ptr=0x55555759dd90, ready_mask=1) at event-loop.cc:551 #20 0x0000555555fd8656 in gdb_wait_for_event (block=0) at event-loop.cc:672 #21 0x0000555555fd731d in gdb_do_one_event (mstimeout=-1) at event-loop.cc:216 #22 0x0000555555ae9bb9 in start_event_loop () at main.c:400 #23 0x0000555555ae9d85 in captured_command_loop () at main.c:464 #24 0x0000555555aeb941 in captured_main (data=0x7fffffffe020) at main.c:1337 #25 0x0000555555aeb9db in gdb_main (args=0x7fffffffe020) at main.c:1356 #26 0x00005555556428bb in main (argc=2, argv=0x7fffffffe158) at gdb.c:38 #27 0x00007ffff6e0ad6e in __libc_start_call_main (main=main@entry=0x555555642809 <main(int, char**)>, argc=argc@entry=2, argv=argv@entry=0x7fffffffe158) at ../sysdeps/nptl/libc_start_call_main.h:58 #28 0x00007ffff6e0ae2a in __libc_start_main_impl (main=0x555555642809 <main(int, char**)>, argc=2, argv=0x7fffffffe158, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe148) at ../csu/libc-start.c:360 #29 0x0000555555642735 in _start () ``` From what I can tell the problem is that inside handle_signal_stop: ```c /* See if there is a breakpoint/watchpoint/catchpoint/etc. that handles this event. */ ecs->event_thread->control.stop_bpstat = bpstat_stop_status (ecs->event_thread->inf->aspace.get (), ecs->event_thread->stop_pc (), ecs->event_thread, ecs->ws, stop_chain); /* Following in case break condition called a function. */ stop_print_frame = true; /* This is where we handle "moribund" watchpoints. Unlike software breakpoints traps, hardware watchpoint traps are always distinguishable from random traps. If no high-level watchpoint is associated with the reported stop data address anymore, then the bpstat does not explain the signal --- simply make sure to ignore it if `stopped_by_watchpoint' is set. */ if (ecs->event_thread->stop_signal () == GDB_SIGNAL_TRAP && !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat, GDB_SIGNAL_TRAP) && stopped_by_watchpoint) { infrun_debug_printf ("no user watchpoint explains watchpoint SIGTRAP, " "ignoring"); } ``` The `bpstat_stop_status` call returns a bpstat with a breakpoint, however it calls the python callback (through `bpstat_check_breakpoint_conditions`) allowing the breakpoint to be deleted, with the function still having a local pointer to it. The breakpoint is then used in the `bpstat_explains_signal` call also shown above, and segfaults here: ```c 4615 if (sig == GDB_SIGNAL_TRAP) 4616 return true; 4617 } 4618 else 4619 { ► 4620 if (bsp->breakpoint_at->explains_signal (sig)) 4621 return true; 4622 } 4623 } 4624 4625 return false; ``` # Security The bug can probably be used to gain code execution though reallocating a 0x170 heap object with an arbitrary function pointer at the correct offset. That being said it requires executing arbitrary python code in the first place.
This also works: repro.c ```c void stuff(){ // i dont do anything :7 } int main() { stuff(); } ```
This seems to works: ... diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index 75f50e1f423..11c205d9dfb 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -1168,12 +1168,18 @@ gdbpy_breakpoint_cond_says_stop (const struct extension_langua ge_defn *extlang, gdbpy_enter enter_py (b->gdbarch); + /* Create a reference to the python object, keeping it alive in case the + breakpoint gets deleted in the stop method. */ + gdbpy_ref<> py_bp_ref = gdbpy_ref<>::new_reference (py_bp); + if (bp_obj->is_finish_bp) bpfinishpy_pre_stop_hook (bp_obj); if (PyObject_HasAttrString (py_bp, stop_func)) { gdbpy_ref<> result = gdbpy_call_method (py_bp, stop_func); + if (bp_obj->bp == nullptr) + error (_("Breakpoint deleted in %s.stop"), Py_TYPE (bp_obj)->tp_name); stop = 1; if (result != NULL) ... getting us: ... $ gdb -q -ex "source repro.py" -ex "run" ./repro Reading symbols from ./repro... Breakpoint 1 at 0x40049b: file repro.c, line 3. stuff break set Loaded. Starting program: /data/vries/gdb/repro [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Temporary breakpoint 2 at 0x4004b9: file repro.c, line 6. finish break set break hit Breakpoint deleted in EvilBreak.stop (gdb) ...
https://sourceware.org/pipermail/gdb-patches/2024-December/213890.html