Skip to content

Port types returning &'static references is unsound #95

Open
@prokopyl

Description

@prokopyl

As of now, all port types return &'static references to their contents, which allows them to be stored outside of the run() function, at which point the host could invalidate them (the following code compiles fine today):

struct Amp {
    foo: Option<&'static [f32]>
}

struct Ports { input: InputPort<Audio> }

impl Plugin for Amp {
    fn run(&mut self, ports: &mut Ports, _features: &mut (), _: u32) {
        self.foo.replace(&*ports.input); // Uh-oh
    }
}

While this example is rather obvious in that it's doing something very odd, issues can be viciously subtle with more complex port types such as Atom: one could want to store a deserialized value.

I haven't made any in-depth testing, but I see two solutions to this:

The first would be to make PortType generic over a 'a lifetime:

pub trait PortType<'a> {
    type InputPortType: Sized + 'a;
    type OutputPortType: Sized + 'a;
}

impl<'a> PortType<'a> for Audio<'a> {
    type InputPortType = &'a [f32];
    type OutputPortType = &'a mut [f32];
}

However, this has a cascading effect, which would force all downstream users of port types to specify their lifetimes:

#[derive(PortCollection)]
struct Ports<'a> {
    gain: InputPort<'a, Control<'a>>,
    input: InputPort<'a, Audio<'a>>,
    output: OutputPort<'a, Audio<'a>>,
}

The second option would be to make the associated types themselves generic:

pub trait PortType {
    type InputPortType<'a>: Sized;
    type OutputPortType<'a>: Sized;
}

impl PortType for Audio {
    type InputPortType<'a> = &'a [f32];
    type OutputPortType<'a> = &'a mut [f32];
}

However, this would require Generic Associated Types being stabilized, but while it seems to be making steady progress, there doesn't seem to be any deadline coming soon.

Both options are breaking changes however.

I think we could potentially use the first solution now, and move to GATs when they are stabilized, making two breaking changes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    💥 Unsound APIA bug that allows to trigger Undefined Behavior in safe Rust💔 Breaking changeChanges to the code that will lead to break one or more APIs🦀 Blocked on Rust featureImplementing this requires Rust features that aren't stabilized yet🐞 BugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions