xref: /linux/rust/zerocopy-derive/derive/try_from_bytes.rs (revision bb29b957eaaa15be33243550d9ee576c2d601669)
1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2 //
3 use proc_macro2::{Ident, Span, TokenStream};
4 use quote::{quote, ToTokens as _};
5 use syn::{spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, Error, Field};
6 
7 use crate::{
8     derive::project::{
9         derive_enum, derive_projection_struct_union, generate_tag_consts, struct_union_variant_id,
10         tag_ident,
11     },
12     repr::EnumRepr,
13     util::{
14         enum_size_from_repr, generate_tag_enum, Client, Ctx, DataExt, FieldBounds,
15         ImplBlockBuilder, Trait, TraitBound,
16     },
17 };
18 
19 fn candidate_ident(ctx: &Ctx) -> Ident {
20     // Choose a source-pointer name distinct from the field names. Mixed-site
21     // hygiene keeps it out of the user's invariant expressions.
22     let fields = ctx.ast.data.fields();
23     let mut name = "candidate".to_owned();
24     while fields.iter().any(|(_, field, _)| field.to_string().trim_start_matches("r#") == name) {
25         name.push('_');
26     }
27     Ident::new(&name, Span::mixed_site())
28 }
29 
30 /// Generates validation of every field of a struct or enum variant, leaving
31 /// the validated pointers in scope for subsequent field invariants.
32 /// A union is checked by invoking this once per field.
33 fn derive_variant_is_safe<'a>(
34     ctx: &Ctx,
35     variant_id: &TokenStream,
36     fields: impl IntoIterator<Item = &'a Field>,
37 ) -> Result<TokenStream, Error> {
38     let fields = fields.into_iter().collect::<Vec<_>>();
39     if fields.is_empty() {
40         return Ok(quote!(true));
41     }
42 
43     let zerocopy_crate = &ctx.zerocopy_crate;
44     let core = ctx.core_path();
45     let candidate = candidate_ident(ctx);
46     let field_names = fields.iter().enumerate().map(|(idx, field)| {
47         field
48             .ident
49             .as_ref()
50             .map(|name| name.to_token_stream())
51             .unwrap_or_else(|| syn::Index::from(idx).to_token_stream())
52     });
53     let field_checks = field_names
54         .map(|name| {
55             quote! {
56                 #zerocopy_crate::into_inner!(
57                     #candidate.project::<
58                         #zerocopy_crate::project_clients::TryFromBytesDerive,
59                         _,
60                         { #variant_id },
61                         { #zerocopy_crate::ident_id!(#name) },
62                     >()
63                 ).try_into_safe::<_, #zerocopy_crate::BecauseImmutable>()
64             }
65         })
66         .collect::<Vec<_>>();
67     // Without invariants, no expression needs the validated pointers. Avoid
68     // introducing bindings that could collide with caller-defined items.
69     if ctx.invariant_span.is_none() {
70         return Ok(quote! { true #(&& #field_checks.is_ok())* });
71     }
72 
73     let field_bindings = fields
74         .iter()
75         .enumerate()
76         .map(|(idx, _)| ctx.fresh_ident(&format!("field_{}", idx)))
77         .collect::<Vec<_>>();
78     let bound_fields =
79         fields.iter().copied().zip(field_bindings.iter().cloned()).collect::<Vec<_>>();
80     let field_validations = fields
81         .iter()
82         .enumerate()
83         .map(|(idx, field)| {
84             let invariants = crate::invariant::parse(&field.attrs)?;
85             let invariants = invariants
86                 .iter()
87                 .map(|expression| crate::invariant::bind_fields(&bound_fields[..=idx], expression));
88             Ok(quote! {
89                 #(
90                     // Keep `return` in an invariant from bypassing later fields.
91                     if !{ #[inline(always)] || -> #core::primitive::bool { #invariants } }() {
92                         return false;
93                     }
94                 )*
95             })
96         })
97         .collect::<Result<Vec<_>, Error>>()?;
98     Ok(quote! {
99         {
100             #(
101                 // Retain each shared pointer for subsequent field invariants.
102                 #[allow(unused_variables)]
103                 let #field_bindings = match #field_checks {
104                     #core::result::Result::Ok(#field_bindings) => #field_bindings,
105                     #core::result::Result::Err(_) => return false,
106                 };
107                 #field_validations
108             )*
109             true
110         }
111     })
112 }
113 
114 /// Generates an implementation of `is_safe` for an arbitrary enum.
115 ///
116 /// For an enum with fields, [`derive_enum`] generates the representation model
117 /// and projection impls. This function reads the tag, matches it against the
118 /// enum's discriminants, and validates each field of the selected variant
119 /// through those projections. A fieldless enum needs only the generated tag
120 /// enum and discriminant constants.
121 pub(crate) fn derive_is_safe(
122     ctx: &Ctx,
123     data: &DataEnum,
124     repr: &EnumRepr,
125 ) -> Result<TokenStream, Error> {
126     if !(repr.is_c() || repr.is_primitive()) {
127         return Err(Error::new(
128             ctx.ast.span(),
129             "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
130         ));
131     }
132 
133     let zerocopy_crate = &ctx.zerocopy_crate;
134     let core = ctx.core_path();
135     let candidate = candidate_ident(ctx);
136     let alignment = ctx.fresh_ident("___ZcAlignment");
137     let tag = Ident::new("tag", Span::mixed_site());
138     let projections = if data.fields().is_empty() {
139         let tag_enum = generate_tag_enum(ctx, repr, data);
140         let tag_consts = generate_tag_consts(data);
141         quote! {
142             #tag_enum
143 
144             type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag<
145                 { #core::mem::size_of::<___ZerocopyTag>() },
146             >;
147 
148             #tag_consts
149         }
150     } else {
151         derive_enum(ctx, data, Client::TryFromBytesDerive)?
152     };
153 
154     let match_arms = data
155         .variants
156         .iter()
157         .enumerate()
158         .map(|(idx, variant)| {
159             let name = &variant.ident;
160             let variant_id = quote! { #zerocopy_crate::ident_id!(#name) };
161             let fields_is_safe = derive_variant_is_safe(ctx, &variant_id, &variant.fields)?;
162             let pattern = if ctx.invariant_span.is_some() {
163                 quote! { #core::option::Option::Some(#idx) }
164             } else {
165                 tag_ident(name).to_token_stream()
166             };
167             Ok(quote! {
168                 #pattern => { #fields_is_safe }
169             })
170         })
171         .collect::<Result<Vec<_>, Error>>()?;
172 
173     let read_tag = quote! {
174         let #tag = #candidate
175             .reborrow()
176             .cast::<
177                 ___ZerocopyTagPrimitive,
178                 #zerocopy_crate::pointer::cast::CastSized,
179                 (#zerocopy_crate::pointer::BecauseRead, _),
180             >()
181             .recall_validity::<_, (_, (_, _))>()
182             .read::<#zerocopy_crate::BecauseImmutable>();
183     };
184     let tag_init = if ctx.invariant_span.is_some() {
185         let allow = crate::util::allow_generated_code();
186         let tag_arms = data.variants.iter().enumerate().map(|(idx, variant)| {
187             let tag = tag_ident(&variant.ident);
188             quote! { #tag => #core::option::Option::Some(#idx) }
189         });
190         quote! {
191             // Keep implementation-only items and their lint allowances out
192             // of the scopes containing caller-authored invariant expressions.
193             #allow
194             let #tag = {
195                 #projections
196                 #read_tag
197                 match #tag {
198                     #(#tag_arms,)*
199                     _ => #core::option::Option::None,
200                 }
201             };
202         }
203     } else {
204         quote! { #projections #read_tag }
205     };
206 
207     Ok(quote! {
208         // SAFETY: We use `is_safe` to validate that the bit pattern of the
209         // enum's tag corresponds to one of the enum's discriminants. Then, we
210         // check the bit validity of each field of the corresponding variant.
211         // Thus, this is a sound implementation of `is_safe`.
212         #[inline]
213         fn is_safe<#alignment>(
214             mut #candidate: #zerocopy_crate::Maybe<'_, Self, #alignment>,
215         ) -> #core::primitive::bool
216         where
217             #alignment: #zerocopy_crate::invariant::Alignment,
218         {
219             #tag_init
220 
221             match #tag {
222                 #(#match_arms,)*
223                 _ => false,
224             }
225         }
226     })
227 }
228 pub(crate) fn derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
229     match &ctx.ast.data {
230         Data::Struct(strct) => derive_try_from_bytes_struct(ctx, strct, top_level),
231         Data::Enum(enm) => derive_try_from_bytes_enum(ctx, enm, top_level),
232         Data::Union(unn) => derive_try_from_bytes_union(ctx, unn, top_level),
233     }
234 }
235 fn derive_try_from_bytes_struct(
236     ctx: &Ctx,
237     strct: &DataStruct,
238     top_level: Trait,
239 ) -> Result<TokenStream, Error> {
240     let extras = if let Some(extras) = try_gen_trivial_is_safe(ctx, top_level) {
241         extras
242     } else {
243         let zerocopy_crate = &ctx.zerocopy_crate;
244         let variant_id = quote! { #zerocopy_crate::STRUCT_VARIANT_ID };
245         let fields_is_safe = derive_variant_is_safe(ctx, &variant_id, &strct.fields)?;
246         let core = ctx.core_path();
247         let candidate = candidate_ident(ctx);
248         let alignment = ctx.fresh_ident("___ZcAlignment");
249         quote!(
250             // SAFETY: We use `is_safe` to validate that each field is bit-valid,
251             // and only return `true` if all of them are. The bit validity of a
252             // struct is just the composition of the bit validities of its
253             // fields, so this is a sound implementation of `is_safe`.
254             #[inline]
255             fn is_safe<#alignment>(
256                 mut #candidate: #zerocopy_crate::Maybe<'_, Self, #alignment>,
257             ) -> #core::primitive::bool
258             where
259                 #alignment: #zerocopy_crate::invariant::Alignment,
260             {
261                 #fields_is_safe
262             }
263         )
264     };
265     Ok(ImplBlockBuilder::new(ctx, strct, Trait::TryFromBytes, FieldBounds::ALL_SELF)
266         .inner_extras(extras)
267         .outer_extras(derive_projection_struct_union(ctx, strct, Client::TryFromBytesDerive))
268         .build())
269 }
270 fn derive_try_from_bytes_union(
271     ctx: &Ctx,
272     unn: &DataUnion,
273     top_level: Trait,
274 ) -> Result<TokenStream, Error> {
275     let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
276 
277     let zerocopy_crate = &ctx.zerocopy_crate;
278     let union_variant_id = struct_union_variant_id(ctx).to_token_stream();
279     let extras = if let Some(extras) = try_gen_trivial_is_safe(ctx, top_level) {
280         extras
281     } else {
282         let fields_is_safe = unn
283             .fields
284             .named
285             .iter()
286             .map(|field| derive_variant_is_safe(ctx, &union_variant_id, [field]))
287             .collect::<Result<Vec<_>, _>>()?;
288         let core = ctx.core_path();
289         let candidate = candidate_ident(ctx);
290         let alignment = ctx.fresh_ident("___ZcAlignment");
291         quote!(
292             // SAFETY: We use `is_safe` to validate that any field is bit-valid;
293             // we only return `true` if at least one of them is and its
294             // invariants also hold. The bit validity of a union is not yet
295             // well defined in Rust, but it is guaranteed to be no more strict
296             // than this definition. See #696 for a more in-depth discussion.
297             #[inline]
298             fn is_safe<#alignment>(
299                 mut #candidate: #zerocopy_crate::Maybe<'_, Self, #alignment>,
300             ) -> #core::primitive::bool
301             where
302                 #alignment: #zerocopy_crate::invariant::Alignment,
303             {
304                 // Keep each field's bindings and early returns local to that
305                 // field, so a failure lets validation try the next field.
306                 false #(|| { #[inline(always)] || { #fields_is_safe } }())*
307             }
308         )
309     };
310     Ok(ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds)
311         .inner_extras(extras)
312         .outer_extras(derive_projection_struct_union(ctx, unn, Client::TryFromBytesDerive))
313         .build())
314 }
315 fn derive_try_from_bytes_enum(
316     ctx: &Ctx,
317     enm: &DataEnum,
318     top_level: Trait,
319 ) -> Result<TokenStream, Error> {
320     let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
321 
322     // If an enum has no fields, it has a well-defined integer representation,
323     // and every possible bit pattern corresponds to a valid discriminant tag,
324     // then it *could* be `FromBytes` (even if the user hasn't derived
325     // `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N
326     // variants.
327     let could_be_from_bytes = enum_size_from_repr(&repr)
328         .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
329         .unwrap_or(false);
330 
331     let trivial_is_safe = try_gen_trivial_is_safe(ctx, top_level);
332     let extra = match (trivial_is_safe, could_be_from_bytes) {
333         (Some(is_safe), _) => is_safe,
334         // SAFETY: It would be sound for the enum to implement `FromBytes`, as
335         // required by `gen_trivial_is_safe_unchecked`.
336         (None, true) => unsafe { gen_trivial_is_safe_unchecked(ctx) },
337         (None, false) => match derive_is_safe(ctx, enm, &repr) {
338             Ok(extra) => extra,
339             Err(e) => return ctx.error_or_skip(e),
340         },
341     };
342 
343     Ok(ImplBlockBuilder::new(ctx, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF)
344         .inner_extras(extra)
345         .build())
346 }
347 fn try_gen_trivial_is_safe(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> {
348     // If the top-level trait is `FromBytes` and `Self` has no type parameters,
349     // then the `FromBytes` derive will fail compilation if `Self` is not
350     // actually soundly `FromBytes`, and so we can rely on that for our `is_safe`
351     // impl. It's plausible that we could make changes - or Rust could make
352     // changes (such as the "trivial bounds" language feature) - that make this
353     // no longer true. To hedge against these, we include an explicit `Self:
354     // FromBytes` check in the generated `is_safe`, which is bulletproof.
355     //
356     // If `ctx.skip_on_error` is true, we can't rely on the `FromBytes` derive
357     // to fail compilation if `Self` is not actually soundly `FromBytes`.
358     if matches!(top_level, Trait::FromBytes)
359         && ctx.ast.generics.params.is_empty()
360         && !ctx.skip_on_error
361     {
362         let zerocopy_crate = &ctx.zerocopy_crate;
363         let core = ctx.core_path();
364         Some(quote!(
365             // SAFETY: See inline.
366             #[inline(always)]
367             fn is_safe<___ZcAlignment>(
368                 _candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
369             ) -> #core::primitive::bool
370             where
371                 ___ZcAlignment: #zerocopy_crate::invariant::Alignment,
372             {
373                 if false {
374                     fn assert_is_from_bytes<T>()
375                     where
376                         T: #zerocopy_crate::FromBytes,
377                         T: ?#core::marker::Sized,
378                     {
379                     }
380 
381                     assert_is_from_bytes::<Self>();
382                 }
383 
384                 // SAFETY: The preceding code only compiles if `Self:
385                 // FromBytes`. Thus, this code only compiles if all initialized
386                 // byte sequences represent valid instances of `Self`.
387                 true
388             }
389         ))
390     } else {
391         None
392     }
393 }
394 
395 /// # Safety
396 ///
397 /// All initialized bit patterns must be valid for `Self`.
398 unsafe fn gen_trivial_is_safe_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream {
399     let zerocopy_crate = &ctx.zerocopy_crate;
400     let core = ctx.core_path();
401     quote!(
402         // SAFETY: The caller of `gen_trivial_is_safe_unchecked` has promised
403         // that all initialized bit patterns are valid for `Self`.
404         #[inline(always)]
405         fn is_safe<___ZcAlignment>(
406             _candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
407         ) -> #core::primitive::bool
408         where
409             ___ZcAlignment: #zerocopy_crate::invariant::Alignment,
410         {
411             true
412         }
413     )
414 }
415 
416 #[cfg(test)]
417 mod tests {
418     use syn::{parse_quote, DeriveInput};
419 
420     use super::*;
421 
422     fn derive_error(input: DeriveInput) -> String {
423         let ctx = Ctx::try_from_derive_input(input).unwrap();
424         match derive_try_from_bytes(&ctx, Trait::TryFromBytes) {
425             Ok(_) => panic!("expected derive to reject conflicting representations"),
426             Err(error) => error.to_string(),
427         }
428     }
429 
430     #[test]
431     fn raw_repr_conflict_matches_ordinary_repr() {
432         let ordinary = derive_error(parse_quote! {
433             #[repr(C, align(8))]
434             #[repr(u8)]
435             enum Packet {
436                 Value(core::num::NonZeroU8),
437                 End,
438             }
439         });
440         let raw = derive_error(parse_quote! {
441             #[repr(C, align(8))]
442             #[r#repr(r#u8)]
443             enum Packet {
444                 Value(core::num::NonZeroU8),
445                 End,
446             }
447         });
448 
449         // Rejecting both spellings prevents generating a validator whose
450         // synthetic enum layout disagrees with the compiler's actual layout.
451         assert_eq!(ordinary, "this conflicts with another representation hint");
452         assert_eq!(ordinary, raw);
453     }
454 }
455