Consider the following D program: ``` int main() { ubyte[] data = new ubyte[1_000_000]; return 0; } ``` The program can be compiled with dmd using the following command: ``` dmd -g test.d ``` I used dmd v2.109.1 on Arch Linux x86_64. Alternatively it can be compiled with gdc or ldc with the same result. The program should be loaded with gdb and a breakpoint should be set for the return statement. Printing the variable `data` with gdb then results in a segmentation fault in gdb. This can be done in one command: ``` gdb --batch -ex "b test.d:4" -ex "run" -ex "p data" ./test ``` Line 4 should be the return statement. For some array sizes gdb only crashes sometimes, so it may be necessary to increase the array size or run the test multiple times. I have tested it with gdb 15.1 from the Arch Linux package and gdb 16.0.50.20240722 built from a source snapshot. Here is the full output (with some paths shortened): ``` GNU gdb (GDB) 16.0.50.20240722-git Copyright (C) 2024 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./test... (gdb) b test.d:4 Breakpoint 1 at 0x251d8: file test.d, line 4. (gdb) run Starting program: test [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". Breakpoint 1, D main () at test.d:4 4 return 0; (gdb) p data Fatal signal: Segmentation fault ----- Backtrace ----- 0x613d21952c6b gdb_internal_backtrace_1 gdb-16.0.50.20240722/gdb/bt-utils.c:121 0x613d21952c6b _Z22gdb_internal_backtracev gdb-16.0.50.20240722/gdb/bt-utils.c:167 0x613d21a79584 handle_fatal_signal gdb-16.0.50.20240722/gdb/event-top.c:917 0x613d21a79752 handle_sigsegv gdb-16.0.50.20240722/gdb/event-top.c:990 0x706123a2d76f ??? 0x613d21a822c0 _Z15extract_integerImSt11is_integralImEET_N3gdb10array_viewIKhEE10bfd_endian gdb-16.0.50.20240722/gdb/extract-store-integer.c:112 0x613d21dba1c2 extract_unsigned_integer gdb-16.0.50.20240722/gdb/extract-store-integer.h:45 0x613d21dba1c2 extract_unsigned_integer gdb-16.0.50.20240722/gdb/extract-store-integer.h:52 0x613d21dba1c2 _Z16generic_printstrP7ui_fileP4typePKhjPKciiiPK19value_print_options gdb-16.0.50.20240722/gdb/valprint.c:2562 0x613d2197166b _ZNK13language_defn8printstrEP7ui_fileP4typePKhjPKciPK19value_print_options gdb-16.0.50.20240722/gdb/c-lang.c:220 0x613d2197ab79 c_value_print_array gdb-16.0.50.20240722/gdb/c-valprint.c:296 0x613d2197ab79 _Z19c_value_print_innerP5valueP7ui_fileiPK19value_print_options gdb-16.0.50.20240722/gdb/c-valprint.c:431 0x613d21db6f14 _Z16common_val_printP5valueP7ui_fileiPK19value_print_optionsPK13language_defn gdb-16.0.50.20240722/gdb/valprint.c:1093 0x613d2197ad63 _Z13c_value_printP5valueP7ui_filePK19value_print_options gdb-16.0.50.20240722/gdb/c-valprint.c:589 0x613d21db70ec _Z11value_printP5valueP7ui_filePK19value_print_options gdb-16.0.50.20240722/gdb/valprint.c:1204 0x613d21bd59b8 _Z11print_valueP5valueRK19value_print_options gdb-16.0.50.20240722/gdb/printcmd.c:1265 0x613d21bd5c89 print_command_1 gdb-16.0.50.20240722/gdb/printcmd.c:1381 0x613d2198ae34 _Z8cmd_funcP16cmd_list_elementPKci cli/cli-decode.c:2741 0x613d21d2c8d8 _Z15execute_commandPKci gdb-16.0.50.20240722/gdb/top.c:570 0x613d21a79fa7 _Z15command_handlerPKc gdb-16.0.50.20240722/gdb/event-top.c:580 0x613d21a7b98c _Z20command_line_handlerOSt10unique_ptrIcN3gdb13xfree_deleterIcEEE gdb-16.0.50.20240722/gdb/event-top.c:816 0x613d21a7a9fc gdb_rl_callback_handler gdb-16.0.50.20240722/gdb/event-top.c:272 0x613d21defda4 rl_callback_read_char gdb-16.0.50.20240722/readline/readline/callback.c:290 0x613d21a7ab5d gdb_rl_callback_read_char_wrapper_noexcept gdb-16.0.50.20240722/gdb/event-top.c:197 0x613d21a7ad0a gdb_rl_callback_read_char_wrapper gdb-16.0.50.20240722/gdb/event-top.c:236 0x613d21d6cbdf stdin_event_handler gdb-16.0.50.20240722/gdb/ui.c:154 0x613d21f041fd gdb_wait_for_event gdb-16.0.50.20240722/gdbsupport/event-loop.cc:694 0x613d21f04f21 _Z16gdb_do_one_eventi gdb-16.0.50.20240722/gdbsupport/event-loop.cc:263 0x613d21b75839 start_event_loop gdb-16.0.50.20240722/gdb/main.c:401 0x613d21b75839 captured_command_loop gdb-16.0.50.20240722/gdb/main.c:465 0x613d21b787b4 captured_main gdb-16.0.50.20240722/gdb/main.c:1338 0x613d21b787b4 _Z8gdb_mainP18captured_main_args gdb-16.0.50.20240722/gdb/main.c:1357 0x613d2189ba75 main gdb-16.0.50.20240722/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/>. ```
I've managed to reproduce this with trunk gdb. With system gdb 13.2, I get this: ... $ gdb --batch -ex "b test.d:4" -ex "run" -ex "p data" ./test Breakpoint 1 at 0x438839: file ./test.d, line 4. [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Breakpoint 1, _Dmain () at ./test.d:4 4 return 0; $1 = <error reading variable: value requires 1000000 bytes, which is more than max-value-size> ...
Reproduced with gdb-15-branch and gdb-14-branch, doesn't reproduce with gdb-13-branch.
Bisects to: ... commit a0c07915778486a950952139d27c01d4285b02b4 Author: Andrew Burgess <andrew.burgess@embecosm.com> Date: Fri Feb 10 23:49:19 2023 +0000 GDB: Introduce limited array lengths while printing values ...
This can also be reproduce with C: ``` char a[1000000]; int main() { return a[0]; } ``` No problem with 'print', but 'output' crashes: ``` (gdb) print a $1 = {0 '\000' <repeats 65536 times>, <unavailable> <repeats 934464 times>} (gdb) output a This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. ```
(In reply to Hannes Domani from comment #4) > This can also be reproduce with C: Using this patch: ... diff --git a/gdb/printcmd.c b/gdb/printcmd.c index e37e30edb87..7e6ca7dcf15 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -1449,6 +1449,8 @@ output_command (const char *exp, int from_tty) need to load as many array elements as we plan to print. */ scoped_array_length_limiting limit_large_arrays (opts.print_max); + val->fetch_limited (); + print_formatted (val, fmt.size, &opts, gdb_stdout); annotate_value_end (); diff --git a/gdb/value.c b/gdb/value.c index 09fb19b9bf8..2f600794148 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -1687,13 +1687,8 @@ value::set_component_location (const struct value *whole) } } -/* Access to the value history. */ - -/* Record a new value in the value history. - Returns the absolute history index of the entry. */ - -int -value::record_latest () +void +value::fetch_limited () { /* We don't want this value to have anything to do with the inferior anymore. In particular, "set $1 = 50" should not affect the variable from which @@ -1719,6 +1714,17 @@ value::record_latest () ULONGEST limit = m_limited_length; if (limit != 0) mark_bytes_unavailable (limit, m_enclosing_type->length () - limit); +} + +/* Access to the value history. */ + +/* Record a new value in the value history. + Returns the absolute history index of the entry. */ + +int +value::record_latest () +{ + fetch_limited (); /* Mark the value as recorded in the history for the availability check. */ m_in_history = true; diff --git a/gdb/value.h b/gdb/value.h index 13cfb007aa2..e697a7a9301 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -638,6 +638,8 @@ struct value LONGEST bit_offset, LONGEST bit_length); + void fetch_limited (); + /* Record this value on the value history, and return its location in the history. The value is removed from the value chain. */ int record_latest (); ... I get: ... $ gdb -q -batch a.out -ex "print a" -ex "output a" $1 = {0 '\000' <repeats 65536 times>, <unavailable> <repeats 934464 times>} {0 '\000' <repeats 65536 times>, <unavailable> <repeats 934464 times>}$ ...
Aha, and this bit: ... /* We know that this is a _huge_ array, any attempt to fetch this is going to cause GDB to throw an error. However, to allow the array to still be displayed we fetch its contents up to `max_value_size' and mark anything beyond "unavailable" in the history. */ if (m_type->code () == TYPE_CODE_ARRAY && m_type->length () > max_value_size && array_length_limiting_element_count.has_value () && m_enclosing_type == m_type && calculate_limited_array_length (m_type) <= max_value_size) m_limited_length = max_value_size; ... doesn't trigger for the d example because m_type->code () is not TYPE_CODE_ARRAY but TYPE_CODE_STRUCT, more specifically: ... (gdb) ptype data type = struct _Array_unsigned char { ulong length; ubyte *ptr; } ...
How about something like this: ``` diff --git a/gdb/value.c b/gdb/value.c index 424a1048dd2..bca733b95de 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -1717,10 +1717,6 @@ value::record_latest () fetch_lazy (); } - ULONGEST limit = m_limited_length; - if (limit != 0) - mark_bytes_unavailable (limit, m_enclosing_type->length () - limit); - /* Mark the value as recorded in the history for the availability check. */ m_in_history = true; @@ -3932,6 +3928,11 @@ value::fetch_lazy_memory () if (len > 0) read_value_memory (this, 0, stack (), addr, contents_all_raw ().data (), len); + + /* If only part of an array was loaded, mark the rest as unavailable. */ + if (m_limited_length > 0) + mark_bytes_unavailable (m_limited_length, + m_enclosing_type->length () - m_limited_length); } /* See value.h. */ ``` This would make sure that the contents always match the unavailable bytes, and not only when being 'output' or added to the value history.
(In reply to Hannes Domani from comment #7) > How about something like this: It works for both the c and d example, and doesn't cause regressions on x86_64-linux, so LGTM. Thanks for working on this, and please submit this fix.
(In reply to Tom de Vries from comment #8) > It works for both the c and d example, and doesn't cause regressions on > x86_64-linux, so LGTM. Thanks for working on this, and please submit this > fix. Done: https://sourceware.org/pipermail/gdb-patches/2024-August/210857.html
The master branch has been updated by Hannes Domani <ssbssa@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=8fdd2b2bcd8117cafcc6ef976e45f0d9f95fb528 commit 8fdd2b2bcd8117cafcc6ef976e45f0d9f95fb528 Author: Hannes Domani <ssbssa@yahoo.de> Date: Tue Aug 6 19:34:18 2024 +0200 Mark unavailable bytes of limited-length arrays when allocating contents Using 'output' to print arrays larger than max-value-size, with only repeating elements, can cause gdb to crash: ``` $ cat a.c: char a[1000000]; int main() { return a[0]; } $ gdb -q a (gdb) print a $1 = {0 '\000' <repeats 65536 times>, <unavailable> <repeats 934464 times>} (gdb) output a This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. ``` Using 'print' works, because value::record_latest sets the unavailable bytes of the value when it's added to the value history. But 'outout' doesn't do that, so the printing tries to access more bytes than are available. The original problem in PR32015 was about using 'print' of a dynamic array in a D program. Here the crash happens because for 'print' the value was a struct with length/ptr fields, which is converted in d-valprint.c into an array. So value::record_latest didn't have a chance to mark the unavailable bytes in this case. To make sure the unavailable bytes always match the contents, this fixes it by marking the unavailable bytes immediately after the contents are allocated. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32015 Reviewed-By: Alexandra Petlanova Hajkova <ahajkova@redhat.com> Approved-By: Andrew Burgess <aburgess@redhat.com>
Fixed.
The gdb-15-branch branch has been updated by Tom de Vries <vries@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b82263060348ed854c03806675de34f70562044b commit b82263060348ed854c03806675de34f70562044b Author: Hannes Domani <ssbssa@yahoo.de> Date: Wed Aug 7 15:05:06 2024 +0200 Mark unavailable bytes of limited-length arrays when allocating contents Using 'output' to print arrays larger than max-value-size, with only repeating elements, can cause gdb to crash: ``` $ cat a.c: char a[1000000]; int main() { return a[0]; } $ gdb -q a (gdb) print a $1 = {0 '\000' <repeats 65536 times>, <unavailable> <repeats 934464 times>} (gdb) output a This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information. ``` Using 'print' works, because value::record_latest sets the unavailable bytes of the value when it's added to the value history. But 'outout' doesn't do that, so the printing tries to access more bytes than are available. The original problem in PR32015 was about using 'print' of a dynamic array in a D program. Here the crash happens because for 'print' the value was a struct with length/ptr fields, which is converted in d-valprint.c into an array. So value::record_latest didn't have a chance to mark the unavailable bytes in this case. To make sure the unavailable bytes always match the contents, this fixes it by marking the unavailable bytes immediately after the contents are allocated. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32015 Reviewed-By: Alexandra Petlanova Hajkova <ahajkova@redhat.com> Approved-By: Andrew Burgess <aburgess@redhat.com> (cherry picked from commit 8fdd2b2bcd8117cafcc6ef976e45f0d9f95fb528)