Skip to content

[libfuzzer] Out-of-memory limit calculations don't work well with realloc #83828

Open
@mactul

Description

@mactul

Hi, I've noticed that libfuzzer is counting the memory allocated by realloc incorrectly.
For example:

void* ptr = malloc(100);
ptr = realloc(ptr, 200);

Here, libfuzzer counts an allocation of 300 bytes (100+200) instead of just 200.

Here's the proof:

#include <stdint.h>
#include <stdlib.h>

extern int LLVMFuzzerTestOneInput(const uint8_t *Data __attribute__((unused)), size_t Size __attribute__((unused)))
{
    void* ptr = NULL;
    for(int i = 10000; i < 100000; i++)
    {
        ptr = realloc(ptr, i);
    }

    free(ptr);

    return 0;
}

By definition, this program only consumes 99999 bytes, which is well below libfuzzer's default limit of 256MB (2048Mb).

However, if you compile the program and run it:

clang -o clang_bug clang_bug.c -fsanitize=address,fuzzer -Wall -Wextra
./clang_bug

You get an out-of-memory

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 1295557038
INFO: Loaded 1 modules   (3 inline 8-bit counters): 3 [0x62836eaa9b88, 0x62836eaa9b8b), 
INFO: Loaded 1 PC tables (3 PCs): 3 [0x62836eaa9b90,0x62836eaa9bc0), 
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
==2493== ERROR: libFuzzer: out-of-memory (used: 2855Mb; limit: 2048Mb)
   To change the out-of-memory limit use -rss_limit_mb=<N>

Live Heap Allocations: 24233028 bytes in 20 chunks; quarantined: 249670930 bytes in 9176 chunks; 51767 other chunks; total chunks: 60963; showing top 95% (at most 8 unique contexts)
24121072 byte(s) (99%) in 8 allocation(s)

My hypothesis is that the memory calculated is in fact the sum of i from 10000 to 100000.
This makes 99999*100000/2 - 9999*10000/2 or 4949955000 bytes, so about 4.6GB, well beyond libfuzzer's default limit.

This hypothesis seems even more credible to me if you consider that this program poses no problems:

#include <stdint.h>
#include <stdlib.h>

extern int LLVMFuzzerTestOneInput(const uint8_t *Data __attribute__((unused)), size_t Size __attribute__((unused)))
{
    void* ptr = NULL;
    for(int i = 10000; i < 100000; i++)
    {
        free(ptr);
        ptr = malloc(i);
    }

    free(ptr);

    return 0;
}

I use clang version 16.0.6 on Arch Linux.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions