Bug 1219520 - ASAN crypt_r interceptor uses wrong sizeof(struct crypt_data)
Summary: ASAN crypt_r interceptor uses wrong sizeof(struct crypt_data)
Status: IN_PROGRESS
Alias: None
Product: PUBLIC SUSE Linux Enterprise Server 15 SP5
Classification: openSUSE
Component: Basesystem (show other bugs)
Version: unspecified
Hardware: x86-64 SLES 15
: P5 - None : Normal
Target Milestone: ---
Assignee: Richard Biener
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-02-03 15:02 UTC by Markus Mäkelä
Modified: 2024-02-08 09:25 UTC (History)
3 users (show)

See Also:
Found By: ---
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Mäkelä 2024-02-03 15:02:05 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)
Comment 1 Richard Biener 2024-02-08 08:01:38 UTC
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.
Comment 2 Richard Biener 2024-02-08 08:05:47 UTC
The crypt interception was removed with

https://github.com/llvm/llvm-project/commit/d7bead833631486e337e541e692d9b4a1ca14edd

and I'm considering to simply backport this.
Comment 3 OBSbugzilla Bot 2024-02-08 09:25:03 UTC
This is an autogenerated message for OBS integration:
This bug (1219520) was mentioned in
https://build.opensuse.org/request/show/1145076 Factory / gcc13