From ef686608af9d2284d5cc662a6dc860a1b53e9ed5 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Tue, 17 Mar 2026 20:33:13 -0400 Subject: [PATCH 1/4] fix: restore expect/wire types per element in primitive vec fast path When decoding a trailing/extra argument that is a primitive vector, `deserialize_ignored_any` relies on `expect_type`/`wire_type` being set to the element type. The fast path introduced in #712 skipped setting these, causing `deserialize_ignored_any` to see the outer `Vec` type and attempt to decode a nested vector instead of a scalar, corrupting the byte stream. Fix: always set `expect_type`/`wire_type` to the element type before calling `seed.deserialize`, and only skip `add_cost(3)` in the fast path. The `primitive_impl!` macro checks `primitive_vec_fast_path` before touching these types, so normal decode performance is unchanged. Co-Authored-By: Claude Sonnet 4.6 --- rust/candid/src/de.rs | 10 ++++------ rust/candid/tests/compatibility_vectors.rs | 9 +++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/rust/candid/src/de.rs b/rust/candid/src/de.rs index 710c757b..341d9ae9 100644 --- a/rust/candid/src/de.rs +++ b/rust/candid/src/de.rs @@ -1241,14 +1241,12 @@ impl<'de> de::SeqAccess<'de> for Compound<'_, 'de> { return Ok(None); } *len -= 1; - if exact_primitive.is_some() { - seed.deserialize(&mut *self.de).map(Some) - } else { + self.de.expect_type = expect.clone(); + self.de.wire_type = wire.clone(); + if exact_primitive.is_none() { self.de.add_cost(3)?; - self.de.expect_type = expect.clone(); - self.de.wire_type = wire.clone(); - seed.deserialize(&mut *self.de).map(Some) } + seed.deserialize(&mut *self.de).map(Some) } Style::Struct { ref expect, diff --git a/rust/candid/tests/compatibility_vectors.rs b/rust/candid/tests/compatibility_vectors.rs index f33254fd..b76e9904 100644 --- a/rust/candid/tests/compatibility_vectors.rs +++ b/rust/candid/tests/compatibility_vectors.rs @@ -76,3 +76,12 @@ fn bulk_encode_primitive_vectors_round_trip() { let empty: Vec = vec![]; assert_eq!(Decode!(&Encode!(&empty).unwrap(), Vec).unwrap(), empty); } + +#[test] +fn primitive_vector_is_extra_args() { + let extra = vec![1i16, 2, 3]; + let bytes = Encode!(&123u8, &extra).unwrap(); + + let decoded_values = Decode!(&bytes, u8).unwrap(); + assert_eq!(decoded_values, 123); +} From ca71d3d6013487ab5860943517081ad3a17f602b Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Tue, 17 Mar 2026 20:33:46 -0400 Subject: [PATCH 2/4] chore: update changelog for primitive vec fast-path fix Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2fccf6..77070607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2026-03-17 + +### Candid 0.10.25 (patch) + +* Bug fixes: + + Fix decoding failure when a trailing/extra argument is a primitive vector (`vec int8/16/32/64`, `vec nat8/16/32/64`, `vec float32/64`, `vec bool`). The fast-path optimization introduced in #712 skipped updating `expect_type`/`wire_type` per element, causing `deserialize_ignored_any` to misidentify the element type and corrupt the byte stream. + ## 2026-03-16 ### Candid 0.10.25 From afea579eedb5ad9a7323beac5e609ac7694a7c96 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Tue, 17 Mar 2026 20:41:08 -0400 Subject: [PATCH 3/4] chore: update changelog for 0.10.26 Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77070607..2533fe9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ # Changelog -## 2026-03-17 +## 2026-03-18 -### Candid 0.10.25 (patch) +### Candid 0.10.26 * Bug fixes: - + Fix decoding failure when a trailing/extra argument is a primitive vector (`vec int8/16/32/64`, `vec nat8/16/32/64`, `vec float32/64`, `vec bool`). The fast-path optimization introduced in #712 skipped updating `expect_type`/`wire_type` per element, causing `deserialize_ignored_any` to misidentify the element type and corrupt the byte stream. + + Fix decoding failure when a trailing argument is a primitive vector ## 2026-03-16 From 314e5633acd007e5ecf8ec89654b4d8353571be1 Mon Sep 17 00:00:00 2001 From: Linwei Shang Date: Tue, 17 Mar 2026 20:43:30 -0400 Subject: [PATCH 4/4] chore: bump version to 0.10.26 Co-Authored-By: Claude Sonnet 4.6 --- Cargo.lock | 8 ++++---- rust/bench/Cargo.lock | 4 ++-- rust/candid/Cargo.toml | 4 ++-- rust/candid_derive/Cargo.toml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47cac772..8eb17f36 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,13 +209,13 @@ dependencies = [ [[package]] name = "candid" -version = "0.10.25" +version = "0.10.26" dependencies = [ "anyhow", "bincode", "binread", "byteorder", - "candid_derive 0.10.25", + "candid_derive 0.10.26", "candid_parser 0.3.0", "hex", "ic_principal 0.1.2", @@ -247,7 +247,7 @@ dependencies = [ [[package]] name = "candid_derive" -version = "0.10.25" +version = "0.10.26" dependencies = [ "lazy_static", "proc-macro2 1.0.86", @@ -280,7 +280,7 @@ version = "0.3.0" dependencies = [ "anyhow", "arbitrary", - "candid 0.10.25", + "candid 0.10.26", "codespan-reporting", "console", "convert_case", diff --git a/rust/bench/Cargo.lock b/rust/bench/Cargo.lock index 0700075b..ef54d1e2 100644 --- a/rust/bench/Cargo.lock +++ b/rust/bench/Cargo.lock @@ -156,7 +156,7 @@ dependencies = [ [[package]] name = "candid" -version = "0.10.25" +version = "0.10.26" dependencies = [ "anyhow", "binread", @@ -177,7 +177,7 @@ dependencies = [ [[package]] name = "candid_derive" -version = "0.10.25" +version = "0.10.26" dependencies = [ "lazy_static", "proc-macro2", diff --git a/rust/candid/Cargo.toml b/rust/candid/Cargo.toml index 222b0409..5655ede0 100644 --- a/rust/candid/Cargo.toml +++ b/rust/candid/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "candid" # sync with the version in `candid_derive/Cargo.toml` -version = "0.10.25" +version = "0.10.26" edition = "2021" rust-version.workspace = true authors = ["DFINITY Team"] @@ -16,7 +16,7 @@ keywords = ["internet-computer", "idl", "candid", "dfinity"] include = ["src", "Cargo.toml", "LICENSE", "README.md"] [dependencies] -candid_derive = { path = "../candid_derive", version = "=0.10.25" } +candid_derive = { path = "../candid_derive", version = "=0.10.26" } ic_principal = { path = "../ic_principal", version = "0.1.0" } binread = { version = "2.2", features = ["debug_template"] } byteorder = "1.5.0" diff --git a/rust/candid_derive/Cargo.toml b/rust/candid_derive/Cargo.toml index 473dc729..9aee2aa3 100644 --- a/rust/candid_derive/Cargo.toml +++ b/rust/candid_derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "candid_derive" # sync with the version in `candid/Cargo.toml` -version = "0.10.25" +version = "0.10.26" edition = "2021" rust-version.workspace = true authors = ["DFINITY Team"]