Bugzilla – Bug 1219520
ASAN crypt_r interceptor uses wrong sizeof(struct crypt_data)
Last modified: 2024-02-08 09:25:03 UTC
I tested this on the opensuse/leap:latest docker image (image hash: ec3c3c09f741) so it should be easy to reproduce. I can confirm that it also happens on SLES 15.5. I verified that it does not happen with GCC 13.2.1 from Fedora 38 which suggests it's not a bug in GCC but something in the build environment on SLES 15. The following program demonstrates the problem. >#include <crypt.h> >#include <iostream> >#include <cstring> > >int main(int argc, char** argv) >{ > struct crypt_data data; > memset(&data, 0, sizeof(data)); > std::cout << crypt_r(argv[1], argv[2], &data) << std::endl; > return 0; >} Compiling it with GCC 13 causes the problem. >sudo zypper -n install gcc13 gcc13-c++ >/usr/bin/g++-13 -g -fsanitize=address -lcrypt test.cc >./a.out 'hello' '$1$world' >================================================================= >==59107==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f55f8208020 at pc 0x7f55fa287061 bp 0x7ffc95a4f3d0 sp 0x7ffc95a4eb90 >WRITE of size 131232 at 0x7f55f8208020 thread T0 > #0 0x7f55fa287060 in __interceptor_crypt_r ../../../../libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:10214 > #1 0x400c47 in main /build/test.cc:9 > #2 0x7f55f9c3e24c in __libc_start_main (/lib64/libc.so.6+0x3524c) (BuildId: f732026552f6adff988b338e92d466bc81a01c37) > #3 0x400a49 in _start ../sysdeps/x86_64/start.S:120 >Address 0x7f55f8208020 is located in stack of thread T0 at offset 32800 in frame > #0 0x400b05 in main /build/test.cc:6 > This frame has 1 object(s): > [32, 32800) 'data' (line 7) <== Memory access at offset 32800 overflows this variable >HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork > (longjmp and C++ exceptions *are* supported) >SUMMARY: AddressSanitizer: stack-buffer-overflow ../../../../libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:10214 in __interceptor_crypt_r >Shadow bytes around the buggy address: > 0x7f55f8207d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8207e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8207e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8207f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8207f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >=>0x7f55f8208000: 00 00 00 00[f3]f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 > 0x7f55f8208080: f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 f3 > 0x7f55f8208100: f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8208180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8208200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > 0x7f55f8208280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >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 >==59107==ABORTING Compiling with GCC 9 does not cause it. >sudo zypper -n install gcc9 gcc9-c++ >/usr/bin/g++-9 -g -fsanitize=address -lcrypt test.cc >./a.out 'hello' '$1$world' >$1$world$9570e6rgzRsa0dGF.W93f. On line 10214 in sanitizer_common_interceptors.inc we have this: > COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, > __sanitizer::struct_crypt_data_sz); And struct_crypt_data_sz is defined in sanitizer_platform_limits_posix.cpp. There we see that it's correctly set to the size of the struct: >#if SANITIZER_INTERCEPT_CRYPT_R > unsigned struct_crypt_data_sz = sizeof(struct crypt_data); >#endif From /usr/include/crypt.h: >/* These sizes are chosen to make sizeof (struct crypt_data) add up to > exactly 32768 bytes. */ >#define CRYPT_DATA_RESERVED_SIZE 767 >#define CRYPT_DATA_INTERNAL_SIZE 30720 Here's the disassembly of libasan for the variable where the size of the struct is stored. >0000000000166ce0 <__sanitizer::struct_crypt_data_sz>: > 166ce0: a0 00 02 00 movabs 0x1000000070000200,%al > 166ce7: If I'm reading that correctly, that's 131232 in base 10 which is exactly the size of the write that ASAN reports and not what the crypt.h header uses. Maybe there's something weird about the libasan build environment that makes the size of the struct different? I also tested Valgrind which confirms that it's indeed a problem in ASAN and not in the library itself. >valgrind ./a.out 'hello' '$1$world' >==58481== Memcheck, a memory error detector >==58481== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. >==58481== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info >==58481== Command: ./a.out hello $1$world >==58481== >$1$world$9570e6rgzRsa0dGF.W93f. >==58481== >==58481== HEAP SUMMARY: >==58481== in use at exit: 0 bytes in 0 blocks >==58481== total heap usage: 2 allocs, 2 frees, 74,752 bytes allocated >==58481== >==58481== All heap blocks were freed -- no leaks are possible >==58481== >==58481== For lists of detected and suppressed errors, rerun with: -s >==58481== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Confirmed. I checked that a locally built libasan works fine. It looks like upstream dropped intercepting of crypt_r, so it will be gone with GCC 14. GCC 13 from Leap/SLE build against glibc from SLE15 GA. The crypt.h I have installed on SLE15 SP5 is from libxcrypt which has a smaller size: /* These sizes are chosen to make sizeof (struct crypt_data) add up to exactly 32768 bytes. */ #define CRYPT_DATA_RESERVED_SIZE 767 #define CRYPT_DATA_INTERNAL_SIZE 30720 compared to what glibc has: #ifdef __USE_GNU /* This structure provides scratch and output buffers for 'crypt_r'. Its contents should not be accessed directly. */ struct crypt_data { char keysched[16 * 8]; char sb0[32768]; char sb1[32768]; char sb2[32768]; char sb3[32768]; /* end-of-aligment-critical-data */ char crypt_3_buf[14]; char current_salt[2]; long int current_saltbits; int direction, initialized; }; libxcrypt was added in SLE15 SP3 as "ABI compatible replacement for libcypt from glibc" when glibc was updated to version 2.31 and its internal crypt was disabled.
The crypt interception was removed with https://github.com/llvm/llvm-project/commit/d7bead833631486e337e541e692d9b4a1ca14edd and I'm considering to simply backport this.
This is an autogenerated message for OBS integration: This bug (1219520) was mentioned in https://build.opensuse.org/request/show/1145076 Factory / gcc13