Bug 2279062 - compiling code with -fsanitize=address results in no leaks found when there should be a leak detected
Summary: compiling code with -fsanitize=address results in no leaks found when there s...
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Fedora
Classification: Fedora
Component: gcc
Version: 40
Hardware: x86_64
OS: Linux
unspecified
medium
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2024-05-04 13:45 UTC by Neil Horman
Modified: 2024-05-06 12:43 UTC (History)
12 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2024-05-06 12:18:43 UTC
Type: ---
Embargoed:


Attachments (Terms of Use)

Description Neil Horman 2024-05-04 13:45:21 UTC
openssl has a memory leak detection validation test, located in test/memleaktest.c:

#include <openssl/crypto.h>                                                     
                                                                                
#include "testutil.h"                                                           
                                                                                
/* __has_feature is a clang-ism, while __SANITIZE_ADDRESS__ is a gcc-ism */     
#if defined(__has_feature)                                                      
# if __has_feature(address_sanitizer)                                           
#  define __SANITIZE_ADDRESS__ 1                                                
# endif                                                                         
#endif                                                                          
/* If __SANITIZE_ADDRESS__ isn't defined, define it to be false */              
/* Leak detection is not yet supported with MSVC on Windows, so */              
/* set __SANITIZE_ADDRESS__ to false in this case as well.      */              
#if !defined(__SANITIZE_ADDRESS__) || defined(_MSC_VER)                         
# undef __SANITIZE_ADDRESS__                                                    
# define __SANITIZE_ADDRESS__ 0                                                 
#endif                                                                          
                                                                                
/*                                                                              
 * We use a proper main function here instead of the custom main from the       
 * test framework to avoid CRYPTO_mem_leaks stuff.                              
 */                                                                             
                                                                                
int main(int argc, char *argv[])                                                
{                                                                               
#if __SANITIZE_ADDRESS__                                                        
    int exitcode = EXIT_SUCCESS;                                                
#else                                                                           
    /*                                                                          
     * When we don't sanitize, we set the exit code to what we would expect     
     * to get when we are sanitizing.  This makes it easy for wrapper scripts   
     * to detect that we get the result we expect.                              
     */                                                                         
    int exitcode = EXIT_FAILURE;                                                
#endif                                                                          
    char *lost;                                                                 
                                                                                
    lost = OPENSSL_malloc(3);                                                   
    if (!TEST_ptr(lost))                                                        
        return EXIT_FAILURE;                                                    
                                                                                
    strcpy(lost, "ab");           
    if (argv[1] && strcmp(argv[1], "freeit") == 0) {                            
        OPENSSL_free(lost);                                                     
        exitcode = EXIT_SUCCESS;                                                
    }                                                                           
                                                                                
    lost = NULL;                                                                
    return exitcode;                                                            
}       


Compiling openssl's latest version of this code with asan enabled results in this test not detecting a memory leak that it should detect

nhorman@hmsvengance:~/git/openssl/test$ ldd ./memleaktest
	linux-vdso.so.1 (0x00007fff85fd2000)
	libasan.so.8 => /lib64/libasan.so.8 (0x00007f6aa7800000)
	libcrypto.so.3 => /lib64/libcrypto.so.3 (0x00007f6aa7200000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f6aa7013000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f6aa6c00000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f6aa771d000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f6aa76f0000)
	libz.so.1 => /lib64/libz.so.1 (0x00007f6aa7ed0000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f6aa7f11000)

nhorman@hmsvengance:~/git/openssl/test$ ldd ../libcrypto.so
	linux-vdso.so.1 (0x00007ffff08ce000)
	libasan.so.8 => /lib64/libasan.so.8 (0x00007efea5a00000)
	libc.so.6 => /lib64/libc.so.6 (0x00007efea5813000)
	libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007efea5400000)
	libm.so.6 => /lib64/libm.so.6 (0x00007efea6d7e000)
	libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007efea6d51000)
	/lib64/ld-linux-x86-64.so.2 (0x00007efea6e7f000)


nhorman@hmsvengance:~/git/openssl/test$ LD_LIBRARY_PATH=/home/nhorman/git/openssl/ ./memleaktest 
nhorman@hmsvengance:~/git/openssl/test$ 


Interestingly, If I change the exit code of the application such that it exits with EXIT_FAILURE rather than EXIT_SUCCESS, the leak is properly detected:
nhorman@hmsvengance:~/git/openssl/test$ LD_LIBRARY_PATH=/home/nhorman/git/openssl/ ./memleaktest 

=================================================================
==11037==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 3 byte(s) in 1 object(s) allocated from:
    #0 0x7f2561af68b7 in malloc (/lib64/libasan.so.8+0xf68b7) (BuildId: 9398d675390359c74744b0e7714408910d853874)
    #1 0x7f25612d6be0 in CRYPTO_malloc crypto/mem.c:202
    #2 0x4024b1 in main test/memleaktest.c:49
    #3 0x7f2560c3d087 in __libc_start_call_main (/lib64/libc.so.6+0x2a087) (BuildId: b098f1c75a76548bb230d8f551eae07a2aeccf06)
    #4 0x7f2560c3d14a in __libc_start_main_alias_2 (/lib64/libc.so.6+0x2a14a) (BuildId: b098f1c75a76548bb230d8f551eae07a2aeccf06)
    #5 0x402614 in _start (/home/nhorman/git/openssl/test/memleaktest+0x402614) (BuildId: 2d89b949e7e0f801b3d552aaa03ad54083c09f1c)

SUMMARY: AddressSanitizer: 3 byte(s) leaked in 1 allocation(s).


Reproducible: Always

Steps to Reproduce:
1.build the latest version of openssl with enable-asan in the config so that -fsanitize=address is added to the CFLAGS/LDFLAGS
2. run memleaktest: LD_LIBRARY_PATH=/path/to/openssl/build ./test/memleaktest

Actual Results:  
No leak is detected

Expected Results:  
The leak implemented by the allocation to the lost pointer via OPENSSL_malloc should be detected

Comment 1 Florian Weimer 2024-05-06 06:31:30 UTC
Please provide a self-contained reproducer. I cannot reproduce is on Fedora 40.

Comment 2 Neil Horman 2024-05-06 12:18:43 UTC
hmm, scratch this.  Looks like a reboot corrected the problem for me.  Apologies for the noise

Comment 3 Florian Weimer 2024-05-06 12:43:12 UTC
I would have suspected an optimized-out allocation in an LTO build.


Note You need to log in before you can comment on or make changes to this bug.