Skip to content

Commit 05aa29a

Browse files
committed
fetch / fix memory leak inside saveResponseAndStatus when using streaming mode
when streaming was enabled - * onprogress allocated emscripten_fetch_t.data * onload called saveResponseAndStatus * xhr.response was NULL (due to streaming) which caused ptr to remain 0 * emscripten_fetch_t.data was uncondtionally overwritten by ptr, leaking what was allocated during onprogress
1 parent efa2897 commit 05aa29a

2 files changed

Lines changed: 26 additions & 19 deletions

File tree

src/Fetch.js

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -576,21 +576,23 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) {
576576
// This receives a condition, which determines whether to save the xhr's
577577
// response, or just 0.
578578
function saveResponseAndStatus() {
579-
var ptr = 0;
580-
var ptrLen = 0;
581-
if (xhr.response && fetchAttrLoadToMemory && {{{ makeGetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, '*') }}} === 0) {
579+
let ptrLen = 0;
580+
if (fetchAttrLoadToMemory && xhr.response) {
581+
let ptr = {{{ makeGetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, '*') }}};
582582
ptrLen = xhr.response.byteLength;
583-
}
584-
if (ptrLen > 0) {
585-
#if FETCH_DEBUG
586-
dbg(`fetch: allocating ${ptrLen} bytes in Emscripten heap for xhr data`);
587-
#endif
583+
588584
// The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is
589585
// freed when emscripten_fetch_close() is called.
590-
ptr = _realloc({{{ makeGetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, '*') }}}, ptrLen);
586+
if (ptrLen > readI53FromI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.numBytes }}})) {
587+
#if FETCH_DEBUG
588+
dbg(`fetch: allocating ${ptrLen} bytes in Emscripten heap for xhr data`);
589+
#endif
590+
ptr = _realloc(ptr, ptrLen);
591+
{{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, 'ptr', '*') }}}
592+
}
593+
591594
HEAPU8.set(new Uint8Array(/** @type{Array<number>} */(xhr.response)), ptr);
592595
}
593-
{{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, 'ptr', '*') }}}
594596
writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.numBytes }}}, ptrLen);
595597
writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.dataOffset }}}, 0);
596598
var len = xhr.response ? xhr.response.byteLength : 0;
@@ -660,21 +662,27 @@ function fetchXHR(fetch, onsuccess, onerror, onprogress, onreadystatechange) {
660662
if (!Fetch.xhrs.has(id)) {
661663
return;
662664
}
663-
var ptrLen = (fetchAttrLoadToMemory && fetchAttrStreamData && xhr.response) ? xhr.response.byteLength : 0;
664-
var ptr = 0;
665-
if (ptrLen > 0 && fetchAttrLoadToMemory && fetchAttrStreamData) {
666-
#if FETCH_DEBUG
667-
dbg(`fetch: allocating ${ptrLen} bytes in Emscripten heap for xhr data`);
668-
#endif
665+
let ptrLen = 0;
666+
if (fetchAttrLoadToMemory && fetchAttrStreamData && xhr.response) {
667+
let ptr = {{{ makeGetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, '*') }}};
668+
ptrLen = xhr.response.byteLength;
669+
669670
#if ASSERTIONS
670671
assert(onprogress, 'When doing a streaming fetch, you should have an onprogress handler registered to receive the chunks!');
671672
#endif
673+
672674
// The data pointer malloc()ed here has the same lifetime as the emscripten_fetch_t structure itself has, and is
673675
// freed when emscripten_fetch_close() is called.
674-
ptr = _realloc({{{ makeGetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, '*') }}}, ptrLen);
676+
if (ptrLen > readI53FromI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.numBytes }}})) {
677+
#if FETCH_DEBUG
678+
dbg(`fetch: allocating ${ptrLen} bytes in Emscripten heap for xhr data`);
679+
#endif
680+
ptr = _realloc(ptr, ptrLen);
681+
{{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, 'ptr', '*') }}}
682+
}
683+
675684
HEAPU8.set(new Uint8Array(/** @type{Array<number>} */(xhr.response)), ptr);
676685
}
677-
{{{ makeSetValue('fetch', C_STRUCTS.emscripten_fetch_t.data, 'ptr', '*') }}}
678686
writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.numBytes }}}, ptrLen);
679687
writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.dataOffset }}}, e.loaded - ptrLen);
680688
writeI53ToI64(fetch + {{{ C_STRUCTS.emscripten_fetch_t.totalBytes }}}, e.total);

test/fetch/test_fetch_stream_file.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ int main() {
2020
attr.onsuccess = [](emscripten_fetch_t *fetch) {
2121
printf("Finished downloading %llu bytes\n", fetch->totalBytes);
2222
printf("Data checksum: %08X\n", checksum);
23-
assert(fetch->data == 0); // The data was streamed via onprogress, no bytes available here.
2423
assert(fetch->numBytes == 0);
2524
assert(fetch->totalBytes == 134217728);
2625
assert(checksum == 0xA7F8E858U);

0 commit comments

Comments
 (0)