Dear Maintainer, I tried to debug this issue. (gdb) bt #0 tidy_path (path=0x55f0c81ea140 <abspath> "/../") at symlinks.c:96 #1 0x000055f0c7fe6908 in fix_symlink (my_dev=2049, path=0x55f0c81ec1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:185 #2 0x000055f0c7fe6e81 in dirwalk (pathlen=<optimized out>, dev=2049, path=0x55f0c81ec1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:290 #3 0x000055f0c7fe5f1d in main (argc=<optimized out>, argv=0x7ffd5d3596f0) at symlinks.c:376
I guess I found a buffer underrun in following lines: 76 static int tidy_path (char *path) ... 91 while ((p = strstr(path,"/../")) != NULL) { 92 s = p+3; 93 for (p--; p != path; p--) if (*p == '/') break; In line 93 p equals path already but is decremented before the condition 'p != path' is checked the first time. Therefore this loop is just ended when the first '/' is found before the begin of the path variable, which is "luckily" in our case in the .rodata section of the executable, to that we want to write in line 96. This modification makes symlinks not crash and shows plausible output: - for (p--; p != path; p--) if (*p == '/') break; + for (p--; p > path; p--) if (*p == '/') break; Kind regards, Bernhard
# Buster/stable amd64 qemu VM 2020-02-16 apt update apt dist-upgrade apt install systemd-coredump mc fakeroot symlinks gdb symlinks-dbgsym apt build-dep symlinks mkdir /home/benutzer/source/symlinks/orig -p cd /home/benutzer/source/symlinks/orig apt source symlinks cd cat <<EOF > reproduce.sh #! /usr/bin/env sh MYTMP=\$(mktemp -d) ln -s /.. \$MYTMP/mylink symlinks \$MYTMP EOF chmod +x reproduce.sh ./reproduce.sh journalctl --no-pager coredumpctl list coredumpctl gdb 1443 set width 0 set pagination off directory /home/benutzer/source/symlinks/orig/symlinks-1.4 bt ########### ########### ########### benutzer@debian:~$ ./reproduce.sh Segmentation fault (core dumped) root@debian:~# journalctl --no-pager ... Feb 17 23:49:17 debian kernel: symlinks[1443]: segfault at 55f0c81e70ed ip 000055f0c7fe675a sp 00007ffd5d359360 error 7 in symlinks[55f0c7fe5000+3000] Feb 17 23:49:17 debian kernel: Code: 1d 0f 1f 80 00 00 00 00 80 3a 2f 74 11 48 83 ea 01 48 39 d5 75 f2 48 89 ea 80 3a 2f 75 29 31 c9 0f b6 74 08 03 bb 01 00 00 00 <40> 88 34 0a 48 83 c1 01 40 84 f6 75 e9 4c 89 e6 48 89 ef e8 0e f6 Feb 17 23:49:17 debian systemd[1]: Created slice system-systemd\x2dcoredump.slice. Feb 17 23:49:17 debian systemd[1]: Started Process Core Dump (PID 1444/UID 0). Feb 17 23:49:17 debian systemd-coredump[1445]: Process 1443 (symlinks) of user 1000 dumped core. Stack trace of thread 1443: #0 0x000055f0c7fe675a n/a (symlinks) #1 0x000055f0c7fe6908 n/a (symlinks) #2 0x000055f0c7fe6e81 n/a (symlinks) #3 0x000055f0c7fe5f1d n/a (symlinks) #4 0x00007fea02a9f09b __libc_start_main (libc.so.6) #5 0x000055f0c7fe61da n/a (symlinks) Feb 17 23:49:17 debian systemd[1]: systemd-coredump@0-1444-0.service: Succeeded. https://wiki.debian.org/InterpretingKernelOutputAtProcessCrash error 7 == 0b111 * bit 0 == 1: protection fault * bit 1 == 1: write access * bit 2 == 1: user-mode access root@debian:~# coredumpctl list TIME PID UID GID SIG COREFILE EXE Mon 2020-02-17 23:49:17 CET 1443 1000 1000 11 present /usr/bin/symlinks root@debian:~# coredumpctl gdb 1443 PID: 1443 (symlinks) UID: 1000 (benutzer) GID: 1000 (benutzer) Signal: 11 (SEGV) Timestamp: Mon 2020-02-17 23:49:17 CET (1min 25s ago) Command Line: symlinks /tmp/tmp.tlVVwIgOZQ Executable: /usr/bin/symlinks Control Group: /user.slice/user-1000.slice/session-3.scope Unit: session-3.scope Slice: user-1000.slice Session: 3 Owner UID: 1000 (benutzer) Boot ID: 2d5d5576a57947a7aad042da5f907289 Machine ID: 33f18f39d2a9438eb75b0ed52848afcd Hostname: debian Storage: /var/lib/systemd/coredump/core.symlinks.1000.2d5d5576a57947a7aad042da5f907289.1443.1581979757000000.lz4 Message: Process 1443 (symlinks) of user 1000 dumped core. Stack trace of thread 1443: #0 0x000055f0c7fe675a n/a (symlinks) #1 0x000055f0c7fe6908 n/a (symlinks) #2 0x000055f0c7fe6e81 n/a (symlinks) #3 0x000055f0c7fe5f1d n/a (symlinks) #4 0x00007fea02a9f09b __libc_start_main (libc.so.6) #5 0x000055f0c7fe61da n/a (symlinks) GNU gdb (Debian 8.2.1-2+b3) 8.2.1 Copyright (C) 2018 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-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://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 /usr/bin/symlinks...(no debugging symbols found)...done. [New LWP 1443] Core was generated by `symlinks /tmp/tmp.tlVVwIgOZQ'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000055f0c7fe675a in ?? () (gdb) set width 0 (gdb) set pagination off (gdb) bt #0 0x000055f0c7fe675a in ?? () #1 0x000055f0c7fe6908 in ?? () #2 0x000055f0c7fe6e81 in ?? () #3 0x000055f0c7fe5f1d in ?? () #4 0x00007fea02a9f09b in __libc_start_main (main=0x55f0c7fe5da0, argc=2, argv=0x7ffd5d3596e8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffd5d3596d8) at ../csu/libc-start.c:308 #5 0x000055f0c7fe61da in ?? () Core was generated by `symlinks /tmp/tmp.tlVVwIgOZQ'. Program terminated with signal SIGSEGV, Segmentation fault. #0 tidy_path (path=0x55f0c81ea140 <abspath> "/../") at symlinks.c:96 96 symlinks.c: Datei oder Verzeichnis nicht gefunden. (gdb) set width 0 (gdb) set pagination off (gdb) directory /home/benutzer/source/symlinks/orig/symlinks-1.4 Source directories searched: /home/benutzer/source/symlinks/orig/symlinks-1.4:$cdir:$cwd (gdb) bt #0 tidy_path (path=0x55f0c81ea140 <abspath> "/../") at symlinks.c:96 #1 0x000055f0c7fe6908 in fix_symlink (my_dev=2049, path=0x55f0c81ec1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:185 #2 0x000055f0c7fe6e81 in dirwalk (pathlen=<optimized out>, dev=2049, path=0x55f0c81ec1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:290 #3 0x000055f0c7fe5f1d in main (argc=<optimized out>, argv=0x7ffd5d3596f0) at symlinks.c:376 (gdb) list symlinks.c:96 warning: Source file is more recent than executable. 91 while ((p = strstr(path,"/../")) != NULL) { 92 s = p+3; 93 for (p--; p != path; p--) if (*p == '/') break; 94 if (*p != '/') 95 break; 96 while ((*p++ = *s++)); 97 tidied = 1; 98 } 99 if (*path == '\0') 100 strcpy(path,"/"); (gdb) bt full #0 tidy_path (path=0x55f0c81ea140 <abspath> "/../") at symlinks.c:96 tidied = <optimized out> s = 0x55f0c81ea144 <abspath+4> "" p = 0x55f0c81e70ee "" #1 0x000055f0c7fe6908 in fix_symlink (my_dev=2049, path=0x55f0c81ec1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:185 p = <optimized out> np = <optimized out> lp = <optimized out> tail = <optimized out> msg = <optimized out> stbuf = {st_dev = 18446744073709551615, st_ino = 18446744073709551615, st_nlink = 18446744073709551615, st_mode = 4294967295, st_uid = 4294967295, st_gid = 0, __pad0 = 0, st_rdev = 0, st_size = 0, st_blksize = 0, st_blocks = 0, st_atim = {tv_sec = 0, tv_nsec = 0}, st_mtim = {tv_sec = 0, tv_nsec = 0}, st_ctim = {tv_sec = 0, tv_nsec = 0}, __glibc_reserved = {0, 0, 0}} lstbuf = {st_dev = 0, st_ino = 0, st_nlink = 0, st_mode = 0, st_uid = 0, st_gid = 0, __pad0 = 0, st_rdev = 0, st_size = 0, st_blksize = 0, st_blocks = 0, st_atim = {tv_sec = 0, tv_nsec = 8}, st_mtim = {tv_sec = 1581979757, tv_nsec = 336592469}, st_ctim = {tv_sec = 1581979757, tv_nsec = 336592469}, __glibc_reserved = {1581979757, 336592469, 0}} c = <optimized out> fix_abs = 0 fix_messy = 0 fix_long = 0 lpath = "/..", '\000' <repeats 4092 times> new = '\000' <repeats 4095 times> abspath = "/../", '\000' <repeats 4091 times> #2 0x000055f0c7fe6e81 in dirwalk (pathlen=<optimized out>, dev=2049, path=0x55f0c81ec1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:290 name = 0x55f0c81ec1f4 <path+20> "mylink" dfd = 0x55f0c9f4f280 st = {st_dev = 2049, st_ino = 131382, st_nlink = 1, st_mode = 41471, st_uid = 1000, st_gid = 1000, __pad0 = 0, st_rdev = 0, st_size = 3, st_blksize = 4096, st_blocks = 0, st_atim = {tv_sec = 1581979757, tv_nsec = 336592469}, st_mtim = {tv_sec = 1581979757, tv_nsec = 336592469}, st_ctim = {tv_sec = 1581979757, tv_nsec = 336592469}, __glibc_reserved = {0, 0, 0}} dp = <optimized out> __s1_len = <optimized out> __s2_len = <optimized out> __s2 = <optimized out> __result = <optimized out> __s1_len = <optimized out> __s2_len = <optimized out> __s2 = <optimized out> __result = <optimized out> #3 0x000055f0c7fe5f1d in main (argc=<optimized out>, argv=0x7ffd5d3596f0) at symlinks.c:376 st = {st_dev = 2049, st_ino = 131381, st_nlink = 2, st_mode = 16832, st_uid = 1000, st_gid = 1000, __pad0 = 0, st_rdev = 0, st_size = 4096, st_blksize = 4096, st_blocks = 8, st_atim = {tv_sec = 1581979757, tv_nsec = 336592469}, st_mtim = {tv_sec = 1581979757, tv_nsec = 336592469}, st_ctim = {tv_sec = 1581979757, tv_nsec = 336592469}, __glibc_reserved = {0, 0, 0}} path = "/tmp/tmp.tlVVwIgOZQ/mylink", '\000' <repeats 4071 times> cwd = <optimized out> dircount = 0 c = <optimized out> p = 0x7ffd5d35a84f "/tmp/tmp.tlVVwIgOZQ" (gdb) (gdb) disassemble $pc-41,$pc+20 Dump of assembler code from 0x55f0c7fe6731 to 0x55f0c7fe676e: 0x000055f0c7fe6731 <tidy_path+145>: nopl 0x0(%rax) 0x000055f0c7fe6738 <tidy_path+152>: cmpb $0x2f,(%rdx) 0x000055f0c7fe673b <tidy_path+155>: je 0x55f0c7fe674e <tidy_path+174> 0x000055f0c7fe673d <tidy_path+157>: sub $0x1,%rdx 0x000055f0c7fe6741 <tidy_path+161>: cmp %rdx,%rbp 0x000055f0c7fe6744 <tidy_path+164>: jne 0x55f0c7fe6738 <tidy_path+152> 0x000055f0c7fe6746 <tidy_path+166>: mov %rbp,%rdx 0x000055f0c7fe6749 <tidy_path+169>: cmpb $0x2f,(%rdx) 0x000055f0c7fe674c <tidy_path+172>: jne 0x55f0c7fe6777 <tidy_path+215> 0x000055f0c7fe674e <tidy_path+174>: xor %ecx,%ecx 0x000055f0c7fe6750 <tidy_path+176>: movzbl 0x3(%rax,%rcx,1),%esi 0x000055f0c7fe6755 <tidy_path+181>: mov $0x1,%ebx => 0x000055f0c7fe675a <tidy_path+186>: mov %sil,(%rdx,%rcx,1) 0x000055f0c7fe675e <tidy_path+190>: add $0x1,%rcx 0x000055f0c7fe6762 <tidy_path+194>: test %sil,%sil 0x000055f0c7fe6765 <tidy_path+197>: jne 0x55f0c7fe6750 <tidy_path+176> 0x000055f0c7fe6767 <tidy_path+199>: mov %r12,%rsi 0x000055f0c7fe676a <tidy_path+202>: mov %rbp,%rdi 0x000055f0c7fe676d <tidy_path+205>: callq 0x55f0c7fe5d80 <strstr@plt> End of assembler dump. (gdb) print/x $sil $1 = 0x2f (gdb) print/x $rdx $2 = 0x55f0c81e70ed (gdb) print/x $rcx $3 = 0x0 (gdb) info target Symbols from "/usr/bin/symlinks". Local core dump file: `/var/tmp/coredump-LWP7Ur', file type elf64-x86-64. ... 0x000055f0c81e7000 - 0x000055f0c81e8000 is load2 ???? ########### ########### ########### gdb -q set width 0 set pagination off directory /home/benutzer/source/symlinks/orig/symlinks-1.4 file /usr/bin/symlinks run /tmp/tmp.tlVVwIgOZQ disassemble $pc-41,$pc+20 print/x $rdx info target benutzer@debian:~$ gdb -q (gdb) set width 0 (gdb) set pagination off (gdb) directory /home/benutzer/source/symlinks/orig/symlinks-1.4 Source directories searched: /home/benutzer/source/symlinks/orig/symlinks-1.4:$cdir:$cwd (gdb) file /usr/bin/symlinks Reading symbols from /usr/bin/symlinks...Reading symbols from /usr/lib/debug/.build-id/39/62acae57f4d516661920488e2cf5b3d856ade1.debug...done. done. (gdb) run /tmp/tmp.tlVVwIgOZQ Starting program: /usr/bin/symlinks /tmp/tmp.tlVVwIgOZQ Program received signal SIGSEGV, Segmentation fault. tidy_path (path=0x555555759140 <abspath> "/../") at symlinks.c:96 warning: Source file is more recent than executable. 96 while ((*p++ = *s++)); (gdb) bt full 2 #0 tidy_path (path=0x555555759140 <abspath> "/../") at symlinks.c:96 tidied = <optimized out> s = 0x555555759144 <abspath+4> "" p = 0x5555557560ee "" #1 0x0000555555555908 in fix_symlink (my_dev=2049, path=0x55555575b1e0 <path> "/tmp/tmp.tlVVwIgOZQ/mylink") at symlinks.c:185 p = <optimized out> np = <optimized out> lp = <optimized out> tail = <optimized out> msg = <optimized out> stbuf = {st_dev = 18446744073709551615, st_ino = 18446744073709551615, st_nlink = 18446744073709551615, st_mode = 4294967295, st_uid = 4294967295, st_gid = 0, __pad0 = 0, st_rdev = 0, st_size = 280375465082880, st_blksize = 0, st_blocks = 0, st_atim = {tv_sec = 0, tv_nsec = 0}, st_mtim = {tv_sec = 0, tv_nsec = 0}, st_ctim = {tv_sec = 0, tv_nsec = 0}, __glibc_reserved = {0, 0, 0}} lstbuf = {st_dev = 0, st_ino = 0, st_nlink = 0, st_mode = 0, st_uid = 0, st_gid = 0, __pad0 = 0, st_rdev = 0, st_size = 0, st_blksize = 0, st_blocks = 0, st_atim = {tv_sec = 0, tv_nsec = 8}, st_mtim = {tv_sec = 1581980596, tv_nsec = 801775324}, st_ctim = {tv_sec = 1581979757, tv_nsec = 336592469}, __glibc_reserved = {1581979757, 336592469, 0}} c = <optimized out> fix_abs = 0 fix_messy = 0 fix_long = 0 lpath = "/..", '\000' <repeats 4092 times> new = '\000' <repeats 4095 times> abspath = "/../", '\000' <repeats 4091 times> (More stack frames follow...) (gdb) disassemble $pc-41,$pc+20 Dump of assembler code from 0x555555555731 to 0x55555555576e: 0x0000555555555731 <tidy_path+145>: nopl 0x0(%rax) 0x0000555555555738 <tidy_path+152>: cmpb $0x2f,(%rdx) 0x000055555555573b <tidy_path+155>: je 0x55555555574e <tidy_path+174> 0x000055555555573d <tidy_path+157>: sub $0x1,%rdx 0x0000555555555741 <tidy_path+161>: cmp %rdx,%rbp 0x0000555555555744 <tidy_path+164>: jne 0x555555555738 <tidy_path+152> 0x0000555555555746 <tidy_path+166>: mov %rbp,%rdx 0x0000555555555749 <tidy_path+169>: cmpb $0x2f,(%rdx) 0x000055555555574c <tidy_path+172>: jne 0x555555555777 <tidy_path+215> 0x000055555555574e <tidy_path+174>: xor %ecx,%ecx 0x0000555555555750 <tidy_path+176>: movzbl 0x3(%rax,%rcx,1),%esi 0x0000555555555755 <tidy_path+181>: mov $0x1,%ebx => 0x000055555555575a <tidy_path+186>: mov %sil,(%rdx,%rcx,1) 0x000055555555575e <tidy_path+190>: add $0x1,%rcx 0x0000555555555762 <tidy_path+194>: test %sil,%sil 0x0000555555555765 <tidy_path+197>: jne 0x555555555750 <tidy_path+176> 0x0000555555555767 <tidy_path+199>: mov %r12,%rsi 0x000055555555576a <tidy_path+202>: mov %rbp,%rdi 0x000055555555576d <tidy_path+205>: callq 0x555555554d80 <strstr@plt> End of assembler dump. (gdb) print/x $rdx $1 = 0x5555557560ed (gdb) info target Symbols from "/usr/bin/symlinks". Native process: Using the running image of child process 4909. While running this, GDB does not access memory from... Local exec file: `/usr/bin/symlinks', file type elf64-x86-64. Entry point: 0x5555555551b0 ... 0x0000555555555f20 - 0x000055555555617c is .rodata ... --> We are not allowed to write to .rodata (gdb) list 76,120 76 static int tidy_path (char *path) 77 { 78 int tidied = 0; 79 char *s, *p; 80 81 s = path + strlen(path) - 1; 82 if (s[0] != '/') { /* tmp trailing slash simplifies things */ 83 s[1] = '/'; 84 s[2] = '\0'; 85 } 86 while (substr(path, "/./", "/")) 87 tidied = 1; 88 while (substr(path, "//", "/")) 89 tidied = 1; 90 91 while ((p = strstr(path,"/../")) != NULL) { 92 s = p+3; 93 for (p--; p != path; p--) if (*p == '/') break; <<<<<<<<< p is decremented even if p == path, therefore we "search" a '/' before path begins 94 if (*p != '/') 95 break; 96 while ((*p++ = *s++)); 97 tidied = 1; 98 } 99 if (*path == '\0') 100 strcpy(path,"/"); 101 p = path + strlen(path) - 1; 102 if (p != path && *p == '/') 103 *p-- = '\0'; /* remove tmp trailing slash */ 104 while (p != path && *p == '/') { /* remove any others */ 105 *p-- = '\0'; 106 tidied = 1; 107 } 108 while (!strncmp(path,"./",2)) { 109 for (p = path, s = path+2; (*p++ = *s++);); 110 tidied = 1; 111 } 112 return tidied; 113 } gdb -q set width 0 set pagination off directory /home/benutzer/source/symlinks/orig/symlinks-1.4 file /usr/bin/symlinks b tidy_path run /tmp/tmp.tlVVwIgOZQ display/i $pc display p display path cont record cont reverse-next ######### ######### ######### --- symlinks-1.4.orig/symlinks.c +++ symlinks-1.4/symlinks.c @@ -90,7 +90,7 @@ static int tidy_path (char *path) while ((p = strstr(path,"/../")) != NULL) { s = p+3; - for (p--; p != path; p--) if (*p == '/') break; + for (p--; p > path; p--) if (*p == '/') break; if (*p != '/') break; while ((*p++ = *s++)); benutzer@debian:~$ symlinks /tmp/tmp.tlVVwIgOZQ absolute: /tmp/tmp.tlVVwIgOZQ/mylink -> /..