[fidl][rust] Validate v2 inlining in rust

The rust decoder is currently able to decode inlinable values regardless
of whether they are inlined or sent out of line. This CL validates that
these values are correctly sent inline.

Bug: 80620
Bug: 81180
Change-Id: Iead03f46a8074f954d44829249483aed84744f90
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/560042
Commit-Queue: Benjamin Prosnitz <bprosnitz@google.com>
Reviewed-by: Mitchell Kember <mkember@google.com>
diff --git a/src/lib/fidl/rust/fidl/src/encoding.rs b/src/lib/fidl/rust/fidl/src/encoding.rs
index 4d884bb..9cdddd6 100644
--- a/src/lib/fidl/rust/fidl/src/encoding.rs
+++ b/src/lib/fidl/rust/fidl/src/encoding.rs
@@ -3023,6 +3023,13 @@
                             if inlined {
                                 decode_inner(decoder, next_offset)?;
                             } else {
+                                match decoder.context().wire_format_version {
+                                    $crate::encoding::WireFormatVersion::V2 =>
+                                        if decoder.inline_size_of::<$member_ty>() <= 4 {
+                                            return Err($crate::Error::InvalidInlineBitInEnvelope);
+                                        },
+                                    _ => (),
+                                };
                                 decoder.read_out_of_line(
                                     decoder.inline_size_of::<$member_ty>(),
                                     decode_inner,
@@ -3235,6 +3242,14 @@
         if inlined {
             decode_inner(decoder, offset + 8)?;
         } else {
+            match decoder.context().wire_format_version {
+                WireFormatVersion::V2 => {
+                    if member_inline_size <= 4 {
+                        return Err(Error::InvalidInlineBitInEnvelope);
+                    }
+                }
+                _ => (),
+            };
             decoder.read_out_of_line(member_inline_size, decode_inner)?;
             if decoder.next_out_of_line() != (next_out_of_line + (num_bytes as usize)) {
                 return Err(Error::InvalidNumBytesInEnvelope);
@@ -3463,6 +3478,13 @@
                 if inlined {
                     decode_inner(decoder, offset + 8)?;
                 } else {
+                    match decoder.context().wire_format_version {
+                        $crate::encoding::WireFormatVersion::V2 =>
+                            if member_inline_size <= 4 {
+                                return Err($crate::Error::InvalidInlineBitInEnvelope);
+                            },
+                        _ => (),
+                    };
                     decoder.read_out_of_line(member_inline_size, decode_inner)?;
                     if decoder.next_out_of_line() != (next_out_of_line + (num_bytes as usize)) {
                         return Err($crate::Error::InvalidNumBytesInEnvelope);
diff --git a/src/lib/fidl/rust/fidl/src/error.rs b/src/lib/fidl/rust/fidl/src/error.rs
index d2fff68..8b55781 100644
--- a/src/lib/fidl/rust/fidl/src/error.rs
+++ b/src/lib/fidl/rust/fidl/src/error.rs
@@ -123,6 +123,10 @@
     #[error("Invalid presence indicator.")]
     InvalidPresenceIndicator,
 
+    /// An inline bit is unset when the type size is known to be <= 4 bytes.
+    #[error("Invalid inline bit in envelope.")]
+    InvalidInlineBitInEnvelope,
+
     /// An envelope in the FIDL message had an unexpected number of bytes.
     #[error("Invalid number of bytes in FIDL envelope.")]
     InvalidNumBytesInEnvelope,
diff --git a/src/tests/fidl/conformance_suite/padding.gidl b/src/tests/fidl/conformance_suite/padding.gidl
index cea6c06e..2ca7a4b3 100644
--- a/src/tests/fidl/conformance_suite/padding.gidl
+++ b/src/tests/fidl/conformance_suite/padding.gidl
@@ -1362,13 +1362,7 @@
             0x01,
             0xee, padding:6, // should be padding:7
         ],
-        v2 = [
-            num(1):8,
-            num(8):4, num(0):2, num(0):2,
-
-            0x01,
-            0xee, padding:6, // should be padding:7
-        ],
+        // v2 inlines so it doesn't have the same padding bytes as v1
     },
     err = INVALID_PADDING_BYTE,
 }
@@ -1385,13 +1379,7 @@
             0x01,
             padding:6, 0xee, // should be padding:7
         ],
-        v2 = [
-            num(1):8,
-            num(8):4, num(0):2, num(0):2,
-
-            0x01,
-            padding:6, 0xee, // should be padding:7
-        ],
+        // v2 inlines so it doesn't have the same padding bytes as v1
     },
     err = INVALID_PADDING_BYTE,
 }
diff --git a/src/tests/fidl/conformance_suite/tables.gidl b/src/tests/fidl/conformance_suite/tables.gidl
index f3a22a6..349ced8 100644
--- a/src/tests/fidl/conformance_suite/tables.gidl
+++ b/src/tests/fidl/conformance_suite/tables.gidl
@@ -174,7 +174,7 @@
 // Tests decode of a value in a table that can be represented inline in the
 // envelope but is incorrectly using the out of line representation.
 decode_failure("TableOutOfLineEnvelopeWhenInlineRequired") {
-    bindings_allowlist = [go,transformer],
+    bindings_allowlist = [go,rust,transformer],
     type = TableFieldInlinedStruct,
     bytes = {
         v2 = [
diff --git a/src/tests/fidl/conformance_suite/union.gidl b/src/tests/fidl/conformance_suite/union.gidl
index 96a7ee6..a2c2c4f 100644
--- a/src/tests/fidl/conformance_suite/union.gidl
+++ b/src/tests/fidl/conformance_suite/union.gidl
@@ -418,7 +418,7 @@
 // Tests decode of a value in a union that can be represented inline in the
 // envelope but is incorrectly using the out of line representation.
 decode_failure("UnionOutOfLineEnvelopeWhenInlineRequired") {
-    bindings_allowlist = [go],
+    bindings_allowlist = [go,rust],
     type = EnvelopeInliningTestUnionStruct,
     bytes = {
         v2 = [
diff --git a/tools/fidl/gidl/rust/conformance.go b/tools/fidl/gidl/rust/conformance.go
index e6229c7..815384f 100644
--- a/tools/fidl/gidl/rust/conformance.go
+++ b/tools/fidl/gidl/rust/conformance.go
@@ -390,6 +390,7 @@
 	gidlir.StringNotUtf8:                "Utf8Error",
 	gidlir.StringTooLong:                "OutOfRange",
 	gidlir.UnionFieldNotSet:             "UnknownUnionTag",
+	gidlir.InvalidInlineBitInEnvelope:   "InvalidInlineBitInEnvelope",
 }
 
 func rustErrorCode(code gidlir.ErrorCode) (string, error) {