Open SkiFire13 opened 3 weeks ago
We will likely have to turn ObserverSystem
into a duplicate of System
entirely, due to #9584.
EDIT: Or we introduce a supertrait of both of them that takes In
as a generic parameter.
what unsafe code is causing the problem? i.e. where are we missing a safety invariant?
what unsafe code is causing the problem? i.e. where are we missing a safety invariant?
I believe this is the problematic unsafe
use.
The assumption that "IntoObserverSystem
is only implemented for functions starting with for<'a> Trigger<'a>
" is wrong, since it is implemented for any IntoSystem<In = Trigger<'static>>
and that includes systems starting with In<Trigger<'static>>
where the 'static
lifetime is exposed.
Isn't ObserverSystem taking a fake 'static
more the issue here? i.e. couldn't you leak the trigger instead and still cause UB?
Isn't ObserverSystem taking a fake 'static more the issue here?
Yes, that's also part of the issue since it's safe to implement and directly receives a Trigger<'static>
, so the same issue can be reimplemented without relying on the SystemParamFunction
implementation with the In
first parameter. However fixing that will likely involve changing how ObserverSystem
is defined, since now the Trigger
is expected to be the input of the underlying System
and that's required to be 'static
(which likely was the orignal reason why the Trigger<'static>
was used). The possibilities I see are:
System
is somehow changed to support non-'static
inputs (I did try this in the past in a local branch and it didn't seem trivial)ObserverSystem
is changed to not require System
, and instead has its own implementations (essentially just moving the ones for SystemParamFunction
with their first argument being Trigger
over to IntoObserverSystem
)i.e. couldn't you leak the trigger instead and still cause UB?
Yes, for example Box::leak(Box::new(trigger)).event()
will return a &'static E
. Ultimately that's just another way to make the trigger
escape the current function lifetime.
Bevy version
0.14.1
What you did
I stored a
Trigger<'static, E, ()>
in aLocal
(could also be astatic
, a component, or anything else that can escape the observer system), and then I read it after the end of the first system call.What went wrong
On my system it prints the following:
Before should print
E("foo")
, notE("bar")
, showing that it is actually accessing the same memory location as the new event.MIRI also reports UB: