Skip to content

Unsound two-phase borrow accepted by compiler #164163

Open

Description

@jsnell

The following code compiles:

pub fn main() {
      let mut a: Vec<u64> = vec![1];
      let mut b: Vec<u64> = vec![2];
      let mut p: &mut Vec<u64> = &mut a;
      let s: &u64;
      // The receiver `&mut *p` is reserved first. The argument then borrows through `p`, and points `p` elsewhere.
      p.push({
          s = &p[0];
          p = &mut b;
          0
      });
      // `a` has been pushed to, so it may have reallocated. `s` still points into its old buffer.
      for _ in 0..64 { p.push(7); }
      println!("{}", *s);
  }

I expected to see this happen: compilation fails

Instead, this happened: prints random garbage when run

Meta

Happens on nightly, stable, and past stables at least back to 1.85.0 on godbolt:

https://rust.godbolt.org/z/zzsTbsKfo

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 11, 2026
  2. added
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Oct 11, 2026
  3. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 11, 2026
  4. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 11, 2026
  5. theemathas commented on Oct 11, 2026

    @theemathas
    Contributor

    Is this a duplicate of #160598?

  6. jsnell commented on Oct 11, 2026

    @jsnell
    Author

    I wouldn't think so. That bug seems to be about control-flow, which is trivial here, and the actual trigger is re-assignment.

  7. zachs18 commented on Oct 11, 2026

    @zachs18
    Contributor
    2015 edition bisection

    searched toolchains nightly-2018-12-04 through nightly-2022-05-14


    Regression in nightly-2019-04-23


    fetching https://static.rust-lang.org/dist/2019-04-22/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2019-04-22: 40 B / 40 B [===================================================================================================================] 100.00 % 960.03 KB/s converted 2019-04-22 to 31a75a1
    fetching https://static.rust-lang.org/dist/2019-04-23/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2019-04-23: 40 B / 40 B [=====================================================================================================================] 100.00 % 1.12 MB/s converted 2019-04-23 to 6d59933
    looking for regression commit between 2019-04-22 and 2019-04-23
    fetching (via remote github) commits from max(31a75a1, 2019-04-20) to 6d59933
    ending github query because we found starting sha: 31a75a1
    get_commits_between returning commits, len: 6
    commit[0] 2019-04-21: Auto merge of #60124 - petrochenkov:stanomut, r=eddyb
    commit[1] 2019-04-21: Auto merge of #60158 - Xanewok:update-clippy, r=matthiaskrgr
    commit[2] 2019-04-22: Auto merge of #60053 - Xanewok:serde-save-analysis, r=nrc
    commit[3] 2019-04-22: Auto merge of #60133 - phansch:deny_rust_2018_idioms, r=Centril
    commit[4] 2019-04-22: Auto merge of #59114 - matthewjasper:enable-migate-2015, r=pnkfelix
    commit[5] 2019-04-22: Auto merge of #60168 - varkor:tidy-leading-newline, r=alexcrichton
    ERROR: no CI builds available between 31a75a1 and 6d59933 within last 167 days

    bisected with cargo-bisect-rustc cargo bisect-rustc --preserve --start 1.32.0 --end 1.62.0 --regress success on edition 2015

    2018 edition (stable) bisection searched toolchains nightly-2018-06-20 through nightly-2018-12-04

    Regression in nightly-2018-09-10


    fetching https://static.rust-lang.org/dist/2018-09-09/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2018-09-09: 40 B / 40 B [=====================================================================================================================] 100.00 % 1.08 MB/s converted 2018-09-09 to 0198a1e
    fetching https://static.rust-lang.org/dist/2018-09-10/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2018-09-10: 40 B / 40 B [===================================================================================================================] 100.00 % 977.81 KB/s converted 2018-09-10 to 2d4e34c
    looking for regression commit between 2018-09-09 and 2018-09-10
    fetching (via remote github) commits from max(0198a1e, 2018-09-07) to 2d4e34c
    ending github query because we found starting sha: 0198a1e
    get_commits_between returning commits, len: 8
    commit[0] 2018-09-08: Auto merge of #53909 - mikhail-m1:53643, r=nikomatsakis
    commit[1] 2018-09-09: Auto merge of #53949 - estebank:unclosed-delim, r=nikomatsakis
    commit[2] 2018-09-09: Auto merge of #53960 - estebank:issue-51303, r=nagisa
    commit[3] 2018-09-09: Auto merge of #53988 - eddyb:issue-53770, r=petrochenkov
    commit[4] 2018-09-09: Auto merge of #53998 - eddyb:issue-53728, r=oli-obk
    commit[5] 2018-09-09: Auto merge of #53902 - dtolnay:group, r=petrochenkov
    commit[6] 2018-09-09: Auto merge of #54057 - matthiaskrgr:stabilize-edition-plus-clippy, r=Mark-Simulacrum
    commit[7] 2018-09-09: Auto merge of #53778 - petrochenkov:shadrelax2, r=nikomatsakis
    ERROR: no CI builds available between 0198a1e and 2d4e34c within last 167 days

    bisected with cargo-bisect-rustc cargo bisect-rustc --preserve --end 1.32.0 --start 2018-06-20 --regress success on edition 2018. This includes the stabilization of the 2018 edition.

    2018 edition (unstable) bisection With `cargo-features = ["edition"]`:

    searched toolchains nightly-2018-06-20 through nightly-2018-12-04


    Regression in nightly-2018-08-03


    fetching https://static.rust-lang.org/dist/2018-08-02/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2018-08-02: 40 B / 40 B [=====================================================================================================================] 100.00 % 1.56 MB/s converted 2018-08-02 to 97085f9
    fetching https://static.rust-lang.org/dist/2018-08-03/channel-rust-nightly-git-commit-hash.txt
    nightly manifest 2018-08-03: 40 B / 40 B [=====================================================================================================================] 100.00 % 1.37 MB/s converted 2018-08-03 to 7e8ca9f
    looking for regression commit between 2018-08-02 and 2018-08-03
    fetching (via remote github) commits from max(97085f9, 2018-07-31) to 7e8ca9f
    ending github query because we found starting sha: 97085f9
    get_commits_between returning commits, len: 12
    commit[0] 2018-08-01: Auto merge of #52958 - pietroalbini:rollup, r=pietroalbini
    commit[1] 2018-08-02: Auto merge of #52206 - RalfJung:zst-slices, r=alexcrichton
    commit[2] 2018-08-02: Auto merge of #52787 - riscv-rust:riscv-rust-pr, r=alexcrichton
    commit[3] 2018-08-02: Auto merge of #52847 - upsuper:thread-stack-reserve, r=alexcrichton
    commit[4] 2018-08-02: Auto merge of #52890 - djrenren:test-visibility, r=petrochenkov
    commit[5] 2018-08-02: Auto merge of #52906 - RalfJung:jemalloc, r=alexcrichton
    commit[6] 2018-08-02: Auto merge of #52975 - pnkfelix:issue-52967-edition-2018-implies-2-phase-borrows, r=Mark-Simulacrum
    commit[7] 2018-08-02: Auto merge of #52949 - Mark-Simulacrum:snap, r=alexcrichton
    commit[8] 2018-08-02: Auto merge of #52782 - pnkfelix:issue-45696-dangly-paths-for-box, r=eddyb
    commit[9] 2018-08-02: Auto merge of #52841 - petrochenkov:premacro, r=alexcrichton
    commit[10] 2018-08-03: Auto merge of #51657 - wesleywiser:wip_profiling, r=eddyb
    commit[11] 2018-08-03: Auto merge of #52919 - alexcrichton:update-cargo, r=Mark-Simulacrum
    ERROR: no CI builds available between 97085f9 and 7e8ca9f within last 167 days

    bisected with cargo-bisect-rustc cargo bisect-rustc --preserve --end 1.32.0 --start 2018-06-20 --regress success on edition 2018 with cargo-features = ["edition"]. This includes #52975 pnkfelix:issue-52967-edition-2018-implies-2-phase-borrows

    On the 2015 edition bisection, #59114 enable-migate-2015 seems related, this issue is present in the first stable 2018 edition nightly, and on the unstable 2018 edition bisection, #52975 pnkfelix:issue-52967-edition-2018-implies-2-phase-borrows seems related, so this seems like an issue that has existed in two-phase borrows/NLL(?) since it was introduced.

  8. removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 14, 2026
  9. rustbot commented on Oct 14, 2026

    @rustbot
    Collaborator

    Assigning P-high (discussion on Zulip).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions