Skip to content

RemoveUnusedModuleElements: Handle null table segments#8884

Open
kripken wants to merge 17 commits into
WebAssembly:mainfrom
kripken:rume.null
Open

RemoveUnusedModuleElements: Handle null table segments#8884
kripken wants to merge 17 commits into
WebAssembly:mainfrom
kripken:rume.null

Conversation

@kripken

@kripken kripken commented Jul 6, 2026

Copy link
Copy Markdown
Member

When an elem segment has a null, we must keep it around as it may be
needed to cause the program to trap (if it overwrites a function that would
not have trapped, had it been called).

@kripken kripken requested a review from a team as a code owner July 6, 2026 21:51
@kripken kripken requested review from aheejin and removed request for a team July 6, 2026 21:51

@aheejin aheejin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if this applies to wasm-split too? We recently started to splitting element segments, so...

Comment on lines +480 to +486
} else {
// A related situation is a table with element segments that write
// nulls: they might overwrite a function, causing a trap, if traps can
// happen, so we must preserve such element segments.
for (auto& elem : info.typeElems[type.getBottom()]) {
reference({ModuleElementKind::ElementSegment, elem});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can't this happen even if table->init exists? Should this be in else branch?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It can, but the first loop is in allElems, and the second on typeElems of a specific type. So the first loop does more, and if we do it, we don't need the second. I'll add a comment.

Comment on lines +292 to +295
// need to read one place. Bottom types are stored without subtyping,
// however: adding a null to all its supertypes would mean adding it to all
// function types, which is wasteful and adds no information; as a result,
// we need to check for nulls specifically and not rely on subtyping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think I can get why we can't remove null segments, but I'm not sure how that's related to bottom types and subtyping. The test doesn't have anything related either. Is this two different PRs together?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sorry, this just uses the fact that nulls always have the bottom type. And nothing else (no function) has that bottom type.

So when we see a null, we just use that type to note it in the data. When we see a function, we not only note it, but also its supertypes - but here, we don't do that, for the reasons in the comment (that the supertypes of the bottom type are literally all the function types).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(This might be less confusing if we called "bottom type" instead "null type", which I personally prefer)

@kripken

kripken commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

I wonder if this applies to wasm-split too? We recently started to splitting element segments, so...

Hmm, I'm not familiar enough with wasm-split, but if it tracks values in tables then it needs to track nulls too (if the nulls could have an effect like overwriting).

@kripken kripken marked this pull request as draft July 7, 2026 17:31
@kripken

kripken commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

Fuzzer found another bug related to this one - let me see if I can fix both together, in a nice way.

@kripken

kripken commented Jul 8, 2026

Copy link
Copy Markdown
Member Author

Ok, sorry for the churn, but this is now in a final state. The problem here is more general: not only nulls can be written, causing traps, but also functions of the wrong kind. We have to keep such writes around in both cases, if they can matter. That means that if one segment tramples another, we must be careful what we remove: if one segment wrote something that does not trap, then later, writing something that does trap will change the behavior (without that first segment writing something valid, we trap anyhow).

To handle this, check for segment overlap. If there is any, keep segments around, it traps can happen. This is simple, and while not optimal, it seems good enough: many users use traps-never-happen anyhow, and in general, overlapping segments is very rare (compilers should not be emitting them).

@kripken kripken marked this pull request as ready for review July 8, 2026 18:07
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