# Unvalidated calldata to trusted module

Generic module.call(data) or payload decode without binding calldata to expected type or method

_Area: Smart contract access control and control flow. Seen in 2 disclosed reports; latest 2026-09-21. Updated 2026-11-01._

## How it works
A contract trusts a module because it is whitelisted, then forwards caller-supplied calldata to it without checking what that calldata does. The whitelist answers "is this contract allowed?" but not "is this operation allowed?" When the trusted module is itself a powerful contract (a token, a settlement handler), any function it exposes becomes reachable by the caller. The broken assumption: that a trusted target implies a trusted action.

## Where it shows up
- A loop or helper doing `module.call(data)` / `call{value}` where `data` comes from the caller and only `moduleWhitelist[module]` and `isContract` are checked [E1](https://bugbountylibrary.com/r/dhl%3A20260921-gaslessreservoirenabler.md)
- Token contracts (e.g. WETH) or other high-value contracts present on the module whitelist [E1](https://bugbountylibrary.com/r/dhl%3A20260921-gaslessreservoirenabler.md)
- Cross-chain or settlement handlers that decode a payload and act on it without asserting a method/type discriminator [E2](https://bugbountylibrary.com/r/c4%3A2024-08-chakra%3AM-02.md)
- Parallel implementations (Cairo vs Solidity, or two code paths) where one side validates and the other does not [E2](https://bugbountylibrary.com/r/c4%3A2024-08-chakra%3AM-02.md)
- Raw account buffers decoded as TLV without first unpacking the base structure [E3](https://bugbountylibrary.com/r/cantina%3Atensor_nov2024%3A3.2.4.md)

## How to review
For every call into a trusted module, ask: is the calldata constrained to a fixed selector and argument shape, or fully caller-controlled? The invariant: the set of operations reachable through the trusted target must equal the set the protocol intends, not everything the target exposes. Concretely, check that the selector is pinned, arguments are bound to the caller's own authority, and no token/asset contract sits on the whitelist. For decoders, confirm a method/type check precedes any mint, unlock or transfer [E2](https://bugbountylibrary.com/r/c4%3A2024-08-chakra%3AM-02.md), and that TLV access uses the correct base-aware accessor [E3](https://bugbountylibrary.com/r/cantina%3Atensor_nov2024%3A3.2.4.md).

## Variants seen
- Whitelisted token reachable via generic `module.call(data)`, enabling `transferFrom` on standing approvals [E1](https://bugbountylibrary.com/r/dhl%3A20260921-gaslessreservoirenabler.md)
- Missing method-id assertion so non-transfer messages are processed as transfers [E2](https://bugbountylibrary.com/r/c4%3A2024-08-chakra%3AM-02.md)
- Wrong TLV decode path causing a filter to always fail (availability, not theft) [E3](https://bugbountylibrary.com/r/cantina%3Atensor_nov2024%3A3.2.4.md)

## Impact and severity
E1 drained 466 victims' approvals for 8.72 WETH — direct fund loss, high severity. E2 allowed unintended mint/unlock of tokens, high severity. E3 broke a bid filter, a functional/DoS defect. Severity rises when the trusted target holds or can move assets and when approvals are standing.

## Why fixes fail
E2 shows a fix present on one implementation (Cairo) but absent on the parallel one (Solidity); E3 shows a correct accessor existing elsewhere but not used on the affected path. Fixing one call site while leaving sibling paths unvalidated leaves the class open.

## Evidence
- E1: https://bugbountylibrary.com/r/dhl%3A20260921-gaslessreservoirenabler.md
- E2: https://bugbountylibrary.com/r/c4%3A2024-08-chakra%3AM-02.md
- E3: https://bugbountylibrary.com/r/cantina%3Atensor_nov2024%3A3.2.4.md
