Skip to content
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

[WIP] [Help wanted] Adapt to upcoming changes in Abomonation #11

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

HadrienG2
Copy link

@HadrienG2 HadrienG2 commented Oct 12, 2019

WIP because 1/depends on a development branch of abomonation and 2/still has a memory-safety-critical bug that I need your help to solve

So, I've been proposing a bunch of changes to abomonation recently. If accepted, they will cause quite a bit of API breakage. This PR matches my understanding of how abomonation_derive will need to change in order to adapt to the new API.

Copy link
Author

@HadrienG2 HadrienG2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a quick tour of what this PR does, and one synstructure question about something which it currently does badly.

@@ -14,38 +14,51 @@ fn derive_abomonation(mut s: synstructure::Structure) -> proc_macro2::TokenStrea
});

let entomb = s.each(|bi| quote! {
::abomonation::Abomonation::entomb(#bi, _write)?;
::abomonation::Entomb::entomb(#bi, _write)?;
Copy link
Author

@HadrienG2 HadrienG2 Oct 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abomonation API change from TimelyDataflow/abomonation#28: The Abomonation trait is now API sugar for two separate traits, Entomb and Exhume, via a blanket impl which ensures that everything which implements those two traits implements Abomonation. This was done to allow deserialization of types which contain references, which requires adding a lifetime parameter to the deserialization trait (as in serde).

The semantics of Exhume<'de>'s lifetime parameter is that if T: Exhume<'de>, it means that it is okay to deserialize an &'de T from &'de mut [u8] bytes. This is not always the case for references, for example you don't want to allow people to deserialize &'static Something from stack-allocated bytes.

src/lib.rs Outdated
});

s.bind_with(|_| synstructure::BindStyle::RefMut);

let exhume = s.each(|bi| quote! {
let temp = bytes;
bytes = ::abomonation::Abomonation::exhume(#bi, temp)?;
bytes = ::abomonation::Exhume::exhume(From::from(#bi), bytes)?;
Copy link
Author

@HadrienG2 HadrienG2 Oct 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abomonation API change from TimelyDataflow/abomonation#22: exhume() now expects NonNull<Self> instead of &mut self. That is because exhume() receives an invalid Self as input (it has dangling pointers/references), and creating an &mut to invalid data is UB. Raw pointers should be used whenever possible in those situations.

});

s.bound_impl(quote!(abomonation::Abomonation), quote! {
#[inline] unsafe fn entomb<W: ::std::io::Write>(&self, _write: &mut W) -> ::std::io::Result<()> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the inline directives since I established in TimelyDataflow/abomonation#24 that they are actually very rarely needed. It's probably better to let the compiler's optimizer do its job if it does it reasonably well.

extern crate abomonation;
extern crate std;

gen unsafe impl abomonation::Entomb for @Self {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abomonation API change from TimelyDataflow/abomonation#25: implementing Abomonation traits is now unsafe, since an incorrect implementation (e.g. forgetting to patch a pointer) can lead to memory unsafety.

) -> Option<&'de mut [u8]> {
// FIXME: This (briefly) constructs an &mut _ to invalid data
// (via "ref mut"), which is UB. The proposed &raw mut
// operator would allow avoiding this.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the FIXME says, this operation cannot be implemented without UB in Rust, for now, but there is a new language feature on the way that should fix that.

Some(bytes)

gen unsafe impl<'de> abomonation::Exhume<'de> for @Self
where Self: 'de,
Copy link
Author

@HadrienG2 HadrienG2 Oct 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

synstructure help wanted here to fix a memory safety bug: assuming that Self is Generic<'a, 'b, 'c>, I would like to enforce that abomonation::Exhume<'de> is only implemented for Generic<'de, 'de, 'de>.

This where clase does not fully do what I want, because it allows Self to be e.g. &'static Something when 'de is shorter-lived than 'static, which is wrong and can lead to memory unsafety through deserialization of a fake &'static T that points into short-lived stack-allocated bytes followed by deallocation of said bytes.

A 'de: 'self bound (where 'self is the lifetime of Self) is not enough either, because abomonation::decode() will ultimately generate a &'de Self, and therefore Self must live for at least 'de.

Since Self must live for at least 'de and at most 'de, it must live for exactly 'de, i.e. Generic<'de, 'de, 'de> is what I want here. But I don't know how to do it.

@HadrienG2 HadrienG2 changed the title [WIP] Adapt to upcoming changes in Abomonation [WIP] [Help wanted] Adapt to upcoming changes in Abomonation Oct 13, 2019
}
}
}
alignment.push(quote!(align));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abomonation API change from TimelyDataflow/abomonation#33: after this PR, abomonation will provide full support for correctly aligning data in memory... which requires from support from the Abomonation trait implementors.

@@ -26,6 +27,7 @@ mod tests {
assert_eq!(bytes.len(), measure(&record));

// decode from binary data
let mut bytes = AlignedBytes::<Struct>::new(&mut bytes);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decode() now expects its input data to be properly aligned, and this is a minimally invasive way to prepare said data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant