Skip to content

LegalizeJSInterface: Pick unique names for generated stubs - #9276

Open
akshat-lakhera wants to merge 2 commits into
WebAssembly:mainfrom
akshat-lakhera:fix/9243-legalize-name-collision
Open

akshat-lakhera wants to merge 2 commits into
WebAssembly:mainfrom
akshat-lakhera:fix/9243-legalize-name-collision

Conversation

@akshat-lakhera

Copy link
Copy Markdown

Fixes #9243

LegalizeJSInterface could read freed memory when the module already contained a function with the name it wanted to give a generated stub.

Cause

For each illegal import, makeLegalStubForCalledImport builds two functions, a stub named legalfunc$ plus the import name and an import named legalimport$ plus the import name, and returns a raw pointer to the stub. The caller stores that pointer in illegalImportsToLegal, and Fixer::visitCall later uses it to rewrite calls.

Both functions were only added to the module when no function with that name already existed. When one did exist (in the issue, a user function called $legalfunc$imp), the stub was not added, so its unique_ptr freed it when the function returned. The pointer saved in illegalImportsToLegal then pointed at freed memory, and Fixer::visitCall read it. That's the heap-use-after-free in the report, and with a Release build the repro segfaults.

Fix

The stub and the legalimport function now get their names from Names::getValidFunctionName, which returns the requested name if it's free and otherwise adds a numeric suffix. Since the names are now always unique, both functions are always added and the "only if absent" checks are gone, so the stored pointer always refers to a function owned by the module. makeLegalStubForCalledImport is called once per illegal import, so this doesn't create duplicate stubs. Modules without a name collision get the same names as before.

Test

I added test/lit/passes/legalize-js-interface-name-collision.wast, based on the repro from the issue plus a colliding $legalimport$imp, with CHECK lines generated by scripts/update_lit_checks.py. With the fix the output uses $legalfunc$imp_4 and $legalimport$imp_4 and the pass completes, while on main the same input crashes wasm-opt. The existing legalize-js-interface lit tests still pass, and git clang-format is clean.

Signed-off-by: Akshat Lakhera <akshatlnct2024@gmail.com>
@akshat-lakhera
akshat-lakhera requested a review from a team as a code owner October 19, 2026 09:22
@akshat-lakhera
akshat-lakhera requested review from aheejin and removed request for a team October 19, 2026 09:22

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request prevents name collisions in the LegalizeJSInterface pass by using Names::getValidFunctionName to generate unique names for legal imports and stubs, and adds a test case to verify this behavior. Feedback on the changes suggests a minor refactoring to directly use the return value of module->addFunction when adding the stub function, which is cleaner and more idiomatic than obtaining the raw pointer before moving the unique pointer.

Comment thread src/passes/LegalizeJSInterface.cpp Outdated
Comment on lines 314 to 317
auto* stubPtr = stub.get();
if (!module->getFunctionOrNull(stub->name)) {
module->addFunction(std::move(stub));
}
if (!module->getFunctionOrNull(legalIm->name)) {
module->addFunction(std::move(legalIm));
}
module->addFunction(std::move(stub));
module->addFunction(std::move(legalIm));
return stubPtr;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Instead of obtaining the raw pointer from the unique_ptr before moving it, we can directly use the return value of module->addFunction, which returns the raw pointer to the added function. This is cleaner and more idiomatic.

    auto* stubPtr = module->addFunction(std::move(stub));
    module->addFunction(std::move(legalIm));
    return stubPtr;

@akshat-lakhera

Copy link
Copy Markdown
Author

cc @kripken @tlively for review when you get a chance.

Signed-off-by: Akshat Lakhera <akshatlnct2024@gmail.com>
@tlively
tlively enabled auto-merge (squash) October 19, 2026 19:13

This branch has not been deployed

No deployments
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.

LegalizeJSInterface: heap-use-after-free in Fixer::visitCall when a function named $legalfunc$<import> already exists

1 participant