Skip to content

Commit 990e166

Browse files
committed
Fixed bug 3894 - Fuzzing crashes for SDL_LoadWAV
Simon Hug I had a look at this and made some additions to SDL_wave.c. The attached patch adds many checks and error messages. For some reason I also added A-law and ?-law decoders. Forgot exactly why... but hey, they're small. The WAVE format is seriously underspecified (at least by the documents that are publicly available on the internet) and it's a shame Microsoft never put something better out there. The language used in them is so loose at times, it's not surprising the encoders and decoders behave very differently. The Windows Media Player doesn't even support MS ADPCM correctly. The patch also adds some hints to make the decoder more strict at the cost of compatibility with weird WAVE files. I still think it needs a bit of cleaning up (Not happy with the MultiplySize function. Don't like the name and other SDL code may want to use something like this too.) and some duplicated code may be folded together. It does work in this state and I have thrown all kinds of WAVE files at it. The AFL files also pass with it and some even play (obviously just noise). Crafty little fuzzer. Any critique would be welcome. I have a fork of SDL with a audio-loadwav branch over here if someone wants to use the commenting feature of Bitbucket: https://bitbucket.org/ChliHug/SDL I also cobbled some Lua scripts together to create WAVE test files: https://bitbucket.org/ChliHug/gendat
1 parent 48ac92a commit 990e166

File tree

4 files changed

+2217
-616
lines changed

4 files changed

+2217
-616
lines changed

include/SDL_audio.h

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -420,23 +420,56 @@ extern DECLSPEC void SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID dev,
420420
/* @} *//* Pause audio functions */
421421

422422
/**
423-
* This function loads a WAVE from the data source, automatically freeing
424-
* that source if \c freesrc is non-zero. For example, to load a WAVE file,
425-
* you could do:
423+
* \brief Load the audio data of a WAVE file into memory
424+
*
425+
* Loading a WAVE file requires \c src, \c spec, \c audio_buf and \c audio_len
426+
* to be valid pointers. The entire data portion of the file is then loaded
427+
* into memory and decoded if necessary.
428+
*
429+
* If \c freesrc is non-zero, the data source gets automatically closed and
430+
* freed before the function returns.
431+
*
432+
* Supported are RIFF WAVE files with the formats PCM (8, 16, 24, and 32 bits),
433+
* IEEE Float (32 bits), Microsoft ADPCM and IMA ADPCM (4 bits), and A-law and
434+
* µ-law (8 bits). Other formats are currently unsupported and cause an error.
435+
*
436+
* If this function succeeds, the pointer returned by it is equal to \c spec
437+
* and the pointer to the audio data allocated by the function is written to
438+
* \c audio_buf and its length in bytes to \c audio_len. The \ref SDL_AudioSpec
439+
* members \c freq, \c channels, and \c format are set to the values of the
440+
* audio data in the buffer. The \c samples member is set to a sane default and
441+
* all others are set to zero.
442+
*
443+
* It's necessary to use SDL_FreeWAV() to free the audio data returned in
444+
* \c audio_buf when it is no longer used.
445+
*
446+
* Because of the underspecification of the Waveform format, there are many
447+
* problematic files in the wild that cause issues with strict decoders. To
448+
* provide compatibility with these files, this decoder is lenient in regards
449+
* to the truncation of the file, the fact chunk, and the size of the RIFF
450+
* chunk. The hints SDL_HINT_WAVE_RIFF_CHUNK_SIZE, SDL_HINT_WAVE_TRUNCATION,
451+
* and SDL_HINT_WAVE_FACT_CHUNK can be used to tune the behavior of the
452+
* loading process.
453+
*
454+
* Any file that is invalid (due to truncation, corruption, or wrong values in
455+
* the headers), too big, or unsupported causes an error. Additionally, any
456+
* critical I/O error from the data source will terminate the loading process
457+
* with an error. The function returns NULL on error and in all cases (with the
458+
* exception of \c src being NULL), an appropriate error message will be set.
459+
*
460+
* It is required that the data source supports seeking.
461+
*
462+
* Example:
426463
* \code
427464
* SDL_LoadWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, ...);
428465
* \endcode
429466
*
430-
* If this function succeeds, it returns the given SDL_AudioSpec,
431-
* filled with the audio data format of the wave data, and sets
432-
* \c *audio_buf to a malloc()'d buffer containing the audio data,
433-
* and sets \c *audio_len to the length of that audio buffer, in bytes.
434-
* You need to free the audio buffer with SDL_FreeWAV() when you are
435-
* done with it.
436-
*
437-
* This function returns NULL and sets the SDL error message if the
438-
* wave file cannot be opened, uses an unknown data format, or is
439-
* corrupt. Currently raw and MS-ADPCM WAVE files are supported.
467+
* \param src The data source with the WAVE data
468+
* \param freesrc A integer value that makes the function close the data source if non-zero
469+
* \param spec A pointer filled with the audio format of the audio data
470+
* \param audio_buf A pointer filled with the audio data allocated by the function
471+
* \param audio_len A pointer filled with the length of the audio data buffer in bytes
472+
* \return NULL on error, or non-NULL on success.
440473
*/
441474
extern DECLSPEC SDL_AudioSpec *SDLCALL SDL_LoadWAV_RW(SDL_RWops * src,
442475
int freesrc,

include/SDL_hints.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,70 @@ extern "C" {
11211121

11221122

11231123

1124+
/**
1125+
* \brief Controls how the size of the RIFF chunk affects the loading of a WAVE file.
1126+
*
1127+
* The size of the RIFF chunk (which includes all the sub-chunks of the WAVE
1128+
* file) is not always reliable. In case the size is wrong, it's possible to
1129+
* just ignore it and step through the chunks until a fixed limit is reached.
1130+
*
1131+
* Note that files that have trailing data unrelated to the WAVE file or
1132+
* corrupt files may slow down the loading process without a reliable boundary.
1133+
* By default, SDL stops after 10000 chunks to prevent wasting time. Use the
1134+
* environment variable SDL_WAVE_CHUNK_LIMIT to adjust this value.
1135+
*
1136+
* This variable can be set to the following values:
1137+
*
1138+
* "chunksearch" - Use the RIFF chunk size as a boundary for the chunk search
1139+
* "ignorezero" - Like "chunksearch", but a zero size searches up to 4 GiB (default)
1140+
* "ignore" - Ignore the RIFF chunk size and always search up to 4 GiB
1141+
* "maximum" - Search for chunks until the end of file (not recommended)
1142+
*/
1143+
#define SDL_HINT_WAVE_RIFF_CHUNK_SIZE "SDL_WAVE_RIFF_CHUNK_SIZE"
1144+
1145+
/**
1146+
* \brief Controls how a truncated WAVE file is handled.
1147+
*
1148+
* A WAVE file is considered truncated if any of the chunks are incomplete or
1149+
* the data chunk size is not a multiple of the block size. By default, SDL
1150+
* decodes until the first incomplete block, as most applications seem to do.
1151+
*
1152+
* This variable can be set to the following values:
1153+
*
1154+
* "verystrict" - Raise an error if the file is truncated
1155+
* "strict" - Like "verystrict", but the size of the RIFF chunk is ignored
1156+
* "dropframe" - Decode until the first incomplete sample frame
1157+
* "dropblock" - Decode until the first incomplete block (default)
1158+
*/
1159+
#define SDL_HINT_WAVE_TRUNCATION "SDL_WAVE_TRUNCATION"
1160+
1161+
/**
1162+
* \brief Controls how the fact chunk affects the loading of a WAVE file.
1163+
*
1164+
* The fact chunk stores information about the number of samples of a WAVE
1165+
* file. The Standards Update from Microsoft notes that this value can be used
1166+
* to 'determine the length of the data in seconds'. This is especially useful
1167+
* for compressed formats (for which this is a mandatory chunk) if they produce
1168+
* multiple sample frames per block and truncating the block is not allowed.
1169+
* The fact chunk can exactly specify how many sample frames there should be
1170+
* in this case.
1171+
*
1172+
* Unfortunately, most application seem to ignore the fact chunk and so SDL
1173+
* ignores it by default as well.
1174+
*
1175+
* This variable can be set to the following values:
1176+
*
1177+
* "truncate" - Use the number of samples to truncate the wave data if
1178+
* the fact chunk is present and valid
1179+
* "strict" - Like "truncate", but raise an error if the fact chunk
1180+
* is invalid, not present for non-PCM formats, or if the
1181+
* data chunk doesn't have that many samples
1182+
* "ignorezero" - Like "truncate", but ignore fact chunk if the number of
1183+
* samples is zero
1184+
* "ignore" - Ignore fact chunk entirely (default)
1185+
*/
1186+
#define SDL_HINT_WAVE_FACT_CHUNK "SDL_WAVE_FACT_CHUNK"
1187+
11241188
/**
11251189
* \brief An enumeration of hint priorities
11261190
*/

0 commit comments

Comments
 (0)