[Toybox] mkpasswd crashes in github ci
Rob Landley
rob at landley.net
Tue Jun 24 16:13:51 PDT 2025
On 6/24/25 17:37, Rob Landley wrote:
> Ok, just to be PEDANTIC I'll add the stupid consts to my function it's
> redirecting to (that CAN'T be it, ELF doesn't annotate at that level.)
> Yup, made no difference.
>
> Did ASAN special case the name "crypt" somehow? This seems unlikely...
Can I strip down a smaller reproduction test case? Hmmm,
$ gcc -lcrypt -fsanitize=address -O1 -g -fno-omit-frame-pointer
-fno-optimize-sibling-calls -static-libasan -Wl,--gc-sections
-Wl,--as-needed -ffunction-sections -fdata-sections
-fno-asynchronous-unwind-tables -fno-strict-aliasing -xc -
<<<$'#include<crypt.h>\nint main(void){crypt("one", "two");}' &&
ASAN_OPTIONS=detect_leaks=0 ./a.out
Produces a 1.4 megabyte binary, which runs with no complaint. (Please
don't tell me this is one of those things that cares about the order of
options on the command line...)
Ok, clean checkout, "ASAN=1 V=1 make clean test_mkpasswd" reproduces it,
and... wait, what? One of the V=1 lines normally silent output is:
/usr/bin/ld: cannot find -lcrypto: No such file or directory
The compile time probe didn't find -lcrypto? It's
/usr/lib/x86_64-linux-gnu/libcrypto.so.3 and the other one found it. I'm
not telling it NOT to search the usual paths. The glibc guys are the
ones who moved it to an external library! How did it LINK if it couldn't
FIND it?
$ gcc -xc - <<<$'#include <crypt.h>\nint main(void){crypt("a","b");}'
/usr/bin/ld: /tmp/cclzm7N3.o: in function `main':
<stdin>:(.text+0x19): undefined reference to `crypt'
collect2: error: ld returned 1 exit status
It doesn't! The glibc guys moved it to an external library! That's the
first thing I hit building that big long... wait.
If I remove "-lcrypt" from the big long test, it compiles and the binary
reproduces the failure. I have no idea WHY it links with a MISSING
SYMBOL, but it does, because ASAN, somehow.
Ok, why didn't the compile time probe find this? Ah, it did. The probe
list has both "crypt" and "crypto" in it (that was a mac or bsd thing,
wasn't it)?
BUT, I reproduced the issue! If you link without -lcrypt but with asan,
the build succeeds and the resulting binary fails. And the mkpasswd
build is doing:
cc -Wall -Wundef -Werror=implicit-function-declaration
-Wno-char-subscripts -Wno-pointer-sign -funsigned-char -Wno-restrict
-Wno-format-overflow -fsanitize=address -O1 -g -fno-omit-frame-pointer
-fno-optimize-sibling-calls -static-libasan -Wno-restrict
-Wno-format-overflow -I . -Os -ffunction-sections -fdata-sections
-fno-asynchronous-unwind-tables -fno-strict-aliasing
-DTOYBOX_VERSION="0.8.12-62-g70157e71bb2c" generated/obj/lib_args.o
generated/obj/lib_commas.o generated/obj/lib_deflate.o
generated/obj/lib_dirtree.o generated/obj/lib_elf.o
generated/obj/lib_env.o generated/obj/lib_hash.o generated/obj/lib_lib.o
generated/obj/lib_llist.o generated/obj/lib_net.o
generated/obj/lib_password.o generated/obj/lib_portability.o
generated/obj/lib_tty.o generated/obj/lib_utf8.o
generated/obj/lib_xwrap.o generated/obj/main.o generated/obj/mkpasswd.o
-Wl,--gc-sections -Wl,--as-needed -lcrypt -lm -lresolv -lselinux -lutil
-lz -o generated/unstripped/mkpasswd
chmod 555 mkpasswd
Which has -lcrypt right there as the first symbol after --as-needed. So
let's reorder the symbols:
$ gcc -fsanitize=address -O1 -g -fno-omit-frame-pointer
-fno-optimize-sibling-calls -static-libasan -Wl,--gc-sections
-ffunction-sections -fdata-sections -fno-asynchronous-unwind-tables
-fno-strict-aliasing -xc - -Wl,--as-needed -lcrypt
<<<$'#include<crypt.h>\nint main(void){crypt("one", "two");}' &&
ASAN_OPTIONS=detect_leaks=0 ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==32558==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
(pc 0x000000000000 bp 0x7ffd12d94be0 sp 0x7ffd12d94bd8 T0)
==32558==Hint: pc points to the zero page.
==32558==The signal is caused by a READ memory access.
==32558==Hint: address points to the zero page.
#0 0x0 (<unknown module>)
#1 0x7fe4159fa249 (/lib/x86_64-linux-gnu/libc.so.6+0x27249)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (<unknown module>)
==32558==ABORTING
So it's grabbing -lcrypt, using that to bump the weak symbol, and then
when the library is --as-needed it's --gc-symbol-ing it away again,
despite that succeeding to link the binary, and null pointer
dereferencing when you try to run it. But only when ASAN is enabled.
I am so very tired. And have no idea what to do with that information.
Yes I _could_ try to engage with gcc, but I've been subscribed to the
coreutils mailing list for something over 3 years now, which has gone
https://lists.gnu.org/archive/html/coreutils/2022-01/msg00007.html to
https://lists.gnu.org/archive/html/coreutils/2023-08/msg00009.html and
https://lists.gnu.org/archive/html/coreutils/2023-08/msg00100.html
and... Sigh. I mean, glass houses, but still.
Rob
P.S. I should probably go back to dumping these big long rants into my
blog and not here. I should edit and post my last month+ of blog entries...
More information about the Toybox
mailing list