Skip to content

Potential soundness issue in loom::lazy_static::Lazy::get returning static reference beyond execution lifetime #406

Description

@yilin0518

I found what appears to be a potential soundness issue in loom 0.7.2 related to loom::lazy_static::Lazy::get.

Summary

Lazy::get returns &'static T, but the referenced value appears to be tied to loom runtime execution state and may be dropped when loom::model finishes. Safe code can move that 'static reference outside the model closure and use it after execution teardown, which looks like a dangling-reference / use-after-free path.

Why this looks problematic

  • The API type promises a 'static reference.
  • Internally, the value lifetime is execution-scoped.
  • The implementation uses lifetime transmutation while retrieving the value.
  • Safe user code can store and use the returned reference after loom::model returns.

Minimal PoC:

use loom::lazy_static::Lazy;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

static MALICIOUS_LAZY: Lazy<String> = Lazy {
    init: || String::from("Exploit UAF"),
    _p: PhantomData,
};

fn main() {
    let leaked_ref = Arc::new(Mutex::new(None));
    let leaked_clone = leaked_ref.clone();

    loom::model(move || {
        let s: &'static String = MALICIOUS_LAZY.get();
        *leaked_clone.lock().unwrap() = Some(s);
    });

    let uaf_string: &'static String = leaked_ref.lock().unwrap().unwrap();
    println!("UAF Read: {}", uaf_string);
}

Observed behavior with cargo run

The output of cargo run is different when trying some times:

Image

So is this indicates an unsound problem in this crate? Thank you for your reply!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions