#free() issue

73 messages ยท Page 1 of 1 (latest)

forest charm
#

HI guys. I'm having problem with freeing memory

num_file = get_filenames(&files);
    
    for (int i = 0; i < num_file; i++)
    {
        do
        { 
            read_file_data(files[i], &data, chunk_size, &data_size, &prev_file_pos);
            printf("data: %s\n", data);

            long alloc_size = data_size + EVP_MAX_BLOCK_LENGTH;
            tmp = realloc(enct, alloc_size);
            if (tmp == NULL)
            {
                printf("Failed realloc %ld bytes for encrypted!\n", alloc_size);
                exit(EXIT_FAILURE);
            }
            enct = tmp;
            tmp = NULL;

            tmp = realloc(dect, alloc_size);
            if (tmp == NULL)
            {
                printf("Failed realloc %ld bytes  for decrypted!\n", alloc_size);
                exit(EXIT_FAILURE);
            }
            dect = tmp;
            tmp = NULL;

            printf("dect pointer after realloc: %p\n", (void *)dect);

            memset(enct, 0, alloc_size);
            memset(dect, 0, alloc_size);

            int len = enc(master_key, iv, data, enct, alloc_size);
            long dec_len = dec(master_key, iv, enct, dect, len);
            dect[dec_len] = '\0'; 

            printf("encrypted: %02x\n", enct);
            printf("decrypted: %s\n", dect);
            printf("dec len %ld : alloc size %ld\n", dec_len, alloc_size);
        }
        while (prev_file_pos > chunk_size);

        free(files[i]);
        free(data);
    }

    free(files);
    free(enct);
    free(dect);

output:
dect pointer after realloc: 0x55e5b9278320
encrypted: b927cf80
decrypted: Hello world 123

dec len 48 : alloc size 48
dect pointer after realloc: 0x55e5b9278320
end of code
double free or corruption (out)
zsh: IOT instruction (core dumped) ./bin/safecli

silent parrotBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

gusty wraith
#

please compile and run with sanitizers

silent parrotBOT
#
Address Sanitizer

Memory errors are common in C and C++ and can be hard to debug because they often manifest far from their source.

Address sanitizer is a runtime tool that identifies memory errors at their source and makes debugging much simpler. This is an essential tool for C and C++ software development.

Address sanitizer is available for gcc/clang on linux and msvc on windows. To use it, simply pass the flag -fsanitize=address to the compiler.

For a detailed walkthrough, see tccpp Address Sanitizer

forest charm
#

how can I do it with make?

#

add CFLAGS?

gusty wraith
#

did you read the bot's message?

gusty wraith
forest charm
gusty wraith
#

thats a different issue - though i suggest you fix that first

#
// the issue
==34490==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000007a80 at pc 0x563579d90887 bp 0x7fffb5559330 sp 0x7fffb5558ab8

READ of size 17 at 0x502000007a80 thread T0
    // where it happened
    #0 0x563579d90886 in printf_common(void*, char const*, __va_list_tag*) asan_interceptors.cpp.o  

    // called by
    #1 0x563579d924e1 in printf (/home/xtonior/Documents/Projects/C/SafeCLI/bin/safecli+0x554e1) (BuildId: d6d0f3f0759b02cdc895916d840d26a7667fa787)

    // called by
    #2 0x563579e4d93f in main (/home/xtonior/Documents/Projects/C/SafeCLI/bin/safecli+0x11093f) (BuildId: d6d0f3f0759b02cdc895916d840d26a7667fa787)

    #3 0x7f9d41c33ca7 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #4 0x7f9d41c33d64 in __libc_start_main csu/../csu/libc-start.c:360:3
    #5 0x563579d6a4d0 in _start (/home/xtonior/Documents/Projects/C/SafeCLI/bin/safecli+0x2d4d0) (BuildId: d6d0f3f0759b02cdc895916d840d26a7667fa787)```
#

if you'd compile with -g you'd get debug info which means (ammong other things) line numbers

forest charm
#
void read_file_data(const char *filename, unsigned char **data, long chunk_size, long *out_len, long *prev_pos)
{
    unsigned char *buffer = {0};

    FILE *fp = NULL;
    struct stat sb = {0};

    char path_buffer[PATH_MAX];
    snprintf(path_buffer, sizeof(path_buffer), "%s/%s", files_path, filename);
    stat(path_buffer, &sb);

    if (S_ISREG(sb.st_mode)) 
    {
        // It's a regular file
        fp = fopen(path_buffer, "rb");

        fseek(fp, 0, SEEK_END);
        int pos = ftell(fp);

        if (pos >= 0 && pos >= chunk_size)
        {
            *prev_pos += chunk_size;

            fseek(fp, -*prev_pos, SEEK_END);
            *out_len = chunk_size;
        }
        else
        {
            *prev_pos = pos;
        
            *out_len = ftell(fp);
            rewind(fp);
            printf("%ld\n", *out_len);
        }

        printf("fp pos: %ld\n", *prev_pos);

        buffer = malloc(*out_len);

        if (!buffer)
        {
            printf("Failed allocating memory for STREAM!\n");
            fclose(fp);

            return;
        }

        fread(buffer, sizeof(buffer[0]), *out_len, fp);

        *data = buffer;
    }

    fclose(fp);
}
#

I'm not sure if there's actually any issue here

gusty wraith
forest charm
#

is it?

#

#1 0x5572c6bffe35 in read_file_data src/main.c:114

gusty wraith
#

great. whats in line in main src/main.c:376?

forest charm
#

printf("data: %s\n", data);

gusty wraith
#

whats the last operation on said data?

forest charm
#

read_file_data(files[i], &data, chunk_size, &data_size, &prev_file_pos);

forest charm
#

data itself is unsigned char *

gusty wraith
#

great. what do you thing this does:

unsigned char *buffer = {0};
...
fread(buffer, sizeof(buffer[0]), *out_len, fp);```?
#

more specifically - do you thing fread null terminate the thing?

forest charm
#

yes I think so

#

since out data_size was 16 bytes for 15 chars length input text

gusty wraith
#

it does not. fread isn't a string related function. it reads bytes and treat the buffer as a byte array rather than a string

forest charm
#

ah

#

should I null terminate thing right after fread()?

gusty wraith
#

if you want to treat data as a c-string - it would be advisable ๐Ÿ™‚

forest charm
#

I'd like to threat it as a block of file tho

gusty wraith
#

in that case printf("data: %s\n", data); would be incorrect

forest charm
#

since I was trying to split large files into blocks and encrypt

forest charm
#

it was debug line btw

gusty wraith
#

debuggers exist for a reason ๐Ÿ™‚

forest charm
#

I have trouble with it rn :)

#

so it's alright with reading data?

gusty wraith
forest charm
#
READ of size 16 at 0x504000005c00 thread T0
    #0 0x7fddde2f2935 in memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
    #1 0x7fddde0140d5  (/lib/x86_64-linux-gnu/libcrypto.so.3+0x4140d5) (BuildId: 267b99e079d8cc3929d64c5c14e902a6d290f1da)
    #2 0x7fddde012f5e  (/lib/x86_64-linux-gnu/libcrypto.so.3+0x412f5e) (BuildId: 267b99e079d8cc3929d64c5c14e902a6d290f1da)
    #3 0x7fdddde0c901 in EVP_DecryptUpdate (/lib/x86_64-linux-gnu/libcrypto.so.3+0x20c901) (BuildId: 267b99e079d8cc3929d64c5c14e902a6d290f1da)
    #4 0x55f4ee8bb036 in dec src/main.c:138
    #5 0x55f4ee8bc84e in main src/main.c:403
    #6 0x7fdddda33ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x7fdddda33d64 in __libc_start_main_impl ../csu/libc-start.c:360
    #8 0x55f4ee8ba490 in _start (/home/xtonior/Documents/Projects/C/SafeCLI/bin/safecli+0x3490) (BuildId: 7ef0f8cd7e70a666cd04fd89e5e415ebe1b683f9)
gusty wraith
forest charm
#
int dec(unsigned char master_key[32], unsigned char iv[16], unsigned char *encrypted, unsigned char *decrypted, int enc_len)
{
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    int len, dec_len;

    EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, master_key, iv);
    EVP_DecryptUpdate(ctx, decrypted, &len, encrypted, enc_len);
    dec_len = len;

    EVP_DecryptFinal(ctx, decrypted, &len);
    dec_len += len;

    EVP_CIPHER_CTX_free(ctx);
    return dec_len;
}

int enc(unsigned char master_key[32], unsigned char iv[16], unsigned char *data, unsigned char *encrypted, size_t data_size)
{
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    int len, enc_len;

    EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, master_key, iv);
    EVP_EncryptUpdate(ctx, encrypted, &len, data, data_size);
    enc_len = len;

    EVP_EncryptFinal(ctx, encrypted + len, &len);
    enc_len += len;

    EVP_CIPHER_CTX_free(ctx);

    return enc_len;
}
#

here's full output btw

gusty wraith
#
    #4 0x55f4ee8bb036 in dec src/main.c:138
    #5 0x55f4ee8bc84e in main src/main.c:403```
whats in these 2?
forest charm
#

138 (dec()): EVP_DecryptUpdate(ctx, decrypted, &len, encrypted, enc_len);
403: long dec_len = dec(master_key, iv, enct, dect, len);

gusty wraith
#

are both decrypted and encrypted allocated properly? if so - are the large enough?

forest charm
#
long alloc_size = data_size + EVP_MAX_BLOCK_LENGTH;
            tmp = realloc(enct, alloc_size);
            if (tmp == NULL)
            {
                printf("Failed realloc %ld bytes for encrypted!\n", alloc_size);
                exit(EXIT_FAILURE);
            }
            enct = tmp;
            tmp = NULL;

            tmp = realloc(dect, alloc_size);
            if (tmp == NULL)
            {
                printf("Failed realloc %ld bytes  for decrypted!\n", alloc_size);
                exit(EXIT_FAILURE);
            }
            dect = tmp;
            tmp = NULL;
#

initially they are

    unsigned char *enct = NULL;
    unsigned char *dect = NULL;
    unsigned char *tmp = NULL;
gusty wraith
#

ok, lets assume they are allocated. in that case it appears they aren't large enough. the library you're using surely provide some function to determine the size required for decrypted

forest charm
#

dec() and enc() return length

#

ohh
enc len 64 : alloc size 48
dec len 48 : alloc size 48

gusty wraith
#

not what i meant

forest charm
#

huh

gusty wraith
#

https://manpages.debian.org/experimental/libssl-doc/EVP_DecryptUpdate.3ssl.en.html#EVP_EncryptUpdate()

The amount of data written depends on the block alignment of the encrypted data. For most ciphers and modes, the amount of data written can be anything from zero bytes to (inl + cipher_block_size - 1) bytes. For wrap cipher modes, the amount of data written can be anything from zero bytes to (inl + cipher_block_size) bytes. For stream ciphers, the amount of data written can be anything from zero bytes to inl bytes. Thus, the buffer pointed to by out must contain sufficient room for the operation being performed.

forest charm
#

ah I missread

#

isn't long alloc_size = data_size + EVP_MAX_BLOCK_LENGTH; enough then?

gusty wraith
#

๐Ÿคทโ€โ™‚๏ธ i have no idea. i never used libssl. it appears it isn't though, or rather it might be that since both are the same size and the output might be larger than the input - it causes the overflow

forest charm
#

i see

#

actually openssl docs suck imo

#

printf("%d\n", EVP_CIPHER_CTX_get_block_size(ctx));

#

16

#

k i fixed

#

just passing data_size in enc() now instead of alloc_size

#

also made some fixes in file read

#

how can I close thread?

gusty wraith
#

with !solved

forest charm
#

thx @gusty wraith for help!

#

!solved

silent parrotBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity