mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
StandalonePluginHolder: Fix out-of-bounds read when audio callbacks use larger-than-expected buffers
This commit is contained in:
parent
0e47da1474
commit
0e7fd1b909
1 changed files with 87 additions and 2 deletions
|
|
@ -408,6 +408,91 @@ public:
|
|||
Array<MidiDeviceInfo> lastMidiDevices;
|
||||
|
||||
private:
|
||||
/* This class can be used to ensure that audio callbacks use buffers with a
|
||||
predictable maximum size.
|
||||
|
||||
On some platforms (such as iOS 10), the expected buffer size reported in
|
||||
audioDeviceAboutToStart may be smaller than the blocks passed to
|
||||
audioDeviceIOCallback. This can lead to out-of-bounds reads if the render
|
||||
callback depends on additional buffers which were initialised using the
|
||||
smaller size.
|
||||
|
||||
As a workaround, this class will ensure that the render callback will
|
||||
only ever be called with a block with a length less than or equal to the
|
||||
expected block size.
|
||||
*/
|
||||
class CallbackMaxSizeEnforcer : public AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
explicit CallbackMaxSizeEnforcer (AudioIODeviceCallback& callbackIn)
|
||||
: inner (callbackIn) {}
|
||||
|
||||
void audioDeviceAboutToStart (AudioIODevice* device) override
|
||||
{
|
||||
maximumSize = device->getCurrentBufferSizeSamples();
|
||||
storedInputChannels .resize ((size_t) device->getActiveInputChannels() .countNumberOfSetBits());
|
||||
storedOutputChannels.resize ((size_t) device->getActiveOutputChannels().countNumberOfSetBits());
|
||||
|
||||
inner.audioDeviceAboutToStart (device);
|
||||
}
|
||||
|
||||
void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples) override
|
||||
{
|
||||
jassert ((int) storedInputChannels.size() == numInputChannels);
|
||||
jassert ((int) storedOutputChannels.size() == numOutputChannels);
|
||||
ignoreUnused (numInputChannels, numOutputChannels);
|
||||
|
||||
int position = 0;
|
||||
|
||||
while (position < numSamples)
|
||||
{
|
||||
const auto blockLength = jmin (maximumSize, numSamples - position);
|
||||
|
||||
initChannelPointers (inputChannelData, storedInputChannels, position);
|
||||
initChannelPointers (outputChannelData, storedOutputChannels, position);
|
||||
|
||||
inner.audioDeviceIOCallback (storedInputChannels.data(),
|
||||
(int) storedInputChannels.size(),
|
||||
storedOutputChannels.data(),
|
||||
(int) storedOutputChannels.size(),
|
||||
blockLength);
|
||||
|
||||
position += blockLength;
|
||||
}
|
||||
}
|
||||
|
||||
void audioDeviceStopped() override
|
||||
{
|
||||
inner.audioDeviceStopped();
|
||||
}
|
||||
|
||||
private:
|
||||
struct GetChannelWithOffset
|
||||
{
|
||||
int offset;
|
||||
|
||||
template <typename Ptr>
|
||||
auto operator() (Ptr ptr) const noexcept -> Ptr { return ptr + offset; }
|
||||
};
|
||||
|
||||
template <typename Ptr, typename Vector>
|
||||
void initChannelPointers (Ptr&& source, Vector&& target, int offset)
|
||||
{
|
||||
std::transform (source, source + target.size(), target.begin(), GetChannelWithOffset { offset });
|
||||
}
|
||||
|
||||
AudioIODeviceCallback& inner;
|
||||
int maximumSize = 0;
|
||||
std::vector<const float*> storedInputChannels;
|
||||
std::vector<float*> storedOutputChannels;
|
||||
};
|
||||
|
||||
CallbackMaxSizeEnforcer maxSizeEnforcer { *this };
|
||||
|
||||
//==============================================================================
|
||||
class SettingsComponent : public Component
|
||||
{
|
||||
|
|
@ -533,7 +618,7 @@ private:
|
|||
const String& preferredDefaultDeviceName,
|
||||
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
|
||||
{
|
||||
deviceManager.addAudioCallback (this);
|
||||
deviceManager.addAudioCallback (&maxSizeEnforcer);
|
||||
deviceManager.addMidiInputDeviceCallback ({}, &player);
|
||||
|
||||
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
|
||||
|
|
@ -544,7 +629,7 @@ private:
|
|||
saveAudioDeviceState();
|
||||
|
||||
deviceManager.removeMidiInputDeviceCallback ({}, &player);
|
||||
deviceManager.removeAudioCallback (this);
|
||||
deviceManager.removeAudioCallback (&maxSizeEnforcer);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue