From 310b3441a07cb07713a8065a39032504e098047b Mon Sep 17 00:00:00 2001 From: Tom de Vries Date: Mon, 17 Jun 2019 22:25:06 +0200 Subject: [PATCH] [gdb] Fix heap-buffer-overflow in child_path When compiling gdb with '-lasan -fsanitizer=address' and running tests with: - export ASAN_OPTIONS="detect_leaks=0:alloc_dealloc_mismatch=0", and - a target board using local-board.exp, which sets sysroot to "" we run into a heap-buffer-overflow in child_path for f.i. gdb.arch/amd64-byte: ... ==3997==ERROR: AddressSanitizer: heap-buffer-overflow on address \ 0x60200002abcf at pc 0x5602acdf6872 bp 0x7ffe5237a090 sp 0x7ffe5237a080 READ of size 1 at 0x60200002abcf thread T0 #0 0x5602acdf6871 in child_path(char const*, char const*) \ gdb/common/pathstuff.c:161 #1 0x5602adb06587 in find_separate_debug_file gdb/symfile.c:1483 #2 0x5602adb06f2f in find_separate_debug_file_by_debuglink[abi:cxx11](...) \ gdb/symfile.c:1563 #3 0x5602ad13b743 in elf_symfile_read gdb/elfread.c:1293 #4 0x5602adb01cfa in read_symbols gdb/symfile.c:798 #5 0x5602adb03769 in syms_from_objfile_1 gdb/symfile.c:1000 #6 0x5602adb039d0 in syms_from_objfile gdb/symfile.c:1017 #7 0x5602adb04551 in symbol_file_add_with_addrs gdb/symfile.c:1124 #8 0x5602adb04ebf in symbol_file_add_from_bfd(...) gdb/symfile.c:1204 #9 0x5602ada5a78d in solib_read_symbols(...) gdb/solib.c:695 #10 0x5602ada5bdae in solib_add(char const*, int, int) gdb/solib.c:1004 #11 0x5602ada49bcd in enable_break gdb/solib-svr4.c:2394 #12 0x5602ada4dae9 in svr4_solib_create_inferior_hook gdb/solib-svr4.c:3028 #13 0x5602ada5d4f1 in solib_create_inferior_hook(int) gdb/solib.c:1215 #14 0x5602ad347f66 in post_create_inferior(target_ops*, int) \ gdb/infcmd.c:467 #15 0x5602ad348b3c in run_command_1 gdb/infcmd.c:663 #16 0x5602ad348e55 in run_command gdb/infcmd.c:686 #17 0x5602acd7d32b in do_const_cfunc gdb/cli/cli-decode.c:106 #18 0x5602acd84bfe in cmd_func(cmd_list_element*, char const*, int) \ gdb/cli/cli-decode.c:1892 #19 0x5602adc62a90 in execute_command(char const*, int) gdb/top.c:630 #20 0x5602ad5053e6 in catch_command_errors gdb/main.c:372 #21 0x5602ad507eb1 in captured_main_1 gdb/main.c:1138 #22 0x5602ad5081ec in captured_main gdb/main.c:1163 #23 0x5602ad508281 in gdb_main(captured_main_args*) gdb/main.c:1188 #24 0x5602ac9ddc3a in main gdb/gdb.c:32 #25 0x7f582b56eb96 in __libc_start_main \ (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) #26 0x5602ac9dda09 in _start \ (/home/smarchi/build/binutils-gdb/gdb/gdb+0x19a2a09) 0x60200002abcf is located 1 bytes to the left of 1-byte region \ [0x60200002abd0,0x60200002abd1) allocated by thread T0 here: #0 0x7f582e0e4b50 in __interceptor_malloc \ (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50) #1 0x5602acdd3656 in xmalloc gdb/common/common-utils.c:44 #2 0x5602aefe17d1 in xstrdup libiberty/xstrdup.c:34 #3 0x5602acdf61f6 in gdb_realpath(char const*) gdb/common/pathstuff.c:80 #4 0x5602adb06278 in find_separate_debug_file gdb/symfile.c:1444 #5 0x5602adb06f2f in find_separate_debug_file_by_debuglink[abi:cxx11](...) \ gdb/symfile.c:1563 #6 0x5602ad13b743 in elf_symfile_read gdb/elfread.c:1293 #7 0x5602adb01cfa in read_symbols gdb/symfile.c:798 #8 0x5602adb03769 in syms_from_objfile_1 gdb/symfile.c:1000 #9 0x5602adb039d0 in syms_from_objfile gdb/symfile.c:1017 #10 0x5602adb04551 in symbol_file_add_with_addrs gdb/symfile.c:1124 #11 0x5602adb04ebf in symbol_file_add_from_bfd(...) gdb/solib.c:695 #13 0x5602ada5bdae in solib_add(char const*, int, int) gdb/solib.c:1004 #14 0x5602ada49bcd in enable_break gdb/solib-svr4.c:2394 #15 0x5602ada4dae9 in svr4_solib_create_inferior_hook gdb/solib-svr4.c:3028 #16 0x5602ada5d4f1 in solib_create_inferior_hook(int) gdb/solib.c:1215 #17 0x5602ad347f66 in post_create_inferior(target_ops*, int) \ gdb/infcmd.c:467 #18 0x5602ad348b3c in run_command_1 gdb/infcmd.c:663 #19 0x5602ad348e55 in run_command gdb/infcmd.c:686 #20 0x5602acd7d32b in do_const_cfunc gdb/cli/cli-decode.c:106 #21 0x5602acd84bfe in cmd_func(cmd_list_element*, char const*, int) \ gdb/cli/cli-decode.c:1892 #22 0x5602adc62a90 in execute_command(char const*, int) gdb/top.c:630 #23 0x5602ad5053e6 in catch_command_errors gdb/main.c:372 #24 0x5602ad507eb1 in captured_main_1 gdb/main.c:1138 #25 0x5602ad5081ec in captured_main gdb/main.c:1163 #26 0x5602ad508281 in gdb_main(captured_main_args*) gdb/main.c:1188 #27 0x5602ac9ddc3a in main gdb/gdb.c:32 #28 0x7f582b56eb96 in __libc_start_main \ (/lib/x86_64-linux-gnu/libc.so.6+0x21b96) SUMMARY: AddressSanitizer: heap-buffer-overflow gdb/common/pathstuff.c:161 \ in child_path(char const*, char const*) Shadow bytes around the buggy address: 0x0c047fffd520: fa fa fd fd fa fa fd fd fa fa fd fa fa fa fd fa 0x0c047fffd530: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa 0x0c047fffd540: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa 0x0c047fffd550: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fa 0x0c047fffd560: fa fa fd fa fa fa fd fa fa fa fd fa fa fa 00 00 =>0x0c047fffd570: fa fa 07 fa fa fa 00 fa fa[fa]01 fa fa fa fa fa 0x0c047fffd580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fffd590: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fffd5a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fffd5b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fffd5c0: 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 ==3997==ABORTING ... The direct cause is that child_path gets called with parent == "", so this test: ... if (IS_DIR_SEPARATOR (parent[parent_len - 1])) ... accesses parent[-1]. [ There is an open discussion (1) about whether an empty sysroot should indeed be represented internally as "". But this patch focuses on fixing the heap-buffer-overflow without any redesign. ] Fix this by guarding the test with 'parent_len > 0'. Note that the fix makes child_path behave the same for: - parent == "/" && child == "/foo" (returns "foo") - parent == "" and child == "/foo" (returns "foo"). Build and reg-tested on x86_64-linux. (1) https://sourceware.org/ml/gdb-patches/2019-05/msg00193.html gdb/ChangeLog: 2019-06-17 Tom de Vries PR gdb/24617 * common/pathstuff.c (child_path): Make sure parent_len > 0 before accessing parent[parent_len - 1]. --- gdb/ChangeLog | 6 ++++++ gdb/common/pathstuff.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 397a08f..22a473a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,9 @@ +2019-06-17 Tom de Vries + + PR gdb/24617 + * common/pathstuff.c (child_path): Make sure parent_len > 0 before + accessing parent[parent_len - 1]. + 2019-06-17 Paul Pluzhnikov PR gdb/24364 diff --git a/gdb/common/pathstuff.c b/gdb/common/pathstuff.c index e0e048d..b295e73 100644 --- a/gdb/common/pathstuff.c +++ b/gdb/common/pathstuff.c @@ -158,7 +158,7 @@ child_path (const char *parent, const char *child) /* The parent path must be a directory and the child must contain at least one component underneath the parent. */ const char *child_component; - if (IS_DIR_SEPARATOR (parent[parent_len - 1])) + if (parent_len > 0 && IS_DIR_SEPARATOR (parent[parent_len - 1])) { /* The parent path ends in a directory separator, so it is a directory. The first child component starts after the common -- 2.7.4