feat: IDL support for custom vector length discriminators#4148
feat: IDL support for custom vector length discriminators#4148Otter-0x4ka5h wants to merge 11 commits into
Conversation
|
Someone is attempting to deploy a commit to the Solana Foundation Team on Vercel. A member of the Team first needs to authorize it. |
acheroncrypto
left a comment
There was a problem hiding this comment.
Have you seen #4076 (comment) ?
|
@acheroncrypto sorry for the confusion here, will make changes. I think I pushed the wrong commit. was implemented with impl IdlSerialization {
/// Returns the number of bytes used for Vec length prefix in this serialization format.
pub fn vec_length_bytes(&self) -> usize {
match self {
IdlSerialization::Borsh => 4,
IdlSerialization::BorshU8 => 1,
IdlSerialization::BorshU16 => 2,
IdlSerialization::Bytemuck | IdlSerialization::BytemuckUnsafe => {
8
}
IdlSerialization::Custom(_) => {
4
}
}
}
} |
re-pushed the correct changes |
acheroncrypto
left a comment
There was a problem hiding this comment.
Overall, I think the approach is correct, but there are a few more things that need to get addressed:
-
The serialization should apply to all unsized types; otherwise, it would be very unintuitive (on-chain programs can't handle more than 2 bytes length
borshde/serialization anyway) -
How to use the new serialization specs?
- In programs, how to use them without having to manually implement the de/serialization?
- How to include this information (the
serializationfield) in the IDL conveniently?
Perhaps something like a generic
#[serialization]attribute or a specific#[custom_borsh]similar to the#[zero_copy]attribute? -
declare_program!also needs to support these new serialization methods, e.g. in:Depending on the resolution of the last point, something like:
IdlSerialization::BorshU8 => quote!(#[custom_borsh(u8)]), IdlSerialization::BorshU16 => quote!(#[custom_borsh(u16)]),
|
Deployment failed with the following error: |
51767f8 to
7256289
Compare
6b09543 to
1059a19
Compare
Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com>
1059a19 to
5ad68dc
Compare
There was a problem hiding this comment.
4 issues found across 4 files
| Severity | Count |
|---|---|
| 🟡 Medium | 3 |
| 🟢 Low | 1 |
Comments Outside Diff (4)
🟡 Medium: Incorrect owner validation in init_if_needed for token accounts
Location: lang/syn/src/codegen/accounts/constraints.rs:597-1177
In generate_constraint_init_group for InitKind::Token, the if_needed logic captures the account's owner before the initialization CPI. If the account is being initialized (i.e., it was previously owned by the system program), the subsequent validation check owner_program != &token_program.key() compares the original owner (system program) against the expected owner (token program). This will cause the validation to fail for any account initialized via init_if_needed in this context, potentially rendering the feature unusable or causing unexpected transaction failures.
🟡 Medium: Stack Overflow via Deeply Nested Array Types in IDL Parsing
Location: idl/spec/src/lib.rs:384-433
The array_from_str function in anchor/idl/spec/src/lib.rs uses recursion to parse nested array types (e.g., [[[[u8; 1]; 1]; 1]; 1]). An attacker providing a deeply nested type string can trigger a stack overflow, leading to a denial-of-service (DoS) condition. While this is a parsing function, if it is exposed to user-provided IDL definitions, it can be exploited.
🟢 Low: Client-side panic via malformed RPC log signature
Duplicate of: Unsafe use of unwrap() in log parsing logic leading to potential DoS
Location: client/src/lib.rs:808-849
The parse_logs_response function uses .unwrap() when parsing the transaction signature from the RPC node's response (logs.value.signature.parse().unwrap()). If a client is configured to connect to a malicious or compromised RPC node, the node can return an invalid signature string, causing the client application to panic and crash. This results in a denial-of-service for the client.
🟡 Medium: Unbounded loop in Borsh deserialization for zero-span layouts
Location: ts/packages/borsh/src/index.ts:278-322
The decodeCollectionValues function in anchor/ts/packages/borsh/src/index.ts fails to validate the count parameter when elementLayout.span is 0. If a malicious payload specifies a very large count for a layout with a zero-byte span, the loop will execute count times, leading to excessive CPU consumption and a potential Denial of Service (DoS). While the loop itself does not allocate memory proportional to count in this specific implementation, the repeated execution of elementLayout.decode and the loop overhead for a large count can block the event loop or consume significant compute resources.
Steps to Reproduce
- Define a layout with span 0.
- Create a buffer with a large length prefix (e.g., 0xFFFFFFFF).
- Attempt to decode this buffer using a collection layout (vec, array, map) containing the zero-span layout.
- Observe the CPU usage spike as the loop executes without consuming any bytes.
# This is a conceptual representation of the trigger.
# Assuming a layout exists where span is 0.
# Buffer: [0xFF, 0xFF, 0xFF, 0xFF, ...] (4 bytes for count)
# The code will loop 4,294,967,295 times.# No script needed, the vulnerability is a logic flaw in the validation check.
# An attacker provides a buffer where the length prefix is a large number (e.g., 0xFFFFFFFF)
# and the following data structure has a zero-span layout.
# The `assertCollectionFitsRemaining` will return without throwing,
# and the subsequent loop in `decodeCollectionValues` will execute 4 billion times.N/APoC Url: N/A
Anchor IDLs now support vectors with custom length discriminators (u8, u16, or u32), not just the default u32. This helps optimize account size for programs that use compact vectors.
Fixes #4076