# Unsigned size check wraps to overflow

Unsigned subtraction or comparison in length checks where the operand can be smaller than the constant

_Area: Memory corruption and native code. Seen in 4 disclosed reports; one confirmed bounty: $31,337; latest 2026-10-20. Updated 2026-10-23._

## How it works
A length or bound is computed with unsigned arithmetic, e.g. `PAGE_SIZE - 2 - size` or `uncompressed_filesize - 8`. The code assumes the left operand is always at least as large as what is subtracted. When an attacker-controlled value reaches or exceeds the constant, the subtraction wraps to a huge unsigned number. A check like `len > limit` then passes, or a copy length becomes enormous, and the heap copy or read runs past the buffer. The broken assumption is that the size field is bounded below by the header/constant it is compared against.

## Where it shows up
- Bounds checks of the form `len > CONST - size` or `size - CONST` with unsigned types [E1](https://bugbountylibrary.com/r/blog%3Awillsroot-io-2022-01-cve-2022-0185-html.md)[E3](https://bugbountylibrary.com/r/h1%3A167896.md)[E4](https://bugbountylibrary.com/r/h1%3A167895.md)
- Lengths derived from attacker-controlled size fields (file sizes, entry headers) minus a fixed header size [E3](https://bugbountylibrary.com/r/h1%3A167896.md)[E4](https://bugbountylibrary.com/r/h1%3A167895.md)
- Paired offsets/lengths from a library where `end - start` is computed without checking `start <= end` [E2](https://bugbountylibrary.com/r/h1%3A141839.md)
- Parser code where the same field feeds both allocation and length arithmetic [E4](https://bugbountylibrary.com/r/h1%3A167895.md)

## How to review
For every subtraction feeding a length or bound, check the type is unsigned and ask whether the subtrahend can be smaller than the constant. The invariant that must hold: before any `a - b` used as a length or in a comparison, `a >= b` is proven, or the arithmetic is done in a signed/wider type with an explicit lower-bound check. For library-returned offset pairs, verify `start <= end` at each consumer [E2](https://bugbountylibrary.com/r/h1%3A141839.md). Confirm the check uses the same value and type as the copy.

## Variants seen
- Kernel bound check `len > PAGE_SIZE - 2 - size` underflowing when size approaches the page limit [E1](https://bugbountylibrary.com/r/blog%3Awillsroot-io-2022-01-cve-2022-0185-html.md)
- Regex engine returning `start > end` offsets, breaking `end - start` in wrappers [E2](https://bugbountylibrary.com/r/h1%3A141839.md)
- Tar phar `uncompressed_filesize - 8` underflowing for sub-8-byte signatures [E3](https://bugbountylibrary.com/r/h1%3A167896.md)
- Zip phar same pattern, with `sig + 8` also past the buffer [E4](https://bugbountylibrary.com/r/h1%3A167895.md)

## Impact and severity
Led to heap out-of-bounds write/read: unprivileged kernel heap overflow enabling container escape and root [E1](https://bugbountylibrary.com/r/blog%3Awillsroot-io-2022-01-cve-2022-0185-html.md); PHP heap overflow and OOB read [E2](https://bugbountylibrary.com/r/h1%3A141839.md)[E3](https://bugbountylibrary.com/r/h1%3A167896.md)[E4](https://bugbountylibrary.com/r/h1%3A167895.md). Severity is high where the overflow is writable and reachable unprivileged [E1](https://bugbountylibrary.com/r/blog%3Awillsroot-io-2022-01-cve-2022-0185-html.md). Worse when the wrapped value also drives an offset into the buffer [E4](https://bugbountylibrary.com/r/h1%3A167895.md).

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Awillsroot-io-2022-01-cve-2022-0185-html.md
- E2: https://bugbountylibrary.com/r/h1%3A141839.md
- E3: https://bugbountylibrary.com/r/h1%3A167896.md
- E4: https://bugbountylibrary.com/r/h1%3A167895.md
