Skip to content

Commit f757eb1

Browse files
authored
Merge pull request #3524 from Ghabry/transition-crash
Transition: Fix crash for "Random Block" (regression)
2 parents 1560cf4 + 7077f96 commit f757eb1

2 files changed

Lines changed: 103 additions & 40 deletions

File tree

src/external/dr_wav.h

Lines changed: 85 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
3-
dr_wav - v0.14.0 - 2025-07-23
3+
dr_wav - v0.14.5 - 2026-03-03
44
55
David Reid - mackron@gmail.com
66
@@ -147,7 +147,7 @@ extern "C" {
147147

148148
#define DRWAV_VERSION_MAJOR 0
149149
#define DRWAV_VERSION_MINOR 14
150-
#define DRWAV_VERSION_REVISION 0
150+
#define DRWAV_VERSION_REVISION 5
151151
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
152152

153153
#include <stddef.h> /* For size_t. */
@@ -2193,6 +2193,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
21932193

21942194
if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
21952195
drwav_uint32 iSampleLoop;
2196+
drwav_uint32 loopCount;
2197+
drwav_uint32 calculatedLoopCount;
2198+
2199+
/*
2200+
When we calcualted the amount of memory required for the "smpl" chunk we excluded the chunk entirely
2201+
if the loop count in the header did not match with the calculated count based on the size of the
2202+
chunk. When this happens, the second stage will still hit this path but the `pMetadata` will be
2203+
non-null, but will either be pointing at the very end of the allocation or at the start of another
2204+
chunk. We need to check the loop counts for consistency *before* dereferencing the pMetadata object
2205+
so it's consistent with how we do it in the first stage.
2206+
*/
2207+
loopCount = drwav_bytes_to_u32(smplHeaderData + 28);
2208+
calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
2209+
if (loopCount != calculatedLoopCount) {
2210+
return totalBytesRead;
2211+
}
21962212

21972213
pMetadata->type = drwav_metadata_type_smpl;
21982214
pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
@@ -2209,7 +2225,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
22092225
The loop count needs to be validated against the size of the chunk for safety so we don't
22102226
attempt to read over the boundary of the chunk.
22112227
*/
2212-
if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
2228+
if (pMetadata->data.smpl.sampleLoopCount == calculatedLoopCount) {
22132229
pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
22142230

22152231
for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
@@ -2234,6 +2250,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars
22342250

22352251
drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
22362252
}
2253+
} else {
2254+
/*
2255+
Getting here means the loop count in the header does not match up with the size of the
2256+
chunk. Clear out the data to zero just to be safe.
2257+
2258+
This should never actually get hit because we check for it above, but keeping this here
2259+
for added safety.
2260+
*/
2261+
DRWAV_ZERO_OBJECT(&pMetadata->data.smpl);
22372262
}
22382263
}
22392264

@@ -5289,7 +5314,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pC
52895314
DRWAV_ASSERT(pFileStdio != NULL);
52905315
DRWAV_ASSERT(pCursor != NULL);
52915316

5292-
#if defined(_WIN32)
5317+
#if defined(_WIN32) && !defined(NXDK)
52935318
#if defined(_MSC_VER) && _MSC_VER > 1200
52945319
result = _ftelli64(pFileStdio);
52955320
#else
@@ -5496,8 +5521,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, dr
54965521

54975522
DRWAV_ASSERT(pWav != NULL);
54985523

5499-
newCursor = pWav->memoryStream.currentReadPos;
5500-
55015524
if (origin == DRWAV_SEEK_SET) {
55025525
newCursor = 0;
55035526
} else if (origin == DRWAV_SEEK_CUR) {
@@ -5570,8 +5593,6 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
55705593

55715594
DRWAV_ASSERT(pWav != NULL);
55725595

5573-
newCursor = pWav->memoryStreamWrite.currentWritePos;
5574-
55755596
if (origin == DRWAV_SEEK_SET) {
55765597
newCursor = 0;
55775598
} else if (origin == DRWAV_SEEK_CUR) {
@@ -5580,7 +5601,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs
55805601
newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
55815602
} else {
55825603
DRWAV_ASSERT(!"Invalid seek origin");
5583-
return DRWAV_INVALID_ARGS;
5604+
return DRWAV_FALSE;
55845605
}
55855606

55865607
newCursor += offset;
@@ -6296,7 +6317,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
62966317
pWav->msadpcm.cachedFrameCount = 2;
62976318

62986319
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
6299-
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) {
6320+
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
63006321
return totalFramesRead; /* Invalid file. */
63016322
}
63026323
} else {
@@ -6323,7 +6344,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
63236344
pWav->msadpcm.cachedFrameCount = 2;
63246345

63256346
/* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
6326-
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
6347+
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table) ||
6348+
pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
63276349
return totalFramesRead; /* Invalid file. */
63286350
}
63296351
}
@@ -6377,14 +6399,16 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
63776399
drwav_int32 newSample0;
63786400
drwav_int32 newSample1;
63796401

6402+
/* The predictor is read from the file and then indexed into a table. Check that it's in bounds. */
6403+
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
6404+
return totalFramesRead;
6405+
}
6406+
63806407
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
63816408
newSample0 += nibble0 * pWav->msadpcm.delta[0];
63826409
newSample0 = drwav_clamp(newSample0, -32768, 32767);
63836410

6384-
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
6385-
if (pWav->msadpcm.delta[0] < 16) {
6386-
pWav->msadpcm.delta[0] = 16;
6387-
}
6411+
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
63886412

63896413
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
63906414
pWav->msadpcm.prevFrames[0][1] = newSample0;
@@ -6394,15 +6418,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
63946418
newSample1 += nibble1 * pWav->msadpcm.delta[0];
63956419
newSample1 = drwav_clamp(newSample1, -32768, 32767);
63966420

6397-
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
6398-
if (pWav->msadpcm.delta[0] < 16) {
6399-
pWav->msadpcm.delta[0] = 16;
6400-
}
6421+
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
64016422

64026423
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
64036424
pWav->msadpcm.prevFrames[0][1] = newSample1;
64046425

6405-
64066426
pWav->msadpcm.cachedFrames[2] = newSample0;
64076427
pWav->msadpcm.cachedFrames[3] = newSample1;
64086428
pWav->msadpcm.cachedFrameCount = 2;
@@ -6412,28 +6432,30 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav
64126432
drwav_int32 newSample1;
64136433

64146434
/* Left. */
6435+
if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= drwav_countof(coeff2Table)) {
6436+
return totalFramesRead; /* Out of bounds. Invalid file. */
6437+
}
6438+
64156439
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
64166440
newSample0 += nibble0 * pWav->msadpcm.delta[0];
64176441
newSample0 = drwav_clamp(newSample0, -32768, 32767);
64186442

6419-
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
6420-
if (pWav->msadpcm.delta[0] < 16) {
6421-
pWav->msadpcm.delta[0] = 16;
6422-
}
6443+
pWav->msadpcm.delta[0] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8, 16, 0x7FFFFFFF);
64236444

64246445
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
64256446
pWav->msadpcm.prevFrames[0][1] = newSample0;
64266447

64276448

64286449
/* Right. */
6450+
if (pWav->msadpcm.predictor[1] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
6451+
return totalFramesRead; /* Out of bounds. Invalid file. */
6452+
}
6453+
64296454
newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
64306455
newSample1 += nibble1 * pWav->msadpcm.delta[1];
64316456
newSample1 = drwav_clamp(newSample1, -32768, 32767);
64326457

6433-
pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
6434-
if (pWav->msadpcm.delta[1] < 16) {
6435-
pWav->msadpcm.delta[1] = 16;
6436-
}
6458+
pWav->msadpcm.delta[1] = (drwav_int32)drwav_clamp(((drwav_int64)adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8, 16, 0x7FFFFFFF);
64376459

64386460
pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
64396461
pWav->msadpcm.prevFrames[1][1] = newSample1;
@@ -8057,6 +8079,12 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns
80578079

80588080
DRWAV_ASSERT(pWav != NULL);
80598081

8082+
/* Check for overflow before multiplication. */
8083+
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int16)) {
8084+
drwav_uninit(pWav);
8085+
return NULL; /* Overflow or invalid channels. */
8086+
}
8087+
80608088
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
80618089
if (sampleDataSize > DRWAV_SIZE_MAX) {
80628090
drwav_uninit(pWav);
@@ -8099,6 +8127,12 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned
80998127

81008128
DRWAV_ASSERT(pWav != NULL);
81018129

8130+
/* Check for overflow before multiplication. */
8131+
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(float)) {
8132+
drwav_uninit(pWav);
8133+
return NULL; /* Overflow or invalid channels. */
8134+
}
8135+
81028136
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
81038137
if (sampleDataSize > DRWAV_SIZE_MAX) {
81048138
drwav_uninit(pWav);
@@ -8141,6 +8175,12 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns
81418175

81428176
DRWAV_ASSERT(pWav != NULL);
81438177

8178+
/* Check for overflow before multiplication. */
8179+
if (pWav->channels == 0 || pWav->totalPCMFrameCount > DRWAV_SIZE_MAX / pWav->channels / sizeof(drwav_int32)) {
8180+
drwav_uninit(pWav);
8181+
return NULL; /* Overflow or invalid channels. */
8182+
}
8183+
81448184
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
81458185
if (sampleDataSize > DRWAV_SIZE_MAX) {
81468186
drwav_uninit(pWav);
@@ -8521,6 +8561,23 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
85218561
/*
85228562
REVISION HISTORY
85238563
================
8564+
v0.14.5 - 2026-03-03
8565+
- Fix a crash when loading files with a malformed "smpl" chunk.
8566+
- Fix a signed overflow bug with the MS-ADPCM decoder.
8567+
8568+
v0.14.4 - 2026-01-17
8569+
- Fix some compilation warnings.
8570+
8571+
v0.14.3 - 2025-12-14
8572+
- Fix a possible out-of-bounds read when reading from MS-ADPCM encoded files.
8573+
- Fix a possible integer overflow error.
8574+
8575+
v0.14.2 - 2025-12-02
8576+
- Fix a compilation warning.
8577+
8578+
v0.14.1 - 2025-09-10
8579+
- Fix an error with the NXDK build.
8580+
85248581
v0.14.0 - 2025-07-23
85258582
- API CHANGE: Seek origin enums have been renamed to the following:
85268583
- drwav_seek_origin_start -> DRWAV_SEEK_SET

src/transition.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,24 +124,24 @@ void Transition::Init(Type type, Scene *linked_scene, int duration, bool next_er
124124
void Transition::SetAttributesTransitions() {
125125
int w, h, beg_i, mid_i, end_i, length;
126126

127-
zoom_position = std::vector<int>(2);
128-
random_blocks = std::vector<uint32_t>(Player::screen_width * Player::screen_height / (size_random_blocks * size_random_blocks));
129-
mosaic_random_offset.resize(total_frames);
127+
auto init_random_blocks = [&]() {
128+
random_blocks = std::vector<uint32_t>(Player::screen_width * Player::screen_height / (size_random_blocks * size_random_blocks));
129+
for (uint32_t i = 0; i < random_blocks.size(); i++) {
130+
random_blocks[i] = i;
131+
}
130132

131-
for (uint32_t i = 0; i < random_blocks.size(); i++) {
132-
random_blocks[i] = i;
133-
}
133+
random_block_transition = Bitmap::Create(Player::screen_width, Player::screen_height, true);
134+
current_blocks_print = 0;
135+
};
134136

135137
switch (transition_type) {
136138
case TransitionRandomBlocks:
137-
random_block_transition = Bitmap::Create(Player::screen_width, Player::screen_height, true);
138-
current_blocks_print = 0;
139+
init_random_blocks();
139140
std::shuffle(random_blocks.begin(), random_blocks.end(), Rand::GetRNG());
140141
break;
141142
case TransitionRandomBlocksDown:
142143
case TransitionRandomBlocksUp:
143-
random_block_transition = Bitmap::Create(Player::screen_width, Player::screen_height, true);
144-
current_blocks_print = 0;
144+
init_random_blocks();
145145
if (transition_type == TransitionRandomBlocksUp) { std::reverse(random_blocks.begin(), random_blocks.end()); }
146146

147147
w = Player::screen_width / 4;
@@ -161,6 +161,8 @@ void Transition::SetAttributesTransitions() {
161161
break;
162162
case TransitionZoomIn:
163163
case TransitionZoomOut:
164+
zoom_position = std::vector<int>(2);
165+
164166
if (scene != nullptr && scene->type == Scene::Map) {
165167
auto map = static_cast<Scene_Map*>(scene);
166168

@@ -174,6 +176,8 @@ void Transition::SetAttributesTransitions() {
174176
break;
175177
case TransitionMosaicIn:
176178
case TransitionMosaicOut:
179+
mosaic_random_offset.resize(total_frames);
180+
177181
for (int i = 0; i < total_frames; ++i) {
178182
// by default i 0..40 for scale 1..41
179183
mosaic_random_offset[i] = Rand::GetRandomNumber(0, i);
@@ -204,7 +208,7 @@ void Transition::Draw(Bitmap& dst) {
204208
dst.BlendBlit(0, 0, *screen1, screen1->GetRect(), color, 255);
205209
return;
206210
}
207-
211+
208212
int tf_off = total_frames - 1;
209213

210214
switch (transition_type) {
@@ -218,7 +222,9 @@ void Transition::Draw(Bitmap& dst) {
218222
case TransitionRandomBlocksUp:
219223
blocks_to_print = random_blocks.size() * (current_frame + 1) / tf_off;
220224

221-
for (uint32_t i = current_blocks_print; i < blocks_to_print; i++) {
225+
// Off-by-one error?
226+
// i < random_blocks.size() prevents out of bounds access on the final frame
227+
for (uint32_t i = current_blocks_print; i < blocks_to_print && i < random_blocks.size(); i++) {
222228
random_block_transition->Blit(random_blocks[i] % (w / size_random_blocks) * size_random_blocks,
223229
random_blocks[i] / (w / size_random_blocks) * size_random_blocks, *screen2,
224230
Rect(random_blocks[i] % (w / size_random_blocks) * size_random_blocks, random_blocks[i] / (w / size_random_blocks) * size_random_blocks,

0 commit comments

Comments
 (0)