Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
schrodingers-toctou — Detect compiler-invented memory loads that turn secure C into TOCTOU vulnerabilities. Includes automated source audits, Unicorn-based binary analysis, and compiler/arch/flag sweeps across 100+ projects. | Kitploit
Tools/GitHubGitHub/xoreaxeaxeax/schrodingers-toctou
Dynamic Analysis (Sandboxing)Static Code Analysis (SAST)Vulnerability AnalysisExploitationBinary AnalysisLearning & Education
GitHubxoreaxeaxeax/schrodingers-toctou

schrodingers-toctou

Detect compiler-invented memory loads that turn secure C into TOCTOU vulnerabilities. Includes automated source audits, Unicorn-based binary analysis, and compiler/arch/flag sweeps across 100+ projects.

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
View Repository
944281 month agoReviewed by Kitploit
Share

Schrödinger's TOCTOU

"...the definition of 'sane compiler' grows ever looser."

The binary you run is not the program you wrote. The compiler optimizer rewrites your source in ways you never see — and some of those changes can silently and legally turn seemingly secure code into vulnerable binaries. The same line can be safe under one compiler and exploitable under another, with nothing in the source to tell you which: a vulnerability held in superposition, collapsed only when you build. Schrödinger's TOCTOU explores compiler-invented loads and their widespread implications for time-of-check to time-of-use (TOCTOU) vulnerabilities — found across open-source kernels, hypervisors, enclaves, firmware, and libraries. Everywhere we look, seemingly secure code is left exposed to the whims of the compiler. But those are a sample, not a boundary; the same bugs are very likely in your code too.

Challenge

Start with something easy.

How many times does this function load *p?

unsigned int g(unsigned short *p)
{
  short t = *p;  /* copy *p into a local for safekeeping */
  return (unsigned short)t - t;
}

Hint: the answer is 1 — the source loads *p a single time into t.

Paste it into Compiler Explorer (arm gcc 14.2.0, -O2) and count the loads from r0, which holds p:

g:
        ldrh    r2, [r0]     # load *p, once
        ldrsh   r0, [r0]     # load *p, twice
        subs    r0, r2, r0
        bx      lr

One load in the source, two in the binary. The second is an invented load — a read the compiler manufactured that you never wrote. It is legal under the C abstract machine, which assumes memory cannot change between two reads. But when that memory is attacker-writable, the assumption becomes an exploit: the invented load can fall after a security check, silently reopening a time-of-check to time-of-use (TOCTOU) window the programmer believed they had closed. The value you validated and the value you use are no longer guaranteed to be the same — even though you never wrote code that re-read it.

A buffer overflow from thin air

The challenge proves the invented load exists; let's see how that turns into memory corruption.

In a TOCTOU vulnerability, a program checks that a value is safe, then uses the value. However, a window for exploitation exists if an attacker can change the value in the sliver of time between those two reads – the harmless value passes the check while the dangerous one is the one that gets used:

if (shared->len <= 20)                       // CHECK reads shared->len
                                             // ** attacker modifies shared->len **
    memcpy(out, shared->data, shared->len);  // USE reads it again: buffer overflow

The textbook fix is to snapshot first: copy any data the attacker might tamper with into a local the attacker can't reach, and then trust nothing but that local. Once len is in a local it is frozen — an attacker racing the shared memory can no longer touch it — so the check and the copy are guaranteed to see the same value. That is how the code in receive below fixes the TOCTOU: it snapshots the message, validates the snapshot, and publishes the validated copy into slot for a consumer to forward:

#include <string.h>

struct message {
    int  len;          /* payload length */
    char data[20];     /* payload        */
};

struct message slot;   /* the most recently validated message */
char out[20];          /* fixed 20-byte destination           */

void receive(struct message *shared) {
    struct message local = *shared;    /* 1. snapshot untrusted input   */
    if (local.len <= 20)               /* 2. validate the snapshot      */
        slot = local;                  /* 3. publish the validated copy */
}

void forward(void) {                   /* the time of use, later        */
    memcpy(out, slot.data, slot.len);  /* slot.len was checked <= 20 ... right? */
}

By the source, this is correct. len is read exactly once — into the snapshot — so the value that clears the <= 20 check is the value published into slot. The TOCTOU window is closed and the code is safe.

Except it isn't. Under x86-64 gcc -O2, receive reads it from the original shared memory twice: once as a scalar to gate the check, and again as part of the bulk copy that gets published into slot:

receive:
        cmp     DWORD PTR [rdi], 20          ; READ #1: the CHECK reads shared->len directly
        movdqu  xmm0, XMMWORD PTR [rdi]      ; READ #2: the bulk copy re-reads it (len is byte 0)
        mov     rax, QWORD PTR [rdi+16]      ; (the bulk copy's tail: struct bytes 16-23)
        jg      .L1                          ; len > 20? skip the publish
        mov     QWORD PTR slot[rip+16], rax  ; (publish that tail)
        movaps  XMMWORD PTR slot[rip], xmm0  ; and publish the TOCTOU-vulnerable snapshot
.L1:
        ret
forward:
        movsx   rdx, DWORD PTR slot[rip]     ; copy size = slot.len, the unchecked READ #2 value
        mov     esi, OFFSET FLAT:slot+4      ; src = slot.data
        mov     edi, OFFSET FLAT:out         ; dst = out[20]
        jmp     memcpy                       ; copies slot.len bytes into out[20]

The check runs on READ #1; the value that lands in slot.len is READ #2. An attacker who flips len between them passes a safe value to the <= 20 check while an oversized one is published into slot — and forward then copies that many bytes into out[20], the exact overflow the snapshot was meant to prevent, reintroduced by the optimizer.

This is turned into a complete proof-of-concept in poc/example.c, where the code uses the canonical TOCTOU-hardened approach: an untrusted message struct gets snapshotted into local so that it cannot be modified, the snapshot's local.len is validated against the buffer capacity, and only the validated copy is published into slot; a consumer later copies slot.len payload bytes into a fixed buffer. Simultaneously, an attacker races shared->len. An unexpected invented load from the compiler re-reads shared->len for the bulk publish, so slot.len carries the attacker's oversized value even though the check passed — reintroducing the TOCTOU the programmer was trying to defend against, and creating a seemingly impossible buffer overflow — from thin air.

Cause

Download Tool