Skip to content

Polonius soundness bug with higher ranked function pointers #161471

Open

Description

@theemathas

I'm filing this issue on behalf of @carlini, who discovered the initial unsoundness reproducer with an LLM. I then minimized the code to the code below.

The code below creates a &mut that aliases with a &, and somehow uses both of them, causing unsoundness. The aliasing references are r and p.

#![forbid(unsafe_code)]

type Payload = Option<Box<i32>>;

type P<'a, 'b> = for<'x> fn(&'a mut &'b Payload, &'x ()) -> &'x Payload;

fn f<'a, 'b, 'c>(x: &'a mut &'b Payload, _: &'c ()) -> &'b Payload {
    *x
}

fn main() {
    let p: &'static mut Payload = Box::leak(Box::new(Some(Box::new(1))));
    let mid: &mut &Payload = Box::leak(Box::new(&*p));
    let r: &Payload = (f as P<'_, '_>)(mid, &());
    let b: &Box<i32> = r.as_ref().unwrap();
    if false {
        let _: &mut &'static Payload = mid;
    } else {
        *p = None;
        println!("{b}");  // Segfault
    };
}

The unsoundness reproduces with -Zpolonius=next, but doesn't reproduce (fails to compile) with -Zpolonius=no.

Meta

Reproducible on the playground with version 1.100.0-nightly (2026-08-20 8925ea358a0f265ca610)

Activity

  1. added
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    C-bugCategory: This is a bug.
    NLL-poloniusIssues related for using Polonius in the borrow checker
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    A-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)
    on Aug 21, 2026
  2. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 21, 2026
  3. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Aug 21, 2026
  4. lcnr commented on Aug 25, 2026

    @lcnr
    Contributor

    let r: &Payload = (f as P<'_, '_>)(mid, &()); seems like it's very much related to #25860. Unsure what causes polonius to behave worse here

  5. amandasystems commented on Sep 16, 2026

    @amandasystems
    Contributor

    let r: &Payload = (f as P<'_, '_>)(mid, &()); seems like it's very much related to #25860. Unsure what causes polonius to behave worse here

    I've looked into this some (and will look into it more) and I think the reason for the difference here is a placeholder outlives constraint which increases the lifetime in NLL, but for some reason doesn't in Polonius.

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 checkerA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessNLL-poloniusIssues related for using Polonius in the borrow checkerT-typesRelevant to the types team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions