Skip to content

Fix sign extension of i32 addresses in interpreter memory access#8348

Merged
kripken merged 1 commit intoWebAssembly:mainfrom
sumleo:fix-interpreter-address-signext
Feb 25, 2026
Merged

Fix sign extension of i32 addresses in interpreter memory access#8348
kripken merged 1 commit intoWebAssembly:mainfrom
sumleo:fix-interpreter-address-signext

Conversation

@sumleo
Copy link
Contributor

@sumleo sumleo commented Feb 20, 2026

Summary

  • Fix implicit sign extension of i32 addresses in getFinalAddress and getFinalAddressWithoutOffset in the interpreter
  • ptr.geti32() returns int32_t, which gets sign-extended to int64_t via C++ ternary promotion rules before being stored as uint64_t
  • For i32 addresses >= 0x80000000, this produces incorrect 64-bit values (e.g., 0xFFFFFFFF80000000 instead of 0x80000000), causing spurious out-of-bounds traps
  • Fix by casting through uint32_t first to zero-extend instead of sign-extend

Test plan

  • All 309 unit tests pass (binaryen-unittests)
  • Build succeeds with no warnings

size_t indexVal = index.getSingleValue().getUnsigned();
if (indexVal >= data->values.size()) {
trap("array oob");
}
Copy link
Member

Choose a reason for hiding this comment

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

This looks like a correct fix, but the title and description of the PR look unrelated?

@sumleo sumleo force-pushed the fix-interpreter-address-signext branch from 73c73bb to 1aaf4f9 Compare February 21, 2026 02:24
@sumleo
Copy link
Contributor Author

sumleo commented Feb 21, 2026

Thanks for catching that! The branch previously contained the wrong change (an array bounds check that was already covered by #8351). I've force-pushed with the actual sign extension fix for getFinalAddress and getFinalAddressWithoutOffset — casting through uint32_t to zero-extend instead of sign-extend.

@sumleo sumleo force-pushed the fix-interpreter-address-signext branch 3 times, most recently from 624d4c7 to c906298 Compare February 23, 2026 01:15
Address memorySizeBytes = memorySize * Memory::kPageSize;
uint64_t addr = ptr.type == Type::i32 ? ptr.geti32() : ptr.geti64();
uint64_t addr = ptr.type == Type::i32 ? (uint64_t)(uint32_t)ptr.geti32()
: (uint64_t)ptr.geti64();
Copy link
Member

Choose a reason for hiding this comment

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

Can this just be getUnsigned()? That returns uint64_t.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done — switched to ptr.getUnsigned() in both getFinalAddress and getFinalAddressWithoutOffset. Much cleaner.

Use ptr.getUnsigned() to properly zero-extend i32 addresses to
uint64_t in getFinalAddress and getFinalAddressWithoutOffset.
Previously, geti32() returned a signed int32_t that sign-extended
when assigned to uint64_t, producing incorrect addresses for
values >= 0x80000000.
@sumleo sumleo force-pushed the fix-interpreter-address-signext branch from c906298 to a760ca0 Compare February 25, 2026 01:12
@kripken kripken merged commit 531702f into WebAssembly:main Feb 25, 2026
17 checks passed
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.

3 participants