Death-crash FIXED: 25-voice explosion preset overflowed the 5-slot audio SourceSet (Gitea #12)
The weekend's crash family root-caused under cdb: SourceSet.sources[5] receives the AllExplosion death preset's 25 streamed voices -- RequestAudioChannels wrote 20 OpenAL source ids past the array, smashing the neighbouring heap object (Release: a vtable overwritten with a source id -> delayed silent AV at AudioControlEvent::Send; Debug: CRT heap- corruption abort in the death Explosion's audio teardown). Explains all three #12 crash flavors (solo enemy kill -- stack-confirmed; MP self- death; MP peer PEER_DOWN cascade -- same generic teardown). Fix: sources[] sized to AUDIO_SOURCESET_CAPACITY=25, static_assert lockstep with MAX_PRESET_SAMPLES, + defence-in-depth clamps at the ctor and acquisition sites. Soak: 26+ deaths across glass AND pod builds under cdb, zero faults, full wreck lifecycle every time. Gotchas S21: the fixed-array-vs-streamed-count overflow class + sweep note. Awaiting human verification: the MP death-and-survive session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6ad1e074cc
commit
a0cec48e3f
@@ -6,10 +6,24 @@
|
||||
#include "..\munga\audio.h"
|
||||
#include "openal/al.h"
|
||||
|
||||
// SourceSet capacity (Gitea #12 death-crash fix): the OpenAL sources array
|
||||
// must hold as many voices as a preset can carry. channelSet.count is set
|
||||
// from GetAudioVoiceCount() (the streamed voiceCount), and a full-zone preset
|
||||
// legitimately carries up to MAX_PRESET_SAMPLES (== 25, L4AUDLVL.h) sample
|
||||
// zones -- one OpenAL source each (RequestAudioChannels alGenSources into
|
||||
// sources[0..count-1], SetupPatch/PlayNote index sources[i] parallel to the
|
||||
// samples[] zones). This array was 5 wide, so the death-boom preset (>5
|
||||
// voices) overwrote adjacent heap on channel acquisition -- corrupting the
|
||||
// next AudioComponent's vtable / the heap guard bytes and crashing seconds
|
||||
// later at the death Explosion's audio-entity teardown (DestroyEntityAudio-
|
||||
// Objects) or the next sequenced virtual dispatch. L4AUDLVL.h static_asserts
|
||||
// MAX_PRESET_SAMPLES <= AUDIO_SOURCESET_CAPACITY to keep the two in lockstep.
|
||||
const int AUDIO_SOURCESET_CAPACITY = 25;
|
||||
|
||||
struct SourceSet
|
||||
{
|
||||
int count;
|
||||
ALuint sources[5];
|
||||
ALuint sources[AUDIO_SOURCESET_CAPACITY];
|
||||
};
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MIDI types ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -672,7 +672,20 @@ L4AudioSource::L4AudioSource(
|
||||
):
|
||||
AudioSource(stream, entity)
|
||||
{
|
||||
channelSet.count = GetAudioVoiceCount();
|
||||
// Gitea #12: never let the voice count exceed the SourceSet capacity -- a
|
||||
// malformed/oversized preset stream would otherwise overflow sources[] on
|
||||
// channel acquisition and corrupt the heap (the death-crash class). The
|
||||
// array is now sized to the largest legitimate preset (AUDIO_SOURCESET_
|
||||
// CAPACITY == MAX_PRESET_SAMPLES), so this clamp is defence-in-depth.
|
||||
int voices = GetAudioVoiceCount();
|
||||
if (voices > AUDIO_SOURCESET_CAPACITY)
|
||||
{
|
||||
DEBUG_STREAM << "[audio] WARNING: voiceCount " << voices
|
||||
<< " exceeds SourceSet capacity " << AUDIO_SOURCESET_CAPACITY
|
||||
<< " -- clamped (Gitea #12)\n" << std::flush;
|
||||
voices = AUDIO_SOURCESET_CAPACITY;
|
||||
}
|
||||
channelSet.count = voices;
|
||||
L4AudioSourceX();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,13 @@ struct SAMPLEINFO
|
||||
// AllExplosion (bank2 p125) authors 25 layered zones -- the largest preset.
|
||||
const int MAX_PRESET_SAMPLES = 25;
|
||||
|
||||
// Gitea #12: the SourceSet OpenAL-source array must hold one source per zone,
|
||||
// so its capacity must cover the largest preset. If MAX_PRESET_SAMPLES ever
|
||||
// grows past the SourceSet array, channel acquisition would overflow the heap
|
||||
// (the death-crash class) -- lock the two together at compile time.
|
||||
static_assert(MAX_PRESET_SAMPLES <= AUDIO_SOURCESET_CAPACITY,
|
||||
"SourceSet.sources[] too small for MAX_PRESET_SAMPLES voices (Gitea #12)");
|
||||
|
||||
struct PRESETINFO
|
||||
{
|
||||
int sampleNum;
|
||||
|
||||
@@ -1294,6 +1294,13 @@ Logical
|
||||
//Do we have enough?
|
||||
int requested = source_request->count;
|
||||
|
||||
// Gitea #12: hard cap at the SourceSet capacity so alGenSources can never
|
||||
// write past sources[] (the death-crash heap overflow). count is set from
|
||||
// the streamed voiceCount and is normally already clamped in the
|
||||
// L4AudioSource ctor; this guards the acquisition site itself.
|
||||
if (requested > AUDIO_SOURCESET_CAPACITY)
|
||||
requested = AUDIO_SOURCESET_CAPACITY;
|
||||
|
||||
bool failed = true;
|
||||
|
||||
alGetError();
|
||||
|
||||
Reference in New Issue
Block a user