Hey, I was reviewing the code and found a couple of issues when reading crafted WAV files.
1. Stack overflow via alloca with large NumChannels
In tinywav_read_f() (line 242):
TW_ALLOC(int16_t, interleaved_data, tw->numChannels*len);
The default build uses alloca, so this ends up as alloca(numChannels * len * 2). The problem is that numChannels comes straight from the WAV file header and isn't validated anywhere in tinywav_open_read().
A crafted WAV file with NumChannels = 65535 causes this to try allocating ~128 MB on the stack when the caller requests even a modest number of frames (e.g. len = 1024). That's an instant crash on basically any system since stacks are typically 1-8 MB.
Even smaller values can be a problem — NumChannels = 500 with len = 1024 is already ~1 MB of stack allocation, which is borderline on many systems (especially embedded).
A simple fix would be to validate NumChannels in tinywav_open_read():
if (tw->h.NumChannels == 0 || tw->h.NumChannels > 64) { // or some reasonable max
tinywav_close_read(tw);
return -1;
}
2. Division by zero with NumChannels = 0
If a WAV file has NumChannels = 0 in the header, there are two division-by-zero crashes:
- Line 220:
tw->numFramesInHeader = tw->h.Subchunk2Size / (tw->numChannels * tw->sampFmt);
- Line 244 (in
tinywav_read_f): frames_read_u32 = samples_read / tw->numChannels;
Same fix as above — reject NumChannels = 0 during open.
3. Integer overflow in numChannels * len
tw->numChannels * len is int * int which can silently overflow. With the alloca path, an overflowed-small value means a tiny stack allocation followed by fread trying to fill it with the un-overflowed amount. With the malloc path you'd get a similar mismatch. This is a secondary concern after the first two though.
These only matter when loading untrusted WAV files, but since tinywav is often used in audio tooling that might open user-provided files, figured it was worth mentioning. Happy to send a PR if you'd prefer.
Hey, I was reviewing the code and found a couple of issues when reading crafted WAV files.
1. Stack overflow via alloca with large NumChannels
In
tinywav_read_f()(line 242):The default build uses
alloca, so this ends up asalloca(numChannels * len * 2). The problem is thatnumChannelscomes straight from the WAV file header and isn't validated anywhere intinywav_open_read().A crafted WAV file with
NumChannels = 65535causes this to try allocating ~128 MB on the stack when the caller requests even a modest number of frames (e.g.len = 1024). That's an instant crash on basically any system since stacks are typically 1-8 MB.Even smaller values can be a problem —
NumChannels = 500withlen = 1024is already ~1 MB of stack allocation, which is borderline on many systems (especially embedded).A simple fix would be to validate NumChannels in
tinywav_open_read():2. Division by zero with NumChannels = 0
If a WAV file has
NumChannels = 0in the header, there are two division-by-zero crashes:tw->numFramesInHeader = tw->h.Subchunk2Size / (tw->numChannels * tw->sampFmt);tinywav_read_f):frames_read_u32 = samples_read / tw->numChannels;Same fix as above — reject NumChannels = 0 during open.
3. Integer overflow in numChannels * len
tw->numChannels * lenisint * intwhich can silently overflow. With the alloca path, an overflowed-small value means a tiny stack allocation followed by fread trying to fill it with the un-overflowed amount. With the malloc path you'd get a similar mismatch. This is a secondary concern after the first two though.These only matter when loading untrusted WAV files, but since tinywav is often used in audio tooling that might open user-provided files, figured it was worth mentioning. Happy to send a PR if you'd prefer.