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

Avoid sorting predicates by DefId #83074

Merged
merged 2 commits into from
Mar 15, 2021
Merged

Conversation

Aaron1011
Copy link
Member

@Aaron1011 Aaron1011 commented Mar 13, 2021

Fixes issue #82920

Even if an item does not change between compilation sessions, it may end
up with a different DefId, since inserting/deleting an item affects
the DefIds of all subsequent items. Therefore, we use a DefPathHash
in the incremental compilation system, which is stable in the face of
changes to unrelated items.

In particular, the query system will consider the inputs to a query to
be unchanged if any DefIds in the inputs have their DefPathHashes
unchanged. Queries are pure functions, so the query result should be
unchanged if the query inputs are unchanged.

Unfortunately, it's possible to inadvertantly make a query result
incorrectly change across compilations, by relying on the specific value
of a DefId. Specifically, if the query result is a slice that gets
sorted by DefId, the precise order will depend on how the DefIds got
assigned in a particular compilation session. If some definitions end up
with different DefIds (but the same DefPathHashes) in a subsequent
compilation session, we will end up re-computing a different value for
the query, even though the query system expects the result to unchanged
due to the unchanged inputs.

It turns out that we have been sorting the predicates computed during
astconv by their DefId. These predicates make their way into the
super_predicates_that_define_assoc_type, which ends up getting used to
compute the vtables of trait objects. This, re-ordering these predicates
between compilation sessions can lead to undefined behavior at runtime -
the query system will re-use code built with a differently ordered
vtable, resulting in the wrong method being invoked at runtime.

This PR avoids sorting by DefId in astconv, fixing the
miscompilation. However, it's possible that other instances of this
issue exist - they could also be easily introduced in the future.

To fully fix this issue, we should

  1. Turn on -Z incremental-verify-ich by default. This will cause the
    compiler to ICE whenver an 'unchanged' query result changes between
    compilation sessions, instead of causing a miscompilation.
  2. Remove the Ord impls for CrateNum and DefId. This will make it
    difficult to introduce ICEs in the first place.

@rust-highfive
Copy link
Collaborator

r? @oli-obk

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 13, 2021
@rust-log-analyzer

This comment has been minimized.

@@ -942,7 +942,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut bounds = Bounds::default();

self.add_bounds(param_ty, ast_bounds, &mut bounds);
bounds.trait_bounds.sort_by_key(|(t, _, _)| t.def_id());
Copy link
Member Author

Choose a reason for hiding this comment

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

This code appears to date back to 2014 from PR #15985:

param_bounds.trait_bounds.sort_by(|a,b| a.def_id.cmp(&b.def_id));

@oli-obk
Copy link
Contributor

oli-obk commented Mar 13, 2021

Thanks for the detailed analysis. I think we should merge this PR quickly, without addressing the two additional protection mechanisms you mentioned. These can then get added to the issue and be addressed independently

@jackh726
Copy link
Member

I'm a little surprised that removing the sorting of auto traits does anything, given that we sort the predicates later: v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));. For the bounds, I wonder if it's worth doing a stable sort still, anyways?

CI looks good, so r=me assuming my comments above don't spark any thoughts/concerns.

@Aaron1011
Copy link
Member Author

I'm a little surprised that removing the sorting of auto traits does anything, given that we sort the predicates later: v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));

Yeah, I'm not quite sure what's going on there. However, this fixes the current miscompilation - once we start verifying the hashes, we'll find out if that sorting has any impact.

For the bounds, I wonder if it's worth doing a stable sort still, anyways?

I tried this in #83005, but it ended up making the UI test output dependent on whether debuginfo/debug-assertions were enabled in the config.toml.

@Aaron1011
Copy link
Member Author

@bors r=jackh726

@bors
Copy link
Contributor

bors commented Mar 13, 2021

📌 Commit 4c0ba8cc0f9f76a95706d6bd5f7dd34ba3cf583d has been approved by jackh726

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 13, 2021
@Aaron1011
Copy link
Member Author

I've added a regression test.

@bors r=jackh726

@bors
Copy link
Contributor

bors commented Mar 13, 2021

📌 Commit 50bd1e9cb1dff6985330b6a24f6b542d771134ac has been approved by jackh726

@rust-log-analyzer

This comment has been minimized.

Fixes issue rust-lang#82920

Even if an item does not change between compilation sessions, it may end
up with a different `DefId`, since inserting/deleting an item affects
the `DefId`s of all subsequent items. Therefore, we use a `DefPathHash`
in the incremental compilation system, which is stable in the face of
changes to unrelated items.

In particular, the query system will consider the inputs to a query to
be unchanged if any `DefId`s in the inputs have their `DefPathHash`es
unchanged. Queries are pure functions, so the query result should be
unchanged if the query inputs are unchanged.

Unfortunately, it's possible to inadvertantly make a query result
incorrectly change across compilations, by relying on the specific value
of a `DefId`. Specifically, if the query result is a slice that gets
sorted by `DefId`, the precise order will depend on how the `DefId`s got
assigned in a particular compilation session. If some definitions end up
with different `DefId`s (but the same `DefPathHash`es) in a subsequent
compilation session, we will end up re-computing a *different* value for
the query, even though the query system expects the result to unchanged
due to the unchanged inputs.

It turns out that we have been sorting the predicates computed during
`astconv` by their `DefId`. These predicates make their way into the
`super_predicates_that_define_assoc_type`, which ends up getting used to
compute the vtables of trait objects. This, re-ordering these predicates
between compilation sessions can lead to undefined behavior at runtime -
the query system will re-use code built with a *differently ordered*
vtable, resulting in the wrong method being invoked at runtime.

This PR avoids sorting by `DefId` in `astconv`, fixing the
miscompilation. However, it's possible that other instances of this
issue exist - they could also be easily introduced in the future.

To fully fix this issue, we should
1. Turn on `-Z incremental-verify-ich` by default. This will cause the
   compiler to ICE whenver an 'unchanged' query result changes between
   compilation sessions, instead of causing a miscompilation.
2. Remove the `Ord` impls for `CrateNum` and `DefId`. This will make it
   difficult to introduce ICEs in the first place.
@Aaron1011
Copy link
Member Author

@bors r=jackh726

@bors
Copy link
Contributor

bors commented Mar 13, 2021

📌 Commit 06546d4 has been approved by jackh726

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Mar 13, 2021
Avoid sorting predicates by `DefId`

Fixes issue rust-lang#82920

Even if an item does not change between compilation sessions, it may end
up with a different `DefId`, since inserting/deleting an item affects
the `DefId`s of all subsequent items. Therefore, we use a `DefPathHash`
in the incremental compilation system, which is stable in the face of
changes to unrelated items.

In particular, the query system will consider the inputs to a query to
be unchanged if any `DefId`s in the inputs have their `DefPathHash`es
unchanged. Queries are pure functions, so the query result should be
unchanged if the query inputs are unchanged.

Unfortunately, it's possible to inadvertantly make a query result
incorrectly change across compilations, by relying on the specific value
of a `DefId`. Specifically, if the query result is a slice that gets
sorted by `DefId`, the precise order will depend on how the `DefId`s got
assigned in a particular compilation session. If some definitions end up
with different `DefId`s (but the same `DefPathHash`es) in a subsequent
compilation session, we will end up re-computing a *different* value for
the query, even though the query system expects the result to unchanged
due to the unchanged inputs.

It turns out that we have been sorting the predicates computed during
`astconv` by their `DefId`. These predicates make their way into the
`super_predicates_that_define_assoc_type`, which ends up getting used to
compute the vtables of trait objects. This, re-ordering these predicates
between compilation sessions can lead to undefined behavior at runtime -
the query system will re-use code built with a *differently ordered*
vtable, resulting in the wrong method being invoked at runtime.

This PR avoids sorting by `DefId` in `astconv`, fixing the
miscompilation. However, it's possible that other instances of this
issue exist - they could also be easily introduced in the future.

To fully fix this issue, we should
1. Turn on `-Z incremental-verify-ich` by default. This will cause the
   compiler to ICE whenver an 'unchanged' query result changes between
   compilation sessions, instead of causing a miscompilation.
2. Remove the `Ord` impls for `CrateNum` and `DefId`. This will make it
   difficult to introduce ICEs in the first place.
@JohnTitor
Copy link
Member

Heads up: This rollup failure (#83096 (comment)) may be related to this PR. I'm uncertain and not going to r- but it'd be great if someone could check it.

@Aaron1011
Copy link
Member Author

@bors rollup=iffy

@bors
Copy link
Contributor

bors commented Mar 14, 2021

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Mar 14, 2021
@Aaron1011
Copy link
Member Author

@bors r=jackh726

@bors
Copy link
Contributor

bors commented Mar 14, 2021

📌 Commit 94604706122cb3b4ae21b79a66d5e3a5fbe105f8 has been approved by jackh726

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 14, 2021
@bors
Copy link
Contributor

bors commented Mar 14, 2021

⌛ Testing commit 94604706122cb3b4ae21b79a66d5e3a5fbe105f8 with merge 9b3d7d5d8658f518007dd1c485a63d8d14aa1517...

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Contributor

bors commented Mar 14, 2021

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Mar 14, 2021
This is needed to get rustdoc to succeed on `dist-x86_64-linux-alt`
@Aaron1011
Copy link
Member Author

@bors r=jackh726 rollup=never

@bors
Copy link
Contributor

bors commented Mar 15, 2021

📌 Commit 18f8979 has been approved by jackh726

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 15, 2021
@bors
Copy link
Contributor

bors commented Mar 15, 2021

⌛ Testing commit 18f8979 with merge 3963c3d...

@bors
Copy link
Contributor

bors commented Mar 15, 2021

☀️ Test successful - checks-actions
Approved by: jackh726
Pushing 3963c3d to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Mar 15, 2021
@bors bors merged commit 3963c3d into rust-lang:master Mar 15, 2021
@rustbot rustbot added this to the 1.52.0 milestone Mar 15, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 2, 2021
Avoid sorting by DefId for `necessary_variants()`

Follow-up to rust-lang#83074. Originally I tried removing `impl Ord for DefId` but that hit *lots* of errors 😅 so I thought I would start with easy things.

I am not sure whether this could actually cause invalid query results, but this is used from `MarkSymbolVisitor::visit_arm` so it's at least feasible.

r? `@Aaron1011`
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Apr 3, 2021
Avoid sorting by DefId for `necessary_variants()`

Follow-up to rust-lang#83074. Originally I tried removing `impl Ord for DefId` but that hit *lots* of errors 😅 so I thought I would start with easy things.

I am not sure whether this could actually cause invalid query results, but this is used from `MarkSymbolVisitor::visit_arm` so it's at least feasible.

r? ``@Aaron1011``
bors added a commit to rust-lang-ci/rust that referenced this pull request Apr 3, 2021
Avoid sorting by DefId for `necessary_variants()`

Follow-up to rust-lang#83074. Originally I tried removing `impl Ord for DefId` but that hit *lots* of errors 😅 so I thought I would start with easy things.

I am not sure whether this could actually cause invalid query results, but this is used from `MarkSymbolVisitor::visit_arm` so it's at least feasible.

r? `@Aaron1011`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants