Skip to content

Unsound drop due to imperfect lifetime checks #90838

Closed
@Patryk27

Description

@Patryk27

Looks like it's possible to impl Drop for a stricter lifetime than the one used in the type:

struct Wrapper<'a, T>(&'a T)
where
    T: 'a;

impl<'a, T> Drop for Wrapper<'a, T>
where
    T: 'static, // ayy ayy
{
    fn drop(&mut self) {
      //
    }
}

... which allows to essentially transmute from T: 'a to T: 'static, leading to unsoundness:

use std::{
    fmt::Debug,
    thread::{sleep, spawn},
    time::Duration,
};

struct Wrapper<'a, T>(&'a T)
where
    T: Clone + Debug + Send + 'a;

impl<'a, T> Drop for Wrapper<'a, T>
where
    T: Clone + Debug + Send + 'static,
{
    fn drop(&mut self) {
        let value = self.0.to_owned();

        spawn(move || {
            // Wait for `main()` to finish dropping `self.0`
            sleep(Duration::from_millis(100));

            // Use-after-free 
            println!("value: {:?}", value);
        });
    }
}

#[derive(Clone, Copy, Debug)]
struct StringWrapper<'a>(&'a String);

fn main() {
    let _ = Wrapper(&StringWrapper(&String::from("Hello!")));

    // Wait for the thread to complete
    sleep(Duration::from_secs(1));
}

On my machine, it prints:

value: StringWrapper("SU�Y\u{5}\u{0}")

Metadata

Metadata

Assignees

Labels

A-destructorsArea: Destructors (`Drop`, …)A-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions