# Integer truncation of length fields

A wide length narrowed to a smaller or signed type before allocation, bounds check, or copy

_Area: Memory corruption and native code. Seen in 12 disclosed reports; 5 confirmed bounties, median $1,000, range $500-$10,000; latest 2026-10-22. Updated 2026-10-23._

## How it works
A length is computed in a wide type (size_t, 64-bit, or a full 32-bit value) but is stored, passed, or compared in a narrower or signed type. The allocation or bounds check uses the narrowed value while the copy loop or index arithmetic uses the original. The two disagree: the buffer is smaller than the data written, or a mask/index derived from the narrowed value points outside the buffer. The broken assumption is that "the length I checked is the length I use." The same mismatch appears when size arithmetic itself wraps (multiply-then-store in a small type, or signed overflow in a rounding formula), so the allocated size is tiny while the copy uses the full length.

## Where it shows up
- A size_t or 64-bit length passed to an API taking `int`, `u_int16_t`, or a bitfield-packed length [E1](https://bugbountylibrary.com/r/h1%3A1340942.md)[E2](https://bugbountylibrary.com/r/h1%3A175587.md)[E7](https://bugbountylibrary.com/r/h1%3A161189.md)[E8](https://bugbountylibrary.com/r/h1%3A140865.md)
- Structs that pack flags into high bits of a length field, with a sibling object keeping the full length [E3](https://bugbountylibrary.com/r/p0%3ACVE-2021-33742.md)
- A dimension truncated for allocation but the original width forwarded to a downstream parser [E4](https://bugbountylibrary.com/r/p0%3ACVE-2020-15999.md)
- Signed length forwarded to a size_t parameter, sign-extending to a huge value [E2](https://bugbountylibrary.com/r/h1%3A175587.md)[E8](https://bugbountylibrary.com/r/h1%3A140865.md)
- Attacker-controlled on-disk, protocol, or IOCTL length fields feeding allocation [E1](https://bugbountylibrary.com/r/h1%3A1340942.md)[E5](https://bugbountylibrary.com/r/p0%3ACVE-2020-17087.md)
- Size arithmetic that wraps: multiply-then-store in USHORT [E5](https://bugbountylibrary.com/r/p0%3ACVE-2020-17087.md), unsigned length cast to signed int for rounding [E6](https://bugbountylibrary.com/r/h1%3A146360.md)

## How to review
Trace each length from its source to every use. The invariant: the value used to size the buffer, the value used to bound the copy, and the value used to advance the index must be the same width and the same number. Check:
- Every narrowing conversion (size_t→int, 32→16 bits, 32→25-bit bitfield) between size computation and allocation/copy [E1](https://bugbountylibrary.com/r/h1%3A1340942.md)[E2](https://bugbountylibrary.com/r/h1%3A175587.md)[E3](https://bugbountylibrary.com/r/p0%3ACVE-2021-33742.md)[E4](https://bugbountylibrary.com/r/p0%3ACVE-2020-15999.md)[E7](https://bugbountylibrary.com/r/h1%3A161189.md)[E8](https://bugbountylibrary.com/r/h1%3A140865.md)
- Whether a negative narrowed length bypasses `remain >= len` style checks [E2](https://bugbountylibrary.com/r/h1%3A175587.md)[E8](https://bugbountylibrary.com/r/h1%3A140865.md)
- Whether a mask like `size & 0x1FFFFFF` drops high bits while a sibling keeps the full length [E3](https://bugbountylibrary.com/r/p0%3ACVE-2021-33742.md)
- Whether a dimension truncated for allocation is also truncated before being handed to a downstream parser [E4](https://bugbountylibrary.com/r/p0%3ACVE-2020-15999.md)
- Whether size arithmetic can wrap before allocation (multiply into small type, signed rounding) [E5](https://bugbountylibrary.com/r/p0%3ACVE-2020-17087.md)[E6](https://bugbountylibrary.com/r/h1%3A146360.md)
- A benign explicit cast is safe only if a range check on the same value precedes it and the checked value is the one used downstream; flag casts without that guard
- Compare sizeof() of the two types at each boundary; grep with -Wconversion, -Wshorten-64-to-32, and UBSan

## Variants seen
- 64-bit size truncated to 32-bit int allocation parameter, full length still copied [E1](https://bugbountylibrary.com/r/h1%3A1340942.md)
- size_t length narrowed to signed int, going negative and bypassing the bounds branch into memcpy [E2](https://bugbountylibrary.com/r/h1%3A175587.md)
- 25-bit bitfield truncation in a packed length field vs. full length in sibling; copy index advances by full length [E3](https://bugbountylibrary.com/r/p0%3ACVE-2021-33742.md)
- 16-bit truncation of width/height for allocation, full 32-bit dimensions passed to libpng [E4](https://bugbountylibrary.com/r/p0%3ACVE-2020-15999.md)
- Attacker length times 6 stored in USHORT, under-allocating a pool buffer [E5](https://bugbountylibrary.com/r/p0%3ACVE-2020-17087.md)
- Unsigned length cast to signed int for rounding arithmetic, wrapping to a small allocation [E6](https://bugbountylibrary.com/r/h1%3A146360.md)
- 64-bit zend_long truncated to 32-bit colorsWanted, wrapping to 0 and under-allocating [E7](https://bugbountylibrary.com/r/h1%3A161189.md)

## Impact and severity
Led to heap and stack overflows, OOB reads and writes, kernel code execution [E1](https://bugbountylibrary.com/r/h1%3A1340942.md)[E5](https://bugbountylibrary.com/r/p0%3ACVE-2020-17087.md), sandbox escape via Office docs [E3](https://bugbountylibrary.com/r/p0%3ACVE-2021-33742.md), heap overflow exploited as a Chrome zero-day [E4](https://bugbountylibrary.com/r/p0%3ACVE-2020-15999.md), and corrupted string ZVALs enabling RCE [E8](https://bugbountylibrary.com/r/h1%3A140865.md). Severity rises when the length is attacker-controlled and the narrowed value silently passes existing checks, so no error is raised before corruption.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1340942.md
- E2: https://bugbountylibrary.com/r/h1%3A175587.md
- E3: https://bugbountylibrary.com/r/p0%3ACVE-2021-33742.md
- E4: https://bugbountylibrary.com/r/p0%3ACVE-2020-15999.md
- E5: https://bugbountylibrary.com/r/p0%3ACVE-2020-17087.md
- E6: https://bugbountylibrary.com/r/h1%3A146360.md
- E7: https://bugbountylibrary.com/r/h1%3A161189.md
- E8: https://bugbountylibrary.com/r/h1%3A140865.md
