Skip to content

lazy::Lazy<T> having public fields is unsound. #105

@eddyb

Description

@eddyb

While normally only used within the lazy_static macro, the type and its fields need to be public, at least for the old macro_rules macros. This poses both a safety (the fields are used in unsafe ways) and stability hazard (changing implementation details is technically breaking).

Because Lazy::get takes &'static mut self, it's hard to abuse (static mut itself is unsafe, so it doesn't really count here), but Box::leak does let you create a &'static mut at runtime, so you could, in theory, leak a Lazy<T>, trigger the call_once manually, then call Lazy::get, which will return an invalid reference to a T (since no actual initialization occurred).

To construct a Lazy<T> to initialize the static in the macro, you can use associated consts (since 1.20):

impl<T> Lazy<T> {
    const INIT: Self = Lazy(None, ONCE_INIT);
}

This way, the fields can be made private (we should try a crater run if possible - cc @kennytm @Mark-Simulacrum - just to make sure nobody was using the Lazy type themselves).

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