-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merged by Bors] - Fix double drop in BlobVec::replace_unchecked (#2597) #2848
Conversation
8221c7f
to
49b4071
Compare
Thanks! Is there a way to write a test for this? |
I'll try to adapt the code snippet from the issue. |
As it turns out, the test failed :) So I fixed another bug. I also have a test for the second code snippet from the issue (#2597 (comment)) but it's still failing, I think due to |
956f072
to
d423cce
Compare
Awesome, thanks for the test and the issue :) |
@@ -97,12 +98,18 @@ impl BlobVec { | |||
|
|||
/// # Safety | |||
/// - index must be in-bounds | |||
// - memory must be previously initialized | |||
/// - memory must be previously initialized |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what memory? this should be clearer that we're talking about value
(and also what type is supposed to be initialised behind it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed it to say value
but after seeing the same comment at initialize_unchecked
I'm pretty sure this is referring to the memory at index
/ptr
, so I'll change it to say that
edit: I don't think I phrased it very clearly, though
/// - the contents of `*value` must not be dropped by the caller. (In fact, | ||
/// this function may overwrite them.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// - the contents of `*value` must not be dropped by the caller. (In fact, | |
/// this function may overwrite them.) | |
/// - the contents of `*value` is left logically uninitialised/moved out of and as such | |
/// should not be used or dropped by the caller |
@@ -88,6 +88,7 @@ impl BlobVec { | |||
/// # Safety | |||
/// - index must be in bounds | |||
/// - memory must be reserved and uninitialized | |||
/// - the contents of `*value` must not be dropped by the caller |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont see why this is the case. this function does not drop the data that was written into value
so if we aren't dropping the data behind value somewhere this would leak memory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*value
is moved into the BlobVec
, so if the caller drops it, that would cause a double drop when dropping the BlobVec
or moving the value out again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh right this is copy not swap like the other function 🤦♀️
// calling `drop`. Otherwise, if `drop` panics, then when the collection | ||
// is dropped during stack unwinding, the collection's `Drop` impl will | ||
// call `drop` again for the old value, so we get a double drop. | ||
std::ptr::swap_nonoverlapping(value, ptr, self.item_layout.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be cheaper to set self.len
to 0
before dropping and then set it back afterwards rather than using swap_nonoverlapping
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These seem correct - I'm not quite sure that the safety comments are quite as precise as I'd choose to make them, but these are strict improvements.
8ad92f9
to
1337eb1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic looks correct to me, the safety comments could be better but that's not the point of this PR and they're already an improvement.
crates/bevy_ecs/src/bundle.rs
Outdated
@@ -109,8 +109,8 @@ macro_rules! tuple_impl { | |||
#[allow(non_snake_case)] | |||
let ($(mut $name,)*) = self; | |||
$( | |||
func((&mut $name as *mut $name).cast::<u8>()); | |||
std::mem::forget($name); | |||
let mut value = ::std::mem::ManuallyDrop::new($name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I removed this commit
1337eb1
to
9a8ff08
Compare
9a8ff08
to
b499744
Compare
looks good to me. thanks! |
bors r+ |
Objective
I thought I'd have a go a trying to fix #2597.
Hopefully fixes #2597.
Solution
I reused the memory pointed to by the value parameter, that is already required by
insert
to not be dropped, to contain the extracted value while dropping it.