juce::Heapblock<float> crashes when trying to free

Hello everyone, this is my first time making a post because I really can’t find anything about this specific issue and it generally means that I personally did something wrong.

I am working with a few juce::HeapBlock<float> like i have been for some time now but I have a specific set of them that don’t act like the others and crash when reallocated. Here’s the exact error:

Upon investigation I figured out that the crash happens during std::free(data) upon reallocating or destroying the HeapBlock, and I cannot find why it happens to these and not the other HeapBlocks I have.

Here is how I define them:

juce::HeapBlock<float> blockMain, blockSide, blockOut;

Here is how I reallocate them:

blockMain.allocate(windowSize, true);
blockSide.allocate(windowSize, true);
blockOut.allocate(windowSizePadded, true);

/* my processing involves convolution so I need more space to store the output with an overlap-add method */

Here is how I use them:

// filling up from audio inputs
blockMain[headIn] = mainPtr[s];
blockSide[headIn] = sidePtr[s];
if (++headIn > windowSize) headIn = 0;

// filling up from computations output
outPtr[s] = blockOut[headOut];
blockOut[headOut] = 0.0f;
if (++headOut > windowSizePadded) headOut = 0;

/* headIn and headOut are reading/writing heads of the ring buffers */
// copying ring buffers content into fft input buffers
int headroom = windowSize - headIn;
std::copy_n(blockMain.get() + headIn,   headroom,   fftMain.get());
std::copy_n(blockMain.get(),            headIn,     fftMain.get() + headroom);
std::copy_n(blockSide.get() + headIn,   headroom,   fftSide.get());
std::copy_n(blockSide.get(),            headIn,     fftSide.get() + headroom);
// copying fft output buffers into ring buffer with an overlap-add
headroom = windowSizePadded - headOut;
juce::FloatVectorOperations::add(blockOut.get() + headOut,  fftMain.get(),              headroom);
juce::FloatVectorOperations::add(blockOut.get(),            fftMain.get() + headroom,   headOut);

I have no clue why those three specific HeapBlocks fail at freeing their memory and not the others, given they are managed in the same way. They don’t appear anywhere else in my code than what is being shown here but if you need more context please ask and I shall send what I can.

I figured out the issue on my own: my incrementation of headIn and headOut is wrong. The crash on free doesn’t mean that std::free(data) was writing out of range, but only that at some point prior to this crash, some data was written out of range.

Right now it does

if (++headIn > windowSize) headIn = 0;
if (++headOut > windowSizePadded) headOut = 0;

which means that headIn == windowSize and headOut == windowSizePadded will pass through and won’t reset the index.

I still have no idea how this out of range index didn’t get flagged before an std::free(data) but at least I figured where the data got corrupted. Let this be a lesson to always double check conditions.

Memory allocation algorithms often write “guard bytes” around the chunk of memory. If those get corrupted (by writing outside of the allocated space for example), this corruption can be detected when the block gets freed.

Interesting, any reason why the corruption isn’t detected when those guard bytes are being overwritten? Shouldn’t they be read-only or something like this specifically to avoid corrupting them?

CPUs typically do not have the ability to set a hardware flag for “read-only” on an arbitrary section of memory. Also, most memory pointers do not know the range that they are allowed to write into.

From a software perspective, some programming languages have more safeguards built into them, for this type of safety checks. Usually this is not “free”, some performance will be sacrificed.

I see, thanks for the detailed answer, have a nice day!

The container class could in principle do bounds checking in its [ ] operator, but that has performance implications if it is done for each access. (Some C++ standard library implementations do that kind of bounds checking for debug builds for example in std::vector but that can significantly reduce performance which already isn’t that great in debug builds.)

Also, the Juce HeapBlock is a very simple wrapper for a pointer, it doesn’t even store the size of the allocation that has been done, so the bounds checking couldn’t be done in that.

1 Like