Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasm2c: Fix handling of locals in setjmp targets #2479

Merged
merged 2 commits into from
Oct 2, 2024

Conversation

SoniEx2
Copy link
Contributor

@SoniEx2 SoniEx2 commented Oct 1, 2024

It is UB to read local variables after a call to setjmp returns, if those variables have been modified between setjmp and longjmp, unless they're marked as volatile. This marks them as volatile.

Closes #2469

Split from #2470

@SoniEx2
Copy link
Contributor Author

SoniEx2 commented Oct 1, 2024

@sbc100

@SoniEx2
Copy link
Contributor Author

SoniEx2 commented Oct 1, 2024

(the tests would catch this, despite UBSAN not having a setjmp sanitizer, because CI runs tests on both debug and release builds)

(upstreaming is proving difficult... WebAssembly/exception-handling#332 )

@sbc100
Copy link
Member

sbc100 commented Oct 2, 2024

(the tests would catch this, despite UBSAN not having a setjmp sanitizer, because CI runs tests on both debug and release builds)

I'm not quite sure what you are saying here. Do you mean the new test you are adding would fail without the fix?

(upstreaming is proving difficult... WebAssembly/exception-handling#332 )

if (setjmp_safe) {
PushFuncSection("exceptions");
Write("volatile ");
PushFuncSection();
Copy link
Member

Choose a reason for hiding this comment

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

I assume this means we only output this volatile when exceptions are enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep!

Copy link
Contributor Author

@SoniEx2 SoniEx2 Oct 2, 2024

Choose a reason for hiding this comment

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

also, only when a try block is used in the function. (i.e. only when setjmp is called.)

@SoniEx2
Copy link
Contributor Author

SoniEx2 commented Oct 2, 2024

I'm not quite sure what you are saying here. Do you mean the new test you are adding would fail without the fix?

the test would fail without the fix, yes. like so: (note the -O0 vs -O3)

$ cd /home/soniex2/git/github/wabt && /usr/bin/cmake -E env WASM2C_CC=/usr/bin/clang WASM2C_CFLAGS=\ -fsanitize=undefined\ -fno-sanitize-recover\ -fsanitize-blacklist=/home/soniex2/git/github/wabt/ubsan.blacklist\ -g\ -O0 /usr/bin/python3.12 /home/soniex2/git/github/wabt/test/run-tests.py --bindir /home/soniex2/git/github/wabt/out test/regress/wasm2c-ehv3-setjmp-volatile.txt 
[+1|-0|%100] (0.55s) test/regress/wasm2c-ehv3-setjmp-volatile.txt
$ cd /home/soniex2/git/github/wabt && /usr/bin/cmake -E env WASM2C_CC=/usr/bin/clang WASM2C_CFLAGS=\ -fsanitize=undefined\ -fno-sanitize-recover\ -fsanitize-blacklist=/home/soniex2/git/github/wabt/ubsan.blacklist\ -g\ -O3 /usr/bin/python3.12 /home/soniex2/git/github/wabt/test/run-tests.py --bindir /home/soniex2/git/github/wabt/out test/regress/wasm2c-ehv3-setjmp-volatile.txt 
- test/regress/wasm2c-ehv3-setjmp-volatile.txt
  expected error code 0, got 1.
  STDERR MISMATCH:
  --- expected
  +++ actual
  @@ -0,0 +1 @@
  +wasm2c-ehv3-setjmp-volatile.txt:22: assertion failed: in w2c_wasm2c__ehv3__setjmp__volatile__0__wasm_setjmp0x2Dbait(&wasm2c__ehv3__setjmp__volatile__0__wasm_instance, 0u): expected 1, got 0.
  STDOUT MISMATCH:
  --- expected
  +++ actual
  @@ -1 +1 @@
  -2/2 tests passed.
  +1/2 tests passed.


**** FAILED ******************************************************************
- test/regress/wasm2c-ehv3-setjmp-volatile.txt
    /usr/bin/python3.12 /home/soniex2/git/github/wabt/test/run-spec-wasm2c.py out/test/regress/wasm2c-ehv3-setjmp-volatile.txt --bindir=/home/soniex2/git/github/wabt/out --no-error-cmdline -o out/test/regress/wasm2c-ehv3-setjmp-volatile --enable-exceptions

for (size_t i = 0; i < func_sections_.size(); ++i) {
auto& [condition, stream] = func_sections_.at(i);
std::unique_ptr<OutputBuffer> buf = stream.ReleaseOutputBuffer();
if (condition.empty() || func_includes_.count(condition)) {
stream_->WriteData(buf->data.data(), buf->data.size());
}

if (i == 0) {
if (i == stack_var_section) {
Copy link
Member

Choose a reason for hiding this comment

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

Is the stack_var_section not always the first one in a given function?

Copy link
Contributor Author

@SoniEx2 SoniEx2 Oct 2, 2024

Choose a reason for hiding this comment

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

not with these changes. we need to write volatile or not based on whether the function uses try or not, and we do that by pushing more func sections where relevant. in particular, we save the stack_var_section after writing params and locals (both of which may contain volatile), so we can put the stack vars in the correct place. (also, stack vars don't need volatile as any reused stack slots are overwritten after longjmp.)

@sbc100 sbc100 merged commit 7d229cf into WebAssembly:main Oct 2, 2024
18 checks passed
@SoniEx2 SoniEx2 deleted the fix-wasm2c-ehv3-volatile branch October 2, 2024 01:21
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.

wasm2c: UB in exception-handling
2 participants