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
This commit is contained in:
Sam Lantinga
2019-06-08 19:02:42 -07:00
parent 48ac92af54
commit 990e166a3b
4 changed files with 2178 additions and 577 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -20,11 +20,12 @@
*/
#include "../SDL_internal.h"
/* WAVE files are little-endian */
/* RIFF WAVE files are little-endian */
/*******************************************/
/* Define values for Microsoft WAVE format */
/*******************************************/
/* FOURCC */
#define RIFF 0x46464952 /* "RIFF" */
#define WAVE 0x45564157 /* "WAVE" */
#define FACT 0x74636166 /* "fact" */
@@ -33,45 +34,116 @@
#define JUNK 0x4B4E554A /* "JUNK" */
#define FMT 0x20746D66 /* "fmt " */
#define DATA 0x61746164 /* "data" */
/* Format tags */
#define UNKNOWN_CODE 0x0000
#define PCM_CODE 0x0001
#define MS_ADPCM_CODE 0x0002
#define IEEE_FLOAT_CODE 0x0003
#define ALAW_CODE 0x0006
#define MULAW_CODE 0x0007
#define IMA_ADPCM_CODE 0x0011
#define MP3_CODE 0x0055
#define MPEG_CODE 0x0050
#define MPEGLAYER3_CODE 0x0055
#define EXTENSIBLE_CODE 0xFFFE
#define WAVE_MONO 1
#define WAVE_STEREO 2
/* Normally, these three chunks come consecutively in a WAVE file */
typedef struct WaveFMT
/* Stores the WAVE format information. */
typedef struct WaveFormat
{
/* Not saved in the chunk we read:
Uint32 FMTchunk;
Uint32 fmtlen;
*/
Uint16 encoding;
Uint16 channels; /* 1 = mono, 2 = stereo */
Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */
Uint32 byterate; /* Average bytes per second */
Uint16 blockalign; /* Bytes per sample block */
Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */
} WaveFMT;
Uint16 formattag; /* Raw value of the first field in the fmt chunk data. */
Uint16 encoding; /* Actual encoding, possibly from the extensible header. */
Uint16 channels; /* Number of channels. */
Uint32 frequency; /* Sampling rate in Hz. */
Uint32 byterate; /* Average bytes per second. */
Uint16 blockalign; /* Bytes per block. */
Uint16 bitspersample; /* Currently supported are 8, 16, 24, 32, and 4 for ADPCM. */
/* The general chunk found in the WAVE file */
typedef struct Chunk
{
Uint32 magic;
Uint32 length;
Uint8 *data;
} Chunk;
/* Extra information size. Number of extra bytes starting at byte 18 in the
* fmt chunk data. This is at least 22 for the extensible header.
*/
Uint16 extsize;
typedef struct WaveExtensibleFMT
{
WaveFMT format;
Uint16 size;
Uint16 validbits;
/* Extensible WAVE header fields */
Uint16 validsamplebits;
Uint32 samplesperblock; /* For compressed formats. Can be zero. Actually 16 bits in the header. */
Uint32 channelmask;
Uint8 subformat[16]; /* a GUID. */
} WaveExtensibleFMT;
Uint8 subformat[16]; /* A format GUID. */
} WaveFormat;
/* Stores information on the fact chunk. */
typedef struct WaveFact {
/* Represents the state of the fact chunk in the WAVE file.
* Set to -1 if the fact chunk is invalid.
* Set to 0 if the fact chunk is not present
* Set to 1 if the fact chunk is present and valid.
* Set to 2 if samplelength is going to be used as the number of sample frames.
*/
Sint32 status;
/* Version 1 of the RIFF specification calls the field in the fact chunk
* dwFileSize. The Standards Update then calls it dwSampleLength and specifies
* that it is 'the length of the data in samples'. WAVE files from Windows
* with this chunk have it set to the samples per channel (sample frames).
* This is useful to truncate compressed audio to a specific sample count
* because a compressed block is usually decoded to a fixed number of
* sample frames.
*/
Uint32 samplelength; /* Raw sample length value from the fact chunk. */
} WaveFact;
/* Generic struct for the chunks in the WAVE file. */
typedef struct WaveChunk
{
Uint32 fourcc; /* FOURCC of the chunk. */
Uint32 length; /* Size of the chunk data. */
Sint64 position; /* Position of the data in the stream. */
Uint8 *data; /* When allocated, this points to the chunk data. length is used for the malloc size. */
size_t size; /* Number of bytes in data that could be read from the stream. Can be smaller than length. */
} WaveChunk;
/* Controls how the size of the RIFF chunk affects the loading of a WAVE file. */
typedef enum WaveRiffSizeHint {
RiffSizeNoHint,
RiffSizeChunkSearch,
RiffSizeIgnoreZero,
RiffSizeIgnore,
RiffSizeMaximum,
} WaveRiffSizeHint;
/* Controls how a truncated WAVE file is handled. */
typedef enum WaveTruncationHint {
TruncNoHint,
TruncVeryStrict,
TruncStrict,
TruncDropFrame,
TruncDropBlock,
} WaveTruncationHint;
/* Controls how the fact chunk affects the loading of a WAVE file. */
typedef enum WaveFactChunkHint {
FactNoHint,
FactTruncate,
FactStrict,
FactIgnoreZero,
FactIgnore,
} WaveFactChunkHint;
typedef struct WaveFile
{
WaveChunk chunk;
WaveFormat format;
WaveFact fact;
/* Number of sample frames that will be decoded. Calculated either with the
* size of the data chunk or, if the appropriate hint is enabled, with the
* sample length value from the fact chunk.
*/
Sint64 sampleframes;
void *decoderdata; /* Some decoders require extra data for a state. */
WaveRiffSizeHint riffhint;
WaveTruncationHint trunchint;
WaveFactChunkHint facthint;
} WaveFile;
/* vi: set ts=4 sw=4 expandtab: */