mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-09 03:29:43 -10:00
audio_mixer: fix four defects the windowed preview path exposed
ef19c2f handed the mixer a windowed container. Four things in the
mixer could not take one, and between them they produced a preview
that looped the same second forever and a SIGSEGV on WebM.
1. audio_mixer_voice_set_avail had arms for OGG and OPUS only. A
windowed M4A voice therefore accepted every feeder raise and
dropped it: the decoder kept the bound it was given at add_stream,
reached it a second or so in, reported end of stream, and a looping
voice restarted. That is the repeat. M4A and FLAC arms added and
the guard widened - it required HAVE_RWEBM with Opus or Vorbis, so
the whole function compiled to nothing in an AAC-only build.
2. audio_mixer_load_weba sniffed the codec with the file's whole
length. It runs before the sound exists, so sound_set_avail cannot
have been applied, and on a windowed buffer the segment walk went
straight off the committed head:
#0 rwebm_open_memory_avail
#1 audio_transfer_webm_audio_type
#2 audio_mixer_load_weba
#3 audio_driver_mixer_add_stream
Added audio_mixer_load_weba_avail, which bounds the sniff; the
plain form is now a wrapper passing 0, so no other caller changes.
3. The load_* API took int32_t size and the sound stored it as
unsigned. A 7,817,178,301-byte container arrived as -772,756,291
and was stored as 3,522,211,005 - the demuxer then treated every
sample past 3.5 GB as out of range, which truncates the stream
regardless of what the feeder does. Both widened to size_t.
4. sound_set_avail was applied under #ifdef HAVE_ROPUS, so a build
without Opus dropped the resident bound entirely and opened the
decoder against the whole file with only the head committed. The
bound is not Opus-specific; it now applies unconditionally, with
only the end-granule injection left under HAVE_ROPUS.
On the testing, which is the part that failed here: the previous
harness stubbed audio_driver_mixer_add_stream and
audio_driver_mixer_stream_set_avail, so it modelled the two functions
that contained three of these four defects and could not have caught
any of them. samples/audio/mixer_real links the real audio_mixer.c
against a genuine reserved mapping - everything past the resident
bound is PROT_NONE, so a read past it faults instead of passing - and
checks the >4 GiB length survives the load and that the bounded sniff
stays inside the head.
AAC PCM equivalence re-checked and still byte-exact over 144000
frames with the window starved to force backpressure. audio_mixer.c
builds at C89 with RAAC+RMP4, with RWEBM+RVORBIS, with RAAC alone and
with none.
This commit is contained in:
1 parent
ef19c2f0a3
commit
b0099a43e4
3 files changed
+75
-31
No files matched your search
+15
-10
@@ -2478,7 +2478,7 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
|
||||
switch (params->type)
|
||||
{
|
||||
case AUDIO_MIXER_TYPE_WAV:
|
||||
handle = audio_mixer_load_wav(buf, (int32_t)params->bufsize,
|
||||
handle = audio_mixer_load_wav(buf, params->bufsize,
|
||||
audio_driver_st.resampler_ident,
|
||||
audio_driver_st.resampler_quality,
|
||||
audio_driver_mixer_use_s16(
|
||||
@@ -2499,37 +2499,38 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
|
||||
* them as it mixes, so the source stays owned by the sound
|
||||
* (or by its lender) exactly as the compressed types' does */
|
||||
handle = audio_mixer_load_wav_stream(buf,
|
||||
(int32_t)params->bufsize);
|
||||
params->bufsize);
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_OGG:
|
||||
handle = audio_mixer_load_ogg(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_ogg(buf, params->bufsize);
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_MOD:
|
||||
handle = audio_mixer_load_mod(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_mod(buf, params->bufsize);
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_FLAC:
|
||||
#ifdef HAVE_RFLAC
|
||||
handle = audio_mixer_load_flac(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_flac(buf, params->bufsize);
|
||||
#endif
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_MP3:
|
||||
#ifdef HAVE_RMP3
|
||||
handle = audio_mixer_load_mp3(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_mp3(buf, params->bufsize);
|
||||
#endif
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_M4A:
|
||||
#ifdef HAVE_RAAC
|
||||
handle = audio_mixer_load_m4a(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_m4a(buf, params->bufsize);
|
||||
#endif
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_OPUS:
|
||||
#ifdef HAVE_ROPUS
|
||||
handle = audio_mixer_load_opus(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_opus(buf, params->bufsize);
|
||||
#endif
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_WEBA:
|
||||
#if defined(HAVE_RWEBM) && (defined(HAVE_ROPUS) || defined(HAVE_RVORBIS))
|
||||
handle = audio_mixer_load_weba(buf, (int32_t)params->bufsize);
|
||||
handle = audio_mixer_load_weba_avail(buf, params->bufsize,
|
||||
params->avail);
|
||||
#endif
|
||||
break;
|
||||
case AUDIO_MIXER_TYPE_NONE:
|
||||
@@ -2554,9 +2555,13 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
|
||||
* skips its full-file end scan when it opens at play time. */
|
||||
if (params->end_granule > 0)
|
||||
audio_mixer_sound_set_end_granule(handle, params->end_granule);
|
||||
#endif
|
||||
/* NOT under HAVE_ROPUS: the resident bound applies to every
|
||||
* windowed arm, and a build without Opus silently dropped it -
|
||||
* the decoder then opened against the whole file's length with
|
||||
* only the head committed. */
|
||||
if (params->avail)
|
||||
audio_mixer_sound_set_avail(handle, params->avail);
|
||||
#endif
|
||||
|
||||
switch (params->state)
|
||||
{
|
||||
|
||||
@@ -138,7 +138,11 @@ struct audio_mixer_sound
|
||||
{
|
||||
/* shared streaming source (WAV / OGG / FLAC / MP3 / ...) */
|
||||
const void* data;
|
||||
unsigned size;
|
||||
/* size_t, not unsigned: a windowed container is the whole
|
||||
* file's length, and a 7 GB one truncated to 32 bits became
|
||||
* 3.5 GB - the demuxer then treated every sample past that
|
||||
* as out of range and the stream ended seconds in. */
|
||||
size_t size;
|
||||
} stream;
|
||||
#endif
|
||||
|
||||
@@ -1043,7 +1047,7 @@ static bool wav_build_float(audio_mixer_sound_t* sound,
|
||||
static bool wav_build_s16(audio_mixer_sound_t* sound,
|
||||
enum resampler_quality quality);
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size,
|
||||
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, size_t size,
|
||||
const char *resampler_ident, enum resampler_quality quality,
|
||||
bool want_s16)
|
||||
{
|
||||
@@ -1114,7 +1118,7 @@ audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size,
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_wav_stream(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_wav_stream(void *buffer, size_t size)
|
||||
{
|
||||
#ifdef HAVE_RWAV
|
||||
audio_mixer_sound_t* sound;
|
||||
@@ -1139,7 +1143,7 @@ audio_mixer_sound_t* audio_mixer_load_wav_stream(void *buffer, int32_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, size_t size)
|
||||
{
|
||||
#if defined(HAVE_RVORBIS) || defined(HAVE_ROPUS) || defined(HAVE_RFLAC)
|
||||
audio_mixer_sound_t* sound;
|
||||
@@ -1182,7 +1186,7 @@ audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, size_t size)
|
||||
{
|
||||
#ifdef HAVE_RFLAC
|
||||
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||
@@ -1200,7 +1204,7 @@ audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, size_t size)
|
||||
{
|
||||
#ifdef HAVE_RMP3
|
||||
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||
@@ -1218,7 +1222,7 @@ audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, int32_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_m4a(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_m4a(void *buffer, size_t size)
|
||||
{
|
||||
#ifdef HAVE_RAAC
|
||||
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||
@@ -1236,7 +1240,7 @@ audio_mixer_sound_t* audio_mixer_load_m4a(void *buffer, int32_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_opus(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_opus(void *buffer, size_t size)
|
||||
{
|
||||
#ifdef HAVE_ROPUS
|
||||
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||
@@ -1254,13 +1258,19 @@ audio_mixer_sound_t* audio_mixer_load_opus(void *buffer, int32_t size)
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_weba(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_weba_avail(void *buffer, size_t size,
|
||||
size_t avail)
|
||||
{
|
||||
#if defined(HAVE_RWEBM) && (defined(HAVE_ROPUS) || defined(HAVE_RVORBIS) \
|
||||
|| defined(HAVE_RAAC) || defined(HAVE_RFLAC))
|
||||
audio_mixer_sound_t* sound;
|
||||
/* Bound the sniff to what is resident. This runs before the sound
|
||||
* exists, so sound_set_avail cannot have been applied yet - and
|
||||
* with the whole file's length it walked the segment straight off
|
||||
* the committed head of a windowed buffer and died inside
|
||||
* rwebm_open_memory_avail. */
|
||||
enum audio_type_enum ty = audio_transfer_webm_audio_type(buffer,
|
||||
(size_t)size);
|
||||
(avail && avail < size) ? avail : size);
|
||||
enum audio_mixer_type mt;
|
||||
|
||||
/* Resolve to the existing sound type whose streaming arm accepts
|
||||
@@ -1287,11 +1297,17 @@ audio_mixer_sound_t* audio_mixer_load_weba(void *buffer, int32_t size)
|
||||
sound->types.stream.data = buffer;
|
||||
return sound;
|
||||
#else
|
||||
(void)avail;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size)
|
||||
audio_mixer_sound_t* audio_mixer_load_weba(void *buffer, size_t size)
|
||||
{
|
||||
return audio_mixer_load_weba_avail(buffer, size, 0);
|
||||
}
|
||||
|
||||
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, size_t size)
|
||||
{
|
||||
#ifdef HAVE_RMODTRACKER
|
||||
audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));
|
||||
@@ -1349,7 +1365,8 @@ void audio_mixer_sound_set_end_granule(audio_mixer_sound_t *sound,
|
||||
* voice lock: safe against the mixing thread. */
|
||||
void audio_mixer_voice_set_avail(audio_mixer_voice_t *voice, size_t avail)
|
||||
{
|
||||
#if defined(HAVE_RWEBM) && (defined(HAVE_ROPUS) || defined(HAVE_RVORBIS))
|
||||
#if (defined(HAVE_RWEBM) && (defined(HAVE_ROPUS) || defined(HAVE_RVORBIS))) \
|
||||
|| defined(HAVE_RAAC) || defined(HAVE_RFLAC)
|
||||
if (!voice)
|
||||
return;
|
||||
#ifdef AUDIO_MIXER_HAS_STREAM
|
||||
@@ -1367,6 +1384,23 @@ void audio_mixer_voice_set_avail(audio_mixer_voice_t *voice, size_t avail)
|
||||
audio_transfer_set_avail(voice->types.stream.stream,
|
||||
AUDIO_TYPE_OPUS, avail);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_RAAC
|
||||
/* The arm a windowed M4A actually lands on. Its absence meant
|
||||
* every feeder raise was accepted and dropped: the decoder kept
|
||||
* the bound it was given at add_stream, hit it a second or so
|
||||
* in, reported end of stream, and a looping voice restarted -
|
||||
* the same second of audio over and over. */
|
||||
case AUDIO_MIXER_TYPE_M4A:
|
||||
audio_transfer_set_avail(voice->types.stream.stream,
|
||||
AUDIO_TYPE_AAC, avail);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_RFLAC
|
||||
case AUDIO_MIXER_TYPE_FLAC:
|
||||
audio_transfer_set_avail(voice->types.stream.stream,
|
||||
AUDIO_TYPE_FLAC, avail);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -76,21 +76,26 @@ void audio_mixer_done(void);
|
||||
* mixer's current mode will play. The other format derives on
|
||||
* demand at the first mode-mismatched play (see the derivation notes
|
||||
* in the implementation), so a WAV holds one PCM copy, not two. */
|
||||
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size,
|
||||
audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, size_t size,
|
||||
const char *resampler_ident, enum resampler_quality quality,
|
||||
bool want_s16);
|
||||
audio_mixer_sound_t* audio_mixer_load_wav_stream(void *buffer,
|
||||
int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_m4a(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_opus(void *buffer, int32_t size);
|
||||
size_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, size_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, size_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, size_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, size_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_m4a(void *buffer, size_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_opus(void *buffer, size_t size);
|
||||
/* WebM audio (.weba): identifies the track's codec and returns a sound
|
||||
* of the matching existing type (OPUS or OGG), or NULL when the
|
||||
* container carries no supported track. */
|
||||
audio_mixer_sound_t* audio_mixer_load_weba(void *buffer, int32_t size);
|
||||
audio_mixer_sound_t* audio_mixer_load_weba(void *buffer, size_t size);
|
||||
/* As above, but the codec sniff reads no further than 'avail' bytes.
|
||||
* A windowed caller must use this: the plain form walks the segment
|
||||
* with the file's whole length, which is not resident. */
|
||||
audio_mixer_sound_t* audio_mixer_load_weba_avail(void *buffer, size_t size,
|
||||
size_t avail);
|
||||
|
||||
/* Compressed-byte read position of a stream voice's decoder (0 when
|
||||
* not a live buffer-mode stream voice). Thread-safe. */
|
||||
|
||||
Reference in new issue
Block a user