[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-94578":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":9,"rankLanguage":9,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":18,"hasPages":18,"topics":20,"createdAt":9,"pushedAt":9,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":14,"starSnapshotCount":14,"syncStatus":24,"lastSyncTime":25,"discoverSource":26},94578,"schrodingers-toctou","xoreaxeaxeax\u002Fschrodingers-toctou","xoreaxeaxeax","The binary you run is not the program you wrote.",null,"C",107,4,1,0,3,42.4,"MIT License",false,"main",[],"2026-08-24 04:01:22","# Schrödinger's TOCTOU\n> *\"...the definition of 'sane compiler' grows ever looser.\"*\n\nThe binary you run is not the program you wrote. The compiler optimizer [rewrites\nyour source in ways you never see](#challenge) — and some of those changes can\nsilently and legally turn seemingly secure code into\n[vulnerable binaries](#a-buffer-overflow-from-thin-air). The same line can be\n[safe under one compiler and exploitable under another](alpha-lab\u002FREADME.md#same-source-different-outcome),\nwith nothing in the source to tell you which: a vulnerability held in\nsuperposition, collapsed only when you build. `Schrödinger's TOCTOU` explores\n**[compiler-invented loads](cat-states\u002FREADME.md)** and their widespread\nimplications for time-of-check to time-of-use (TOCTOU) vulnerabilities — found\nacross open-source\n[kernels](observer-effect\u002Faudits\u002Faudit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline),\n[hypervisors](observer-effect\u002Faudits\u002Faudit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact),\n[enclaves](observer-effect\u002Faudits\u002Faudit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural),\n[firmware](observer-effect\u002Faudits\u002Faudit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore),\nand [libraries](observer-effect\u002Faudits\u002Faudit-glibc-glibc-2.42.md#candidate-1--_dl_check_map_versions-verneed-version-index-write).\nEverywhere we look, seemingly secure code is left [exposed to the whims of the\ncompiler](observer-effect\u002FREPORT.md). But those are a sample, not a boundary; the\nsame bugs are very likely in [your code too](#open-the-box).\n\n## Challenge\n\n> *Start with something easy.*\n\nHow many times does this function load `*p`?\n\n```c\nunsigned int g(unsigned short *p)\n{\n  short t = *p;  \u002F* copy *p into a local for safekeeping *\u002F\n  return (unsigned short)t - t;\n}\n```\n\nHint: the answer is 1 — the source loads `*p` a single time into `t`.\n\nPaste it into [Compiler Explorer](https:\u002F\u002Fgodbolt.org\u002Fz\u002Fc5K9P4dPd) (`arm gcc 14.2.0`,\n`-O2`) and count the loads from `r0`, which holds `p`:\n\n```asm\ng:\n        ldrh    r2, [r0]     # load *p, once\n        ldrsh   r0, [r0]     # load *p, twice\n        subs    r0, r2, r0\n        bx      lr\n```\n\nOne load in the source, two in the binary. The second is an **invented load** —\na read the compiler manufactured that you never wrote. It is legal under the C\nabstract machine, which assumes memory cannot change between two reads. But when\nthat memory is attacker-writable, the assumption becomes an exploit: the\ninvented load can fall *after* a security check, silently reopening a\ntime-of-check to time-of-use (TOCTOU) window the programmer believed they had\nclosed. The value you validated and the value you use are no longer guaranteed\nto be the same — even though you never wrote code that re-read it.\n\n## A buffer overflow from thin air\n\nThe challenge proves the invented load exists; let's see how that turns into\nmemory corruption.\n\nIn a TOCTOU vulnerability, a program checks that a value is safe, then uses the\nvalue.  However, a window for exploitation exists if an attacker can *change*\nthe value in the sliver of time between those two reads – the harmless value\npasses the check while the dangerous one is the one that gets used:\n\n```c\nif (shared->len \u003C= 20)                       \u002F\u002F CHECK reads shared->len\n                                             \u002F\u002F ** attacker modifies shared->len **\n    memcpy(out, shared->data, shared->len);  \u002F\u002F USE reads it again: buffer overflow\n```\n\nThe textbook fix is to **snapshot first**: copy any data the attacker might\ntamper with into a local the attacker can't reach, and then trust nothing but\nthat local. Once `len` is in a local it is frozen — an attacker racing the\nshared memory can no longer touch it — so the check and the copy are guaranteed\nto see the same value. That is how the code in `receive` below fixes the TOCTOU:\nit snapshots the message, validates the snapshot, and publishes the validated\ncopy into `slot` for a consumer to forward:\n\n```c\n#include \u003Cstring.h>\n\nstruct message {\n    int  len;          \u002F* payload length *\u002F\n    char data[20];     \u002F* payload        *\u002F\n};\n\nstruct message slot;   \u002F* the most recently validated message *\u002F\nchar out[20];          \u002F* fixed 20-byte destination           *\u002F\n\nvoid receive(struct message *shared) {\n    struct message local = *shared;    \u002F* 1. snapshot untrusted input   *\u002F\n    if (local.len \u003C= 20)               \u002F* 2. validate the snapshot      *\u002F\n        slot = local;                  \u002F* 3. publish the validated copy *\u002F\n}\n\nvoid forward(void) {                   \u002F* the time of use, later        *\u002F\n    memcpy(out, slot.data, slot.len);  \u002F* slot.len was checked \u003C= 20 ... right? *\u002F\n}\n```\n\nBy the source, this is correct. `len` is read exactly once — into the snapshot —\nso the value that clears the `\u003C= 20` check is the value published into `slot`.\nThe TOCTOU window is closed and the code is safe.\n\nExcept it isn't. Under **x86-64 gcc** `-O2`, `receive` reads [it from the\n*original* shared memory twice](cat-states\u002FREADME.md#the-memcpy-cat-state): once\nas a scalar to gate the check, and again as part of [the bulk\ncopy](cat-states\u002Fbuffer_tail_bulk_overlap.c) that gets published into `slot`:\n\n```nasm\nreceive:\n        cmp     DWORD PTR [rdi], 20          ; READ #1: the CHECK reads shared->len directly\n        movdqu  xmm0, XMMWORD PTR [rdi]      ; READ #2: the bulk copy re-reads it (len is byte 0)\n        mov     rax, QWORD PTR [rdi+16]      ; (the bulk copy's tail: struct bytes 16-23)\n        jg      .L1                          ; len > 20? skip the publish\n        mov     QWORD PTR slot[rip+16], rax  ; (publish that tail)\n        movaps  XMMWORD PTR slot[rip], xmm0  ; and publish the TOCTOU-vulnerable snapshot\n.L1:\n        ret\nforward:\n        movsx   rdx, DWORD PTR slot[rip]     ; copy size = slot.len, the unchecked READ #2 value\n        mov     esi, OFFSET FLAT:slot+4      ; src = slot.data\n        mov     edi, OFFSET FLAT:out         ; dst = out[20]\n        jmp     memcpy                       ; copies slot.len bytes into out[20]\n```\n\nThe check runs on READ #1; the value that lands in `slot.len` is READ #2. An\nattacker who flips `len` between them passes a safe value to the `\u003C= 20` check\nwhile an oversized one is published into `slot` — and `forward` then copies that\nmany bytes into `out[20]`, the exact overflow the snapshot was meant to prevent,\nreintroduced by the optimizer.\n\nThis is turned into a complete proof-of-concept in\n[`poc\u002Fexample.c`](poc\u002Fexample.c), where the code uses the canonical\nTOCTOU-hardened approach: an untrusted `message` struct gets snapshotted into\n`local` so that it cannot be modified, the snapshot's `local.len` is validated\nagainst the buffer capacity, and only the validated copy is published into\n`slot`; a consumer later copies `slot.len` payload bytes into a fixed buffer.\nSimultaneously, an attacker races `shared->len`. An unexpected invented load\nfrom the compiler re-reads `shared->len` for the bulk publish, so `slot.len`\ncarries the attacker's oversized value even though the check passed —\nreintroducing the TOCTOU the programmer was trying to defend against, and\ncreating a seemingly impossible buffer overflow — from thin air.\n\n## Cause\n\n> *By the time C reaches machine code, it's been reshaped by frontend lowering,\n> IR optimizations, register allocation, and backend codegen — a deep, multi-stage\n> pipeline making decisions you can't see. There is no one stage to blame. The\n> invented load is an emergent property of the whole pipeline, not a bug in any\n> part of it.*\n\nAt this point: compilers *can* emit invented loads, and the very idiom meant to\nprevent the bug — snapshot, validate, use — is what reintroduces it. The next\nstep (to know whether we are actually vulnerable) is to characterize *when* it\nhappens. Turns out that's hard.\n\nIn [`cat-states\u002F`](cat-states\u002F), we search for the proofs-of-concept that show\nit is real — and that it is everywhere:\n\n| Mechanism | Toolchains | Targets |\n|---|---|---|\n| [**Rematerialization**](cat-states\u002FREADME.md#rematerialization-class-1) | GCC, Clang, ICX, ICC, MSVC | x86-64, i386, m68k, VAX, MSP430 |\n| [**Width-mismatch reload**](cat-states\u002FREADME.md#width-mismatch-reload-class-2) | GCC | ARM, MIPS, MIPS64, RV64, s390x |\n| [**Bulk-vs-scalar overlap**](cat-states\u002FREADME.md#bulk-vs-scalar-overlap-class-3) | GCC, Clang, ICX, MSVC | x86-64, ARM, AArch64, AVR, Xtensa, SPARC, PPC64, s390x, MIPS64, RV64, m68k, MSP430, VAX, HPPA |\n| [**Cross-class reload**](cat-states\u002FREADME.md#cross-class-reload-class-4) | GCC | x86-64, s390x |\n| [**CISC mem-op fold**](cat-states\u002FREADME.md#cisc-alu-mem-op-fold-class-7) | GCC, Clang | m68k, MSP430, s390x, VAX, 6502 |\n| [**Byte-order reload**](cat-states\u002FREADME.md#byte-order-divergent-reload-class-8) | GCC | s390x |\n\nEach PoC above pins down a single point where the load *can* appear;\n[`alpha-lab\u002F`](alpha-lab\u002F) charts the space around it to find where the edges\nfall — a three-stage pipeline driven from a single `.c` file.\nThe [matrix runner](alpha-lab\u002Fmatrix_runner.py) sweeps the compiler ×\narchitecture × flag matrix on [Compiler Explorer](https:\u002F\u002Fgodbolt.org);\nthe [load detector](alpha-lab\u002Fdetect.py) runs each resulting binary under\n[Unicorn](https:\u002F\u002Fwww.unicorn-engine.org\u002F) and catches any byte read twice; and\nthe [flag minimizer](alpha-lab\u002Fflag_search.py) delta-debugs each hit down to the\nminimal flag set that flips a secure build into a double-read TOCTOU.\n\n**The result**: no single compiler, flag, or pass is to blame — the double-read\nemerges from the complex interaction of many compiler layers, each making\nlocally valid decisions. The effect is non-linear: small changes in source,\nflags, or target can [cascade into different outcomes](observer-effect\u002Faudits\u002Faudit-imagemagick-7.1.2-25.md#candidate-1--readsunimage-sun_infolength-alloc-vs-copy). The only reliable way to\nknow whether a given line is vulnerable is to [**compile it and look.**](alpha-lab\u002FREADME.md#same-source-different-outcome)\n\n**The cat is alive — and it's not.** Until you build, a call site that\nsnapshots, validates, and uses a local copy is *neither* safe nor vulnerable —\nit is both, and the compiler, its version, the target, and the flags decide\nwhich. The build is the measurement, and it collapses the superposition one way\nor the other. This is a **Schrödinger TOCTOU**: a check on a value the\nprogrammer believed frozen, that the C standard quietly permits the compiler to\nre-read from attacker-controlled memory. The box stays closed until someone,\nsomewhere, picks a toolchain and opens it.\n\n## Effect\n\n> *The pattern appears nearly everywhere — woven into the most carefully\n> reviewed code in the world through simple idiomatic C.*\n\nThe problem is **virtually intractable**. The same snippet of code can be\nvulnerable or not vulnerable depending on the precise combination of compiler ×\nversion × architecture × flags — and there are more such combinations than there\nare atoms in the observable universe. Bounding it for even a single codebase is\na near-hopeless search; doing it across the ecosystem is far worse.\n\nEven deciding whether a *single* call site is safe resists inspection: a\npossible barrier like the kernel's `copy_from_user` only\n[forecloses the bug](observer-effect\u002FREADME.md#analysis)\nafter ~six layers of inlining, macros, and `CONFIG`\u002FCPU-feature forks bottom out\nin an opaque `asm` — and the *same* source line is no barrier at all in other\nconfigurations. Reading the call *site* tells us nothing.\n\nThe only path forward is automation. A heuristic-based analysis was run across\nprominent open-source targets — hypervisors, TEE\u002Fenclave runtimes, firmware,\nkernel subsystems, protocol libraries — and found **300+ Schrödinger TOCTOUs**\nacross **100+ security-critical projects**: sites where the C standard *permits*\nthe compiler to re-read attacker-writable memory between a check and its use.\nThe automated analysis identifies the trust boundaries, searches for the\nSchrödinger pattern, and assesses likelihood\u002Fimpact\u002Frisk.\n\nThe results show that seemingly innocuous compiler-invented loads easily cascade\ninto devastating consequences.\n\nThe compiler doesn't invent a *load* so much as the *capability* that load hands\nan attacker:\n\n---\n\n- **compiler-invented VM escape** — [QEMU](observer-effect\u002Faudits\u002Faudit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact), [Xen](observer-effect\u002Faudits\u002Faudit-xen-ptwalk-RELEASE-4.21.1.md#candidate-1--guest_walk_tables-pte-walk), [bhyve](observer-effect\u002Faudits\u002Faudit-bhyve-release-15.0.0.md#candidate-1--ahci-prdt-byte-count-write-path-oob-write), [KVM](observer-effect\u002Faudits\u002Faudit-linux-v7.0-kvm-host.md#candidate-1--svm-nested-vmcb12-save-area-cache-flagship), [ACRN](observer-effect\u002Faudits\u002Faudit-acrn-v3.3.md#candidate-1--nested-ept-shadow-walk)\n- **compiler-invented root** — [siw](observer-effect\u002Faudits\u002Faudit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline), [VMBus](observer-effect\u002Faudits\u002Faudit-linux-v7.0-hyperv-vmbus.md#candidate-2--msgtype-dispatch-index), [systemd](observer-effect\u002Faudits\u002Faudit-systemd-v260.md#candidate-1--sd_journal_enumerate_fields-sz-field-payload-size-alloc-vs-copy), [af-packet](observer-effect\u002Faudits\u002Faudit-linux-v7.0-af-packet.md#candidate-1--tp_len-tx-packet-length), [snd-pcm](observer-effect\u002Faudits\u002Faudit-linux-snd-pcm-v7.0.md#candidate-1--snd_pcm_indirect_playback_transfer-appl_ptr-snapshot-used-for-diff-and-stored-baseline), [seL4](observer-effect\u002Faudits\u002Faudit-sel4-15.0.0.md#candidate-1-flagship--untyped-retype-object-window)\n- **compiler-invented platform persistence** — [edk2](observer-effect\u002Faudits\u002Faudit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore), [coreboot](observer-effect\u002Faudits\u002Faudit-coreboot-26.03.md#candidate-1--smmstore_rawread_region-bufsize--com-buffer-mapping-overflow), [U-Boot](observer-effect\u002Faudits\u002Faudit-u-boot-v2026.04.md#candidate-1--virtqueue_get_buf-used-ring-id-primary), [OpenSBI](observer-effect\u002Faudits\u002Faudit-opensbi-v1.8.1.md#candidate-1--dbtr-update-trigger-index-primary)\n- **compiler-invented enclave breach** — [SGX](observer-effect\u002Faudits\u002Faudit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural), [Keystone](observer-effect\u002Faudits\u002Faudit-keystone-master-88c49ee.md#candidate-1--edge_call_get_ptr_from_offset--edge_call_ret_ptr-host-written-return-offsetsize), [OpenEnclave](observer-effect\u002Faudits\u002Faudit-openenclave-v0.19.15.md#candidate-1--sgx-ecall-context-ocall-buffer), [OP-TEE](observer-effect\u002Faudits\u002Faudit-optee-os-4.10.0.md#candidate-1--register_shm-raw-tmem-reads)\n\n---\n\nEach of these can be catastrophic on its own, but the breadth is what unsettles:\nthe same shape turns up everywhere the analysis looks, in code that shares\nnothing but the idiom:\n\n| Target | Site | Impact |\n|---|---|---|\n| **QEMU** | [`ahci_populate_sglist`](observer-effect\u002Faudits\u002Faudit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact) | guest AHCI PRDT length latched once → **OOB read \u002F attacker-directed host DMA** |\n| **Linux \u002F RDMA** | [`siw_rqe_get`](observer-effect\u002Faudits\u002Faudit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline) | software-RDMA `num_sge` reused → **kernel OOB write** |\n| **edk2 \u002F UEFI** | [`SmmLockBoxRestore`](observer-effect\u002Faudits\u002Faudit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore) | SMM buffer length reused → **OOB write into SMRAM** (ring -2) |\n| **TPM 2.0** | [`CryptParameterDecryption`](observer-effect\u002Faudits\u002Faudit-ms-tpm-20-ref-v1.83r1.md#candidate-1--cryptparameterdecryption-in-place-decrypt-length) | in-place decrypt length reused → **OOB write in the TPM root-of-trust** |\n| **seL4** | [`decodeUntypedInvocation`](observer-effect\u002Faudits\u002Faudit-sel4-15.0.0.md#candidate-1-flagship--untyped-retype-object-window) | retype object-window reused → **kernel compromise** |\n| **Xen** | [`guest_walk_tables`](observer-effect\u002Faudits\u002Faudit-xen-ptwalk-RELEASE-4.21.1.md#86-per-candidate-finding) | guest PTE reused on the walk → **privilege escalation** |\n| **SGX** | [edger8r ECALL bridge](observer-effect\u002Faudits\u002Faudit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural) | `[in]`\u002F`[in,out]` length reused for `malloc`\u002F`memcpy_s` → **enclave heap overflow** (every ECALL) |\n| **ARM TF-A** | [`spmc_ffa_fill_desc`](observer-effect\u002Faudits\u002Faudit-tf-a-v2.15.0.md#candidate-1--spmc-ffa_mem_sharelend-send-path-primary-could--yes) | FF-A descriptor field reused to size `memcpy` → **heap overflow in the EL3 secure monitor** |\n| **Linux \u002F Hyper-V** | [Hyper-V VMBus `__vmbus_on_msg_dpc`](observer-effect\u002Faudits\u002Faudit-linux-v7.0-hyperv-vmbus.md#candidate-2--msgtype-dispatch-index) | host `msgtype` reused to index handler table → **wild indirect call** in the guest kernel |\n| **U-Boot** | [`virtqueue_get_buf`](observer-effect\u002Faudits\u002Faudit-u-boot-v2026.04.md#candidate-1--virtqueue_get_buf-used-ring-id-primary) | virtio used-ring `id` reused as array index → **heap OOB read\u002Fwrite** in the bootloader |\n| **glibc** | [`_dl_check_map_versions`](observer-effect\u002Faudits\u002Faudit-glibc-glibc-2.42.md#candidate-1--_dl_check_map_versions-verneed-version-index-write) | dynamic-loader VERNEED version index reused as a write subscript → **OOB write in `ld.so`** when mapping a crafted shared library |\n| **systemd** | [`sd_journal_enumerate_fields`](observer-effect\u002Faudits\u002Faudit-systemd-v260.md#candidate-1--sd_journal_enumerate_fields-sz-field-payload-size-alloc-vs-copy) | journal field size reused across alloc\u002Fcopy → **heap OOB write** in `journalctl`\u002F`coredumpctl` (frequently root) |\n| **git** | [`read_table_of_contents`](observer-effect\u002Faudits\u002Faudit-git-v2.54.0.md#candidate-1--read_table_of_contents-chunk-offset-to-start-pointer) | object-store chunk offset reused as a chunk base\u002Fsize → **OOB read** parsing a crafted `.idx` \u002F multi-pack-index \u002F commit-graph (shared repo \u002F forge backend) |\n| **SQLite** | [`btreeComputeFreeSpace`](observer-effect\u002Faudits\u002Faudit-sqlite-version-3.53.2.md#candidate-1--btreecomputefreespace-freeblock-offset-pc-→-data-index) | B-tree freeblock offset reused as a page index → **OOB read of an `mmap`'d database page** |\n| **FreeType** | [`ft_var_readpackedpoints`](observer-effect\u002Faudits\u002Faudit-freetype-VER-2-14-3.md#candidate-1--ft_var_readpackedpoints-gvar-packed-point-count-n) | variable-font packed point-count reused → **heap OOB write** rendering a crafted font (ubiquitous: Android \u002F Chrome \u002F desktop) |\n| **libtiff** | [`NeXTDecode`](observer-effect\u002Faudits\u002Faudit-libtiff-v4.7.1.md#candidate-1--nextdecode-literalspan-off--n-controlled-oob-write) | NeXT-RLE span offset\u002Flength reused → **heap OOB write** decoding a crafted TIFF (default `mmap`'d read mode) |\n| **binutils \u002F ld** | [`sframe_decode`](observer-effect\u002Faudits\u002Faudit-binutils-binutils-2_46_1.md#candidate-1--sframe_decode-sfh_num_fdes-fde-table-alloc-vs-fill) | SFrame FDE count reused as alloc size **and** fill bound → **heap OOB write in the linker** on a crafted object |\n| **ClamAV** | [`autoit` EA05 `csize`](observer-effect\u002Faudits\u002Faudit-clamav-clamav-1.5.2.md#candidate-1--autoit-ea05-csize-alloc-vs-fill-heap-oob-write) | AutoIt `csize` reused as alloc size **and** copy length → **heap OOB write** in the scanner |\n| **YARA** | [`pe_parse_exports`](observer-effect\u002Faudits\u002Faudit-yara-v4.5.7.md#candidate-1--pe_parse_exports-number_of_exports-loop-bound) | PE export count reused as a loop bound → **OOB read** scanning a crafted sample |\n| **WAMR** | [`_vprintf_wa`](observer-effect\u002Faudits\u002Faudit-wamr-WAMR-2.4.4.md#candidate-1--_vprintf_wa-s-handler-s_offset-string-address-rematerialization) | guest `%s` offset re-read past the sandbox arena → **OOB read leaking host memory to the wasm guest** |\n| **ImageMagick** | [`ReadSUNImage`](observer-effect\u002Faudits\u002Faudit-imagemagick-7.1.2-25.md#candidate-1--readsunimage-sun_infolength-alloc-vs-copy) | SUN-raster length reused as alloc size **and** copy length → **heap OOB write → RCE** decoding a crafted image (LTO builds) |\n| **FreeBSD** | [`virtqueue_dequeue`](observer-effect\u002Faudits\u002Faudit-freebsd-drivers-release-15.0.0.md#candidate-1--virtqueue_dequeue-used-ring-desc_idx) | host-written virtio used-ring `id` reused as an unbounded array index → **descriptor double-free \u002F UAF** in the kernel |\n\nEverything is vulnerable. And everything is not. In every situation, the source\ndoes the right thing: snapshot the untrusted input, validate the copy, use the\ncopy. But in each, the C standard quietly permits the compiler to optionally\n*undo* that process, and create a TOCTOU out of thin air. Whether a\ngiven site is exploitable is *not a property of the source*: it is decided\nby the compiler, its version, the architecture, the flags, and it collapses one\nway only when you build. Until then each one is both — a vulnerability held in\nsuperposition, indistinguishable at the source level from code that is genuinely\nfine. Each is a Schrödinger TOCTOU — and the table above is what they look like\nat scale.\n\nThe unsettling part is not that these particular projects are flawed — it is\nthat the pattern turns up nearly everywhere the analysis looks, woven into [the\nmost carefully reviewed code in the world](observer-effect\u002Faudits\u002Faudit-sel4-15.0.0.md#82-executive-summary)\nthrough nothing more than idiomatic C. The 100+ repositories are a\n**sample, not the boundary**: the same latent bug almost certainly reaches your\nown codebase.\n\nThe full audit and impact analysis is in [observer-effect\u002F](observer-effect\u002F)\nand its [REPORT.md](observer-effect\u002FREPORT.md).\n\n## Solutions\n\n> *There are none.*\n\nBut here are some things we can try anyway.\n\nThe reflex solution is to try to pin the load — [`volatile`](observer-effect\u002FBARRIERS.md#i-1--volatile--read_once-access-site-latch), [`READ_ONCE`](observer-effect\u002FBARRIERS.md#i-1--volatile--read_once-access-site-latch), an\n[atomic](observer-effect\u002FBARRIERS.md#i-2--atomic--acquire-load), a [`\"memory\"`-clobber `barrier()`](observer-effect\u002FBARRIERS.md#i-3--memory-clobber-compiler-barrier). Those are spec-sound and survive\n`-O3`, LTO, and inlining; where a bare read is *[found](observer-effect\u002Faudits\u002Faudit-linux-v7.0-kvm-host-sev-snp.md#candidate-1--snp_begin_psc-idx_end-loop-bound)*, they are [the correct\npatch](observer-effect\u002FREADME.md#confirmed-in-the-wild). Unfortunately, they patch the wound, but not the cause:\n\n- **`volatile` launders away silently.** It [qualifies *the lvalue access*](cat-states\u002FREADME.md#the-volatile-cat-state), not\n  the object, the pointer, or the region. A `volatile T *p` read through a plain\n  lvalue [gives zero protection](cat-states\u002Fvolatile_lvalue_launder.c), and the\n  qualifier is dropped with **no diagnostic** when it [passes through `memcpy`'s\n  `const void *`](cat-states\u002Fvolatile_memcpy_overlap.c) — there is [no volatile-preserving `memcpy`](observer-effect\u002FBARRIERS.md#false-friends--look-like-barriers-but-are-not). The barrier you\n  wrote evaporates at the call you didn't.\n\n- **`READ_ONCE` doesn't scale.** \"Use `READ_ONCE`\" really means: annotate\n  *every* attacker-reachable access of [*every* field](observer-effect\u002Faudits\u002Faudit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline), forever, and [position a\n  fence between the read and *all* of its uses](observer-effect\u002FBARRIERS.md#i-3--memory-clobber-compiler-barrier). [Miss one](observer-effect\u002Faudits\u002Faudit-linux-v7.0-io_uring.md#candidate-1--nvme_uring_cmd_io-nsid) and the discipline is\n  void. It [cannot be enforced at scale](observer-effect\u002FBARRIERS.md#i-1--volatile--read_once-access-site-latch), and it regresses silently.\n\n- **A `barrier()`'s correctness lives frames from the source line.** Deciding\n  whether one `copy_from_user(&local, uptr, n)` even carries a `\"memory\"` clobber\n  means [tracing five inlined layers and an out-of-line call](observer-effect\u002FREADME.md#analysis) from generic C\n  into arch-specific asm, resolving a fistful of `CONFIG`\u002FCPU-feature\u002F`__builtin`\n  forks. And even once found, the clobber [names no read](observer-effect\u002FBARRIERS.md#i-3--memory-clobber-compiler-barrier): a step out of place it [pins\n  nothing](observer-effect\u002FBARRIERS.md#i-3--memory-clobber-compiler-barrier); a step the other way it [*forces* the very reload it should\n  stop](observer-effect\u002FBARRIERS.md#principles--shared-facts-the-cards-lean-on).\n\nBut more importantly: the source never asks for a reload to begin with. This is\nthe deeper issue. The programmer wrote `local.len` and *meant* `local.len`: one\nvalue, read once. If we say `x` we mean `x`, not \"`x`, but `y` if the compiler\nlikes that instead.\" The reload is invented beneath the abstract machine, so the\ncode that needs the annotation [looks identical to the code that\ndoesn't](alpha-lab\u002FREADME.md#same-source-different-outcome) — there is [no\nsignal at the site that a barrier is\nrequired](observer-effect\u002FBARRIERS.md#proposed-levers-do-not-exist-in-usable-form-today).\nYou cannot remember to guard a read you never wrote.\n\nThe full catalog of defenses — with their [strengths](observer-effect\u002Faudits\u002Faudit-linux-binder-v7.0.md#executive-summary) and [failures](observer-effect\u002Faudits\u002Faudit-libspdm-3.8.2.md#durability-assessment) — is in the\n[barriers report](observer-effect\u002FBARRIERS.md).\n\n## Open the box\n\n> *The TOCTOU-from-thin-air pattern is everywhere. Check if your code has it.*\n\nCheck your own code with\n[`observer-effect\u002FAUDIT-PROMPT.md`](observer-effect\u002FAUDIT-PROMPT.md), which will\nlook for the trust boundaries, search for the Schrödinger pattern, prune based on\nspec-compliant barriers, and assess likelihood\u002Fimpact\u002Frisk. Hand it to your\npreferred coding agent with your source in context and point it at a subsystem:\n\n```sh\ncd ~\u002Fyour-project          # the codebase you want audited\nclaude -p \"$(cat path\u002Fto\u002Fobserver-effect\u002FAUDIT-PROMPT.md)\nAudit drivers\u002Fnet\u002F for invented-load TOCTOUs.\"\n```\n\nIt depends on nothing else in this repo — copy the one file and go.\n\n## Future\n\n`Schrödinger's TOCTOU` dissects one specific instantiation of some random\noptimization allowed by the [500-page C-specification](https:\u002F\u002Fwww.open-std.org\u002Fjtc1\u002Fsc22\u002Fwg14\u002Fwww\u002Fdocs\u002Fn1256.pdf). But it's just scratching\nthe surface: there is *so much* ground left to explore. This repository will\ncontinue to poke, capture, and catalog the unexpected ways your favorite\ncompiler undercuts you — silently, legally, and at every optimization level.\n\n> *\"... if gcc did that, much of the kernel would go down in flames.\"*\n\n— Paul E. McKenney, LKML, 2009-04-16 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002F20090417050530.GB6885@linux.vnet.ibm.com\u002F)\n\n> *\"People love to talk about 'safe C', but compiler people have actively tried to make C\n> unsafer for decades. The C standards committee has been complicit.\"*\n\n— Linus Torvalds, 2025-02-21 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002FCAHk-=whZwXK9shqeV5fpRF9CRqApVy5wz6myNeAkyuFm-ERTpQ@mail.gmail.com\u002F)\n\n> *\"I would very much prefer a compiler switch that instructs the compiler to not do\n> bloody stupid things like this instead of marking every other load\u002Fstore in the kernel\n> with volatile.\"*\n\n— Peter Zijlstra, 2015-06-17 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002F20150617151109.GD19282@twins.programming.kicks-ass.net\u002F)\n\n> *\"The spec is just so much toilet paper. The ONLY thing that matters is what\n> real hardware does.\"*\n\n— Linus Torvalds, 2006-12-04 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002FPine.LNX.4.64.0611150739110.3349@woody.osdl.org\u002F)\n\n> \"By that argumentation we need to plaster half of the kernel with _ONCE() … Can we finally\n> put a foot down and tell compiler and standard committee people to stop this insanity?\n\n— Thomas Gleixner, 2019-08-16 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002Falpine.DEB.2.21.1908162245440.1923@nanos.tec.linutronix.de\u002F)\n\n> \"Compilers that 'optimize' things to touch fields that aren't touched by the source\n> code are simply inherently buggy shit. I'm not at all interested in catering to their\n> insanity... Claiming that they need to be marked volatile is a symptom of a\n> diseased compiler writer.\"\n\n— Linus Torvalds, 2014-12-04 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002FCA+55aFxDmaA2D37Zz1Zc7y6iD6gVs=wskP5wgJb37R98aN-qmg@mail.gmail.com\u002F)\n\n> *\"Insane? Probably so. But there are compiler guys who swear by it.\"*\n\n— Paul E. McKenney, LKML, 2008-02-04 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002F20080205051310.GC8457@linux.vnet.ibm.com\u002F)\n\n> *\"It's a good thing if they have _tested_ all the code-paths, but they've\n> invariably been tested with a compiler that doesn't go out of its way to try to\n> generate \"legal but idiotic\" code. So the testing won't generally find cases\n> where the compiler may have been _allowed_ to do something else. ... Compiler\n> people who don't realize this aren't compiler people. They're academics involved\n> with mental masturbation.\"*\n\n— Linus Torvalds, LKML, 2007-01-04 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002FPine.LNX.4.64.0701040937460.3661@woody.osdl.org\u002F)\n\n> *\"Of course, it is not the stupid compilers that worry me, but rather the smart ones...\"*\n\n— Paul E. McKenney, LKML, 2013-10-09 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002F20131009223652.GC5790@linux.vnet.ibm.com\u002F)\n\n> *\"... we've had compiler writers that say \"if you read the specs, that's ok\".  No,\n> it's not ok. Because reality trumps any weasel-spec-reading.\"*\n\n— Linus Torvalds, LKML, 2019-08-16 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002FCAHk-=wi_KeD1M-_-_SU_H92vJ-yNkDnAGhAS=RR1yNNGWKW+aA@mail.gmail.com\u002F)\n\n> *\"... the definition of 'sane compiler' grows ever looser.\"*\n\n— Paul E. McKenney, LKML, 2013-09-24 ·\n[lore](https:\u002F\u002Flore.kernel.org\u002Flkml\u002F1380073359-31959-3-git-send-email-paulmck@linux.vnet.ibm.com\u002F)\n\n## References\n\n* Whitepaper: (coming soon)\n* Slides: (coming soon)\n* Presentation: (coming soon)\n\n---\n\n## Author\n\n`Schrödinger's TOCTOU` is a research effort from Christopher Domas ([@xoreaxeaxeax](https:\u002F\u002Fx.com\u002Fxoreaxeaxeax\u002F))\n\n---\n\n![Experiment](data-sheets\u002Fexperiment.jpg)\n\n---\n","这是一个研究编译器优化引发TOCTOU（时序竞争）漏洞的安全分析项目。它揭示C语言编译器在优化过程中可能合法地插入未显式编写的内存读取（即‘编译器发明的加载’），导致原本看似安全的检查-使用逻辑被破坏，从而在运行时产生隐蔽的竞态漏洞。项目通过真实案例（Linux内核、QEMU、SGX、EDK2固件、glibc等）验证该问题的广泛存在，并提供可复现的汇编级证据与审计方法。适用于系统安全研究人员、底层软件开发者及编译器安全审计人员，在开发高保障系统、审查可信计算组件或进行漏洞挖掘时使用。",2,"2026-08-12 02:30:08","CREATED_QUERY"]