[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