Skip to content

Fix: writeI53ToI64 crashes with -sASSERTIONS=2 when num >= 2^32#26410

Open
49iohcy wants to merge 1 commit intoemscripten-core:mainfrom
49iohcy:writeI53ToI64-low-word-mask
Open

Fix: writeI53ToI64 crashes with -sASSERTIONS=2 when num >= 2^32#26410
49iohcy wants to merge 1 commit intoemscripten-core:mainfrom
49iohcy:writeI53ToI64-low-word-mask

Conversation

@49iohcy
Copy link

@49iohcy 49iohcy commented Mar 7, 2026

Hello.

I've recently been working on a WebGPU demo w/ Dawn WebGPU C/C++ implementation. When I tried to get the adapter limit in a node:24-slim devcontainer on my M2 Apple Silicon, I got 4294967296 for the maxBufferSize, which is a valid parameter for writeI53ToI64.

When -sASSERTIONS=2 is set, there's a trailing checkInt32 call in the makeSetValue implementation. Without masking the low word of num, checkInt32 throws in the current implementation of libint53.mjs, which I believe is not the desired behavior.

The following are relevant references for the most up-to-date implementations of Emscripten and Dawn.

https://github.com/google/dawn/blob/89b4626b4e838738227dc410209c4d03410d94a9/third_party/emdawnwebgpu/pkg/webgpu/src/library_webgpu.js#L581

function makeSetValue(ptr, pos, value, type) {
var rtn = makeSetValueImpl(ptr, pos, value, type);
if (ASSERTIONS == 2 && (type.startsWith('i') || type.startsWith('u'))) {
const width = getBitWidth(type);
const assertion = `checkInt${width}(${value})`;
rtn += `;${assertion}`;
}
return rtn;
}

$writeI53ToI64: (ptr, num) => {
{{{ makeSetValue('ptr', 0, 'num', 'u32') }}};
var lower = {{{ makeGetValue('ptr', 0, 'u32') }}};
{{{ makeSetValue('ptr', 4, '(num - lower)/4294967296', 'u32') }}};
#if ASSERTIONS
var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr);
var offset = {{{ getHeapOffset('ptr', 'u32') }}};
if (deserialized != num) warnOnce(`writeI53ToI64() out of range: serialized JS Number ${num} to Wasm heap as bytes lo=${ptrToString(HEAPU32[offset])}, hi=${ptrToString(HEAPU32[offset+1])}, which deserializes back to ${deserialized} instead!`);
#endif
},

…a num ge 4294967296 (u32 max + 1) is passed.
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tracking down!

I guess we are missing a test for this. Perhaps we can expand test/core/test_int53.c to include a test here.

// C/C++ side code to interpret the resulting number as signed or unsigned as is desirable.
$writeI53ToI64: (ptr, num) => {
{{{ makeSetValue('ptr', 0, 'num', 'u32') }}};
{{{ makeSetValue('ptr', 0, 'num&0xFFFFFFFF', 'u32') }}};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we normally write this as num >>> 0

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I'll see what I can do with the test and let you know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants