From a10c6d414a1ed6b0b3626d4a33944ae05ad0fcf0 Mon Sep 17 00:00:00 2001 From: Henri Chataing Date: Fri, 17 May 2024 10:28:23 -0700 Subject: [PATCH] Validate constraints in decode_partial --- pdl-compiler/src/backends/rust/mod.rs | 23 +++++++++++++ .../packet_decl_child_packets_big_endian.rs | 16 ++++++++++ ...packet_decl_child_packets_little_endian.rs | 16 ++++++++++ .../packet_decl_grand_children_big_endian.rs | 32 +++++++++++++++++++ ...acket_decl_grand_children_little_endian.rs | 32 +++++++++++++++++++ ...decl_parent_with_alias_child_big_endian.rs | 24 ++++++++++++++ ...l_parent_with_alias_child_little_endian.rs | 24 ++++++++++++++ ..._decl_parent_with_no_payload_big_endian.rs | 8 +++++ ...cl_parent_with_no_payload_little_endian.rs | 8 +++++ .../struct_decl_child_structs_big_endian.rs | 16 ++++++++++ ...struct_decl_child_structs_little_endian.rs | 16 ++++++++++ .../struct_decl_grand_children_big_endian.rs | 32 +++++++++++++++++++ ...truct_decl_grand_children_little_endian.rs | 32 +++++++++++++++++++ pdl-runtime/src/lib.rs | 7 ++++ 14 files changed, 286 insertions(+) diff --git a/pdl-compiler/src/backends/rust/mod.rs b/pdl-compiler/src/backends/rust/mod.rs index a1b6cd2..f3a2c53 100644 --- a/pdl-compiler/src/backends/rust/mod.rs +++ b/pdl-compiler/src/backends/rust/mod.rs @@ -445,6 +445,27 @@ fn generate_derived_packet_decl( } }; + // Constraint checks are only run for constraints added to this declaration + // and not parent constraints which are expected to have been validated + // earlier. + let constraint_checks = decl.constraints().map(|c| { + let field_id = c.id.to_ident(); + let field_name = &c.id; + let packet_name = id; + let value = constraint_value(&parent_data_fields, c); + let value_str = value.to_string(); + quote! { + if parent.#field_id() != #value { + return Err(DecodeError::InvalidFieldValue { + packet: #packet_name, + field: #field_name, + expected: #value_str, + actual: format!("{:?}", parent.#field_id()), + }) + } + } + }); + let decode_partial = if parent_decl.payload().is_some() { // Generate an implementation of decode_partial that will decode // data fields present in the parent payload. @@ -453,6 +474,7 @@ fn generate_derived_packet_decl( quote! { fn decode_partial(parent: &#parent_name) -> Result { let mut buf: &[u8] = &parent.payload; + #( #constraint_checks )* #field_parser if buf.is_empty() { Ok(Self { @@ -472,6 +494,7 @@ fn generate_derived_packet_decl( // return DecodeError::InvalidConstraint. quote! { fn decode_partial(parent: &#parent_name) -> Result { + #( #constraint_checks )* Ok(Self { #( #copied_field_ids: parent.#copied_field_ids, )* }) diff --git a/pdl-compiler/tests/generated/rust/packet_decl_child_packets_big_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_child_packets_big_endian.rs index c43d5f0..b700aa7 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_child_packets_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_child_packets_big_endian.rs @@ -221,6 +221,14 @@ impl TryFrom<&Bar> for Vec { impl Bar { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.a() != 100 { + return Err(DecodeError::InvalidFieldValue { + packet: "Bar", + field: "a", + expected: "100", + actual: format!("{:?}", parent.a()), + }); + } if buf.remaining() < 1 { return Err(DecodeError::InvalidLengthError { obj: "Bar", @@ -313,6 +321,14 @@ impl TryFrom<&Baz> for Vec { impl Baz { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.b() != Enum16::B { + return Err(DecodeError::InvalidFieldValue { + packet: "Baz", + field: "b", + expected: "Enum16 :: B", + actual: format!("{:?}", parent.b()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Baz", diff --git a/pdl-compiler/tests/generated/rust/packet_decl_child_packets_little_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_child_packets_little_endian.rs index 7996f47..44e8f7f 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_child_packets_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_child_packets_little_endian.rs @@ -221,6 +221,14 @@ impl TryFrom<&Bar> for Vec { impl Bar { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.a() != 100 { + return Err(DecodeError::InvalidFieldValue { + packet: "Bar", + field: "a", + expected: "100", + actual: format!("{:?}", parent.a()), + }); + } if buf.remaining() < 1 { return Err(DecodeError::InvalidLengthError { obj: "Bar", @@ -313,6 +321,14 @@ impl TryFrom<&Baz> for Vec { impl Baz { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.b() != Enum16::B { + return Err(DecodeError::InvalidFieldValue { + packet: "Baz", + field: "b", + expected: "Enum16 :: B", + actual: format!("{:?}", parent.b()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Baz", diff --git a/pdl-compiler/tests/generated/rust/packet_decl_grand_children_big_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_grand_children_big_endian.rs index f1b0080..ad359aa 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_grand_children_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_grand_children_big_endian.rs @@ -261,6 +261,14 @@ impl Child { } fn decode_partial(parent: &Parent) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.foo() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "Child", + field: "foo", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.foo()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Child", @@ -395,6 +403,22 @@ impl GrandChild { } fn decode_partial(parent: &Child) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.bar() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "bar", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.bar()), + }); + } + if parent.quux() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "quux", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.quux()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); if buf.is_empty() { @@ -499,6 +523,14 @@ impl TryFrom<&GrandGrandChild> for Vec { impl GrandGrandChild { fn decode_partial(parent: &GrandChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.baz() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandGrandChild", + field: "baz", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.baz()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); if buf.is_empty() { diff --git a/pdl-compiler/tests/generated/rust/packet_decl_grand_children_little_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_grand_children_little_endian.rs index 3de51ea..d9e2f4f 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_grand_children_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_grand_children_little_endian.rs @@ -261,6 +261,14 @@ impl Child { } fn decode_partial(parent: &Parent) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.foo() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "Child", + field: "foo", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.foo()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Child", @@ -395,6 +403,22 @@ impl GrandChild { } fn decode_partial(parent: &Child) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.bar() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "bar", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.bar()), + }); + } + if parent.quux() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "quux", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.quux()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); if buf.is_empty() { @@ -499,6 +523,14 @@ impl TryFrom<&GrandGrandChild> for Vec { impl GrandGrandChild { fn decode_partial(parent: &GrandChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.baz() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandGrandChild", + field: "baz", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.baz()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); if buf.is_empty() { diff --git a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_big_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_big_endian.rs index c79295d..5370aab 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_big_endian.rs @@ -275,6 +275,14 @@ impl TryFrom<&NormalChild> for Vec { impl NormalChild { fn decode_partial(parent: &Parent) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.v() != Enum8::A { + return Err(DecodeError::InvalidFieldValue { + packet: "NormalChild", + field: "v", + expected: "Enum8 :: A", + actual: format!("{:?}", parent.v()), + }); + } if buf.is_empty() { Ok(Self {}) } else { Err(DecodeError::TrailingBytes) } } pub fn encode_partial(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { @@ -337,6 +345,14 @@ impl TryFrom<&NormalGrandChild1> for Vec { impl NormalGrandChild1 { fn decode_partial(parent: &AliasChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.v() != Enum8::B { + return Err(DecodeError::InvalidFieldValue { + packet: "NormalGrandChild1", + field: "v", + expected: "Enum8 :: B", + actual: format!("{:?}", parent.v()), + }); + } if buf.is_empty() { Ok(Self {}) } else { Err(DecodeError::TrailingBytes) } } pub fn encode_partial(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { @@ -401,6 +417,14 @@ impl TryFrom<&NormalGrandChild2> for Vec { impl NormalGrandChild2 { fn decode_partial(parent: &AliasChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.v() != Enum8::C { + return Err(DecodeError::InvalidFieldValue { + packet: "NormalGrandChild2", + field: "v", + expected: "Enum8 :: C", + actual: format!("{:?}", parent.v()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); if buf.is_empty() { diff --git a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_little_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_little_endian.rs index c79295d..5370aab 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_alias_child_little_endian.rs @@ -275,6 +275,14 @@ impl TryFrom<&NormalChild> for Vec { impl NormalChild { fn decode_partial(parent: &Parent) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.v() != Enum8::A { + return Err(DecodeError::InvalidFieldValue { + packet: "NormalChild", + field: "v", + expected: "Enum8 :: A", + actual: format!("{:?}", parent.v()), + }); + } if buf.is_empty() { Ok(Self {}) } else { Err(DecodeError::TrailingBytes) } } pub fn encode_partial(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { @@ -337,6 +345,14 @@ impl TryFrom<&NormalGrandChild1> for Vec { impl NormalGrandChild1 { fn decode_partial(parent: &AliasChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.v() != Enum8::B { + return Err(DecodeError::InvalidFieldValue { + packet: "NormalGrandChild1", + field: "v", + expected: "Enum8 :: B", + actual: format!("{:?}", parent.v()), + }); + } if buf.is_empty() { Ok(Self {}) } else { Err(DecodeError::TrailingBytes) } } pub fn encode_partial(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { @@ -401,6 +417,14 @@ impl TryFrom<&NormalGrandChild2> for Vec { impl NormalGrandChild2 { fn decode_partial(parent: &AliasChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.v() != Enum8::C { + return Err(DecodeError::InvalidFieldValue { + packet: "NormalGrandChild2", + field: "v", + expected: "Enum8 :: C", + actual: format!("{:?}", parent.v()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); if buf.is_empty() { diff --git a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_big_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_big_endian.rs index 7e85540..e5497be 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_big_endian.rs @@ -171,6 +171,14 @@ impl TryFrom<&Child> for Vec { } impl Child { fn decode_partial(parent: &Parent) -> Result { + if parent.v() != Enum8::A { + return Err(DecodeError::InvalidFieldValue { + packet: "Child", + field: "v", + expected: "Enum8 :: A", + actual: format!("{:?}", parent.v()), + }); + } Ok(Self {}) } pub fn encode_partial(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { diff --git a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_little_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_little_endian.rs index 7e85540..e5497be 100644 --- a/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/packet_decl_parent_with_no_payload_little_endian.rs @@ -171,6 +171,14 @@ impl TryFrom<&Child> for Vec { } impl Child { fn decode_partial(parent: &Parent) -> Result { + if parent.v() != Enum8::A { + return Err(DecodeError::InvalidFieldValue { + packet: "Child", + field: "v", + expected: "Enum8 :: A", + actual: format!("{:?}", parent.v()), + }); + } Ok(Self {}) } pub fn encode_partial(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { diff --git a/pdl-compiler/tests/generated/rust/struct_decl_child_structs_big_endian.rs b/pdl-compiler/tests/generated/rust/struct_decl_child_structs_big_endian.rs index 2fda403..cbb1341 100644 --- a/pdl-compiler/tests/generated/rust/struct_decl_child_structs_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/struct_decl_child_structs_big_endian.rs @@ -222,6 +222,14 @@ impl TryFrom<&Bar> for Vec { impl Bar { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.a() != 100 { + return Err(DecodeError::InvalidFieldValue { + packet: "Bar", + field: "a", + expected: "100", + actual: format!("{:?}", parent.a()), + }); + } if buf.remaining() < 1 { return Err(DecodeError::InvalidLengthError { obj: "Bar", @@ -314,6 +322,14 @@ impl TryFrom<&Baz> for Vec { impl Baz { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.b() != Enum16::B { + return Err(DecodeError::InvalidFieldValue { + packet: "Baz", + field: "b", + expected: "Enum16 :: B", + actual: format!("{:?}", parent.b()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Baz", diff --git a/pdl-compiler/tests/generated/rust/struct_decl_child_structs_little_endian.rs b/pdl-compiler/tests/generated/rust/struct_decl_child_structs_little_endian.rs index 09bf722..e20953e 100644 --- a/pdl-compiler/tests/generated/rust/struct_decl_child_structs_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/struct_decl_child_structs_little_endian.rs @@ -222,6 +222,14 @@ impl TryFrom<&Bar> for Vec { impl Bar { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.a() != 100 { + return Err(DecodeError::InvalidFieldValue { + packet: "Bar", + field: "a", + expected: "100", + actual: format!("{:?}", parent.a()), + }); + } if buf.remaining() < 1 { return Err(DecodeError::InvalidLengthError { obj: "Bar", @@ -314,6 +322,14 @@ impl TryFrom<&Baz> for Vec { impl Baz { fn decode_partial(parent: &Foo) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.b() != Enum16::B { + return Err(DecodeError::InvalidFieldValue { + packet: "Baz", + field: "b", + expected: "Enum16 :: B", + actual: format!("{:?}", parent.b()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Baz", diff --git a/pdl-compiler/tests/generated/rust/struct_decl_grand_children_big_endian.rs b/pdl-compiler/tests/generated/rust/struct_decl_grand_children_big_endian.rs index 0650d32..7e2e80d 100644 --- a/pdl-compiler/tests/generated/rust/struct_decl_grand_children_big_endian.rs +++ b/pdl-compiler/tests/generated/rust/struct_decl_grand_children_big_endian.rs @@ -262,6 +262,14 @@ impl Child { } fn decode_partial(parent: &Parent) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.foo() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "Child", + field: "foo", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.foo()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Child", @@ -397,6 +405,22 @@ impl GrandChild { } fn decode_partial(parent: &Child) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.bar() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "bar", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.bar()), + }); + } + if parent.quux() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "quux", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.quux()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); let payload = Vec::from(payload); @@ -502,6 +526,14 @@ impl TryFrom<&GrandGrandChild> for Vec { impl GrandGrandChild { fn decode_partial(parent: &GrandChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.baz() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandGrandChild", + field: "baz", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.baz()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); let payload = Vec::from(payload); diff --git a/pdl-compiler/tests/generated/rust/struct_decl_grand_children_little_endian.rs b/pdl-compiler/tests/generated/rust/struct_decl_grand_children_little_endian.rs index eca914a..ee12130 100644 --- a/pdl-compiler/tests/generated/rust/struct_decl_grand_children_little_endian.rs +++ b/pdl-compiler/tests/generated/rust/struct_decl_grand_children_little_endian.rs @@ -262,6 +262,14 @@ impl Child { } fn decode_partial(parent: &Parent) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.foo() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "Child", + field: "foo", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.foo()), + }); + } if buf.remaining() < 2 { return Err(DecodeError::InvalidLengthError { obj: "Child", @@ -397,6 +405,22 @@ impl GrandChild { } fn decode_partial(parent: &Child) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.bar() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "bar", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.bar()), + }); + } + if parent.quux() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandChild", + field: "quux", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.quux()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); let payload = Vec::from(payload); @@ -502,6 +526,14 @@ impl TryFrom<&GrandGrandChild> for Vec { impl GrandGrandChild { fn decode_partial(parent: &GrandChild) -> Result { let mut buf: &[u8] = &parent.payload; + if parent.baz() != Enum16::A { + return Err(DecodeError::InvalidFieldValue { + packet: "GrandGrandChild", + field: "baz", + expected: "Enum16 :: A", + actual: format!("{:?}", parent.baz()), + }); + } let payload = buf.to_vec(); buf.advance(payload.len()); let payload = Vec::from(payload); diff --git a/pdl-runtime/src/lib.rs b/pdl-runtime/src/lib.rs index a5c7a78..a86e316 100644 --- a/pdl-runtime/src/lib.rs +++ b/pdl-runtime/src/lib.rs @@ -38,6 +38,13 @@ pub enum DecodeError { value: u64, type_: &'static str, }, + #[error("invalid field {packet}::{field} value, {expected} != {actual}")] + InvalidFieldValue { + packet: &'static str, + field: &'static str, + expected: &'static str, + actual: String, + }, #[error("expected child {expected}, got {actual}")] InvalidChildError { expected: &'static str, actual: String }, #[error("packet has trailing bytes")]