How 13 Bytes Freeze Google Perfetto: An Integer-Overflow DoS Behind Billions of Devices
Here's a fun one. I found a way to make a tiny file - just 13 bytes, smaller than this sentence - completely freeze one of Google's most widely used engineering tools. Not crash it with a bang. Freeze it. Pin a CPU core at 100% and make it spin forever, going nowhere, until you kill it.
The tool is called Perfetto, and once you understand where it lives, this stops being a curiosity and starts being a genuinely useful lesson about how software breaks - one that's worth reading whether you write code, run a security team, or sign the budget.
Let me walk you through it in plain English.
First, what is Perfetto - and why should you care?
Most people have never heard of Perfetto, yet it quietly runs near a staggering number of devices.
Perfetto is Google's open-source system tracing toolkit. Think of it as a flight recorder for software: it captures a detailed, timestamped record of everything a device is doing - which app woke up, which function ran, where the milliseconds went. Engineers use those traces to hunt down why an app is slow, why a phone drains battery, or why a service stutters.
The important part: Perfetto is the tracing system baked into Android - the operating system running on billions of active devices worldwide - and it's used inside Chrome and countless backend systems too. Google even hosts a free web viewer at ui.perfetto.dev, where anyone can drag in a trace file and explore it right in the browser.
So we're not talking about some obscure hobby project. We're talking about a piece of core engineering plumbing that sits behind a huge slice of modern computing. Which is exactly why a "small" bug in it is worth a second look.
The bug: when a number counts backwards
At the heart of Perfetto is a component called the trace processor - the part that reads a trace file and makes sense of it. Trace files are packed in a compact binary format, so the trace processor walks through them piece by piece, and for each piece it basically asks: "How long is this chunk? Okay, skip ahead that many bytes and look at the next one."
That "how long is this chunk?" number comes straight from the file - which means an attacker gets to choose it.
Here's where it goes wrong. The code did roughly this:
position + length = where the next chunk starts.
Computers store numbers in fixed-size boxes. When a number gets bigger than its box can hold, it doesn't error out - it silently wraps around to zero, like a car odometer rolling from 999,999 back to 000,000. Security folks call this an integer overflow.
I handed Perfetto a chunk whose declared length was enormous - near the maximum a 64-bit number can hold. When the code added that giant length to the current position, the result overflowed its box and wrapped all the way around to zero. The safety check that was supposed to catch a too-long chunk looked at that zero and happily concluded "yep, that fits." The code then advanced its position by zero bytes - so it didn't move at all - and looped back to read the same chunk again. And again. And again. Forever.
One malformed length turned the whole engine into a dog chasing its own tail.
The 13-byte trap
The proof was almost comically small. Thirteen carefully chosen bytes - a valid-looking header followed by that one poisoned length field - were enough to trigger the loop.
Run it through Perfetto's command-line tool and the process hangs at 100% CPU; you have to kill it manually. Drop the same file into the ui.perfetto.dev web viewer and the browser's background worker spins forever: the progress bar never finishes, the trace never loads, and one CPU core roars until you refresh the page.
No exotic setup. No special permissions. Just a tiny file and a tool that trusted a number it shouldn't have.
Watch it happen - the 13-byte file hangs Perfetto at ~100% CPU:
So… how bad is it, really?
Let me be straight with you, because honesty is the whole point of good security writing.
This is a denial-of-service bug - it makes the tool hang and burn CPU. It is not a way to steal data, and it is not remote code execution. Nobody's taking over your phone with this. If a bug report ever tells you a hung dev tool means "billions of devices compromised," be skeptical - that's not what happened here.
But "just a hang" still matters, and here's the realistic threat model:
- Shared trace files are untrusted input. Traces get emailed around, attached to bug reports, and shared as links. A support engineer or developer opening a booby-trapped trace gets a frozen tab or a stuck tool.
- Automated pipelines. Plenty of teams feed traces into scripts and CI systems for analysis. A single poisoned file can wedge a job at 100% CPU with no natural timeout.
- It's a canary. The same class of mistake - trusting a length or size straight from a file - is how far nastier bugs (memory corruption, real RCE) begin. Finding and fixing the "harmless" version closes a door before someone walks through it.
In short: low drama, real principle. Availability is part of security, and "a tiny input should never be able to make my program run forever" is a rule that holds from a phone all the way up to a data center.
Why this should interest you - whoever you are
- If you're just starting out: this is one of the cleanest real-world examples of an integer overflow you'll find. A number got too big, wrapped to zero, defeated a safety check, and hung a world-class tool. Remember it - you'll meet this pattern for the rest of your career.
- If you're a senior engineer: the lesson is never trust a length from untrusted data. Validate it before you do arithmetic with it, use overflow-safe math, and make sure your parsing loops are guaranteed to make forward progress. Fuzzing - throwing random and malformed inputs at your parsers - catches exactly this.
- If you're a CXO or security leader: your developer and telemetry tooling is part of your attack surface. The files your teams open - traces, logs, profiles, crash dumps - arrive from the outside world and deserve the same suspicion as any other input. Cheap habits (input validation, fuzzing, safe defaults) prevent expensive incidents. And when your engineers find issues in the tools you depend on, reporting them responsibly makes the whole ecosystem safer - including you.
The fix, and the takeaway
The good news: the fix is small and clean. Check the length before the arithmetic, cap it to what's actually left in the file, and make the loop refuse to stand still. If a chunk claims to be impossibly long, reject the file instead of chasing it forever - fail safely, not endlessly.
I reported this to the Perfetto team, and it was fixed and shipped in Perfetto v58.2, where the release notes list it plainly as "a hang when skipping an oversized protobuf field." I'm credited as a first-time contributor in that release - which, honestly, is the part that makes the late nights worth it.
The one-line summary you can take to a standup or a board meeting:
A single number, trusted without checking, can make even the most battle-tested software run forever. Validate your inputs - especially the boring ones like "how long is this?"
Thirteen bytes. One unchecked number. A tool behind billions of devices, spinning in place. Small bug, big reminder.
References
- Perfetto v58.2 release notes - fix listed under Trace Processor improvements ("a hang when skipping an oversized protobuf field")
- Perfetto is Google's open-source system-tracing suite and the tracing backbone of Android; web viewer at ui.perfetto.dev
- Fix contributed via the Perfetto GitHub project (PR #6968)
Comments
Post a Comment