Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
schrodingers-toctou — Detecta carregamentos de memória inventados pelo compilador que transformam C seguro em vulnerabilidades TOCTOU. Inclui auditorias automatizadas de código-fonte, análise binária baseada em Unicorn e varreduras de compilador/arquitetura/flags em mais de 100 projetos. | Kitploit
Ferramentas/GitHubGitHub/xoreaxeaxeax/schrodingers-toctou
Análise Dinâmica (Sandboxing)Análise Estática de Código (SAST)Análise de VulnerabilidadesExploraçãoAnálise de BináriosAprendizado e Educação
GitHubxoreaxeaxeax/schrodingers-toctou

schrodingers-toctou

Detecta carregamentos de memória inventados pelo compilador que transformam C seguro em vulnerabilidades TOCTOU. Inclui auditorias automatizadas de código-fonte, análise binária baseada em Unicorn e varreduras de compilador/arquitetura/flags em mais de 100 projetos.

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
Ver Repositório
944há 15 diasAinda não revisado

Schrödinger's TOCTOU

"...a definição de 'compilador sensato' fica cada vez mais frouxa."

O binário que você executa não é o programa que você escreveu. O otimizador do compilador reescreve seu código-fonte de maneiras que você nunca vê — e algumas dessas mudanças podem silenciosa e legalmente transformar código aparentemente seguro em binários vulneráveis. A mesma linha pode ser segura com um compilador e explorável com outro, sem nada no código-fonte que lhe diga qual delas: uma vulnerabilidade mantida em superposição, colapsada apenas quando você compila. Schrödinger's TOCTOU explora loads inventados pelo compilador e suas amplas implicações para vulnerabilidades do tipo time-of-check to time-of-use (TOCTOU) — encontradas em código aberto: kernels, hipervisores, enclaves, firmware, e bibliotecas. Onde quer que olhemos, código aparentemente seguro fica exposto aos caprichos do compilador. Mas esses são uma amostra, não um limite; os mesmos bugs estão muito provavelmente no seu código também.

Desafio

"Comece com algo fácil."

Quantas vezes esta função carrega *p?```c unsigned int g(unsigned short *p) { short t = p; / copy *p into a local for safekeeping */ return (unsigned short)t - t; }

root@kitploit:~
Dica: a resposta é 1 — o código-fonte carrega `*p` uma única vez em `t`.

Cole-o no [Compiler Explorer](https://godbolt.org/z/c5K9P4dPd) (`arm gcc 14.2.0`,
`-O2`) e conte as cargas de `r0`, que contém `p`:```asm
g:
        ldrh    r2, [r0]     # load *p, once
        ldrsh   r0, [r0]     # load *p, twice
        subs    r0, r2, r0
        bx      lr

Uma carga no código-fonte, duas no binário. A segunda é uma carga inventada — uma leitura que o compilador fabricou e que você nunca escreveu. Ela é legal sob a máquina abstrata de C, que assume que a memória não pode mudar entre duas leituras. Mas quando essa memória é gravável por um atacante, a suposição se torna uma exploração: a carga inventada pode cair depois de uma verificação de segurança, reabrindo silenciosamente uma janela de tempo-de-verificação para tempo-de-uso (TOCTOU) que o programador acreditava ter fechado. O valor que você validou e o valor que você usa não são mais garantidamente os mesmos — mesmo que você nunca tenha escrito código que o lesse novamente.

Um estouro de buffer vindo do nada

O desafio prova que a carga inventada existe; vejamos como isso se transforma em corrupção de memória.

Em uma vulnerabilidade TOCTOU, um programa verifica que um valor é seguro e então usa o valor. No entanto, existe uma janela para exploração se um atacante puder alterar o valor no ínterim entre essas duas leituras – o valor inofensivo passa na verificação enquanto o perigoso é o que é usado:```c if (shared->len <= 20) // CHECK reads shared->len // ** attacker modifies shared->len ** memcpy(out, shared->data, shared->len); // USE reads it again: buffer overflow

root@kitploit:~
A correção clássica é **tirar um snapshot primeiro**: copie quaisquer dados que o atacante possa
adulterar para uma área local que o atacante não possa alcançar, e então confie apenas
nessa área local. Uma vez que `len` está em uma variável local, ele fica congelado — um atacante disputando a
memória compartilhada não pode mais tocá-lo — portanto, a verificação e a cópia têm garantia
de ver o mesmo valor. É assim que o código em `receive` abaixo corrige o TOCTOU:
ele tira um snapshot da mensagem, valida o snapshot e publica a cópia validada
em `slot` para um consumidor encaminhar:```c
#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? */
}

Pelo código-fonte, isso está correto. len é lido exatamente uma vez — para o snapshot — então o valor que satisfaz a verificação <= 20 é o valor publicado em slot. A janela TOCTOU está fechada e o código está seguro.

Exceto que não está. Sob x86-64 gcc -O2, receive o lê da memória compartilhada original duas vezes: uma como um escalar para controlar a verificação, e novamente como parte de a cópia em massa que é publicada em slot:```nasm 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]

root@kitploit:~
A verificação roda na LEITURA #1; o valor que chega a `slot.len` é a LEITURA #2. Um
atacante que altera `len` entre elas passa um valor seguro para a verificação `<= 20`
enquanto um valor superdimensionado é publicado em `slot` — e `forward` então copia
essa quantidade de bytes para `out[20]`, exatamente o estouro que o snapshot deveria evitar,
reintroduzido pelo otimizador.

Isto é transformado em uma prova de conceito completa em
[`poc/example.c`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/poc/example.c), onde o código usa a abordagem canônica
endurecida contra TOCTOU: uma struct `message` não confiável recebe um snapshot em
`local` para que não possa ser modificada, o `local.len` do snapshot é validado
contra a capacidade do buffer, e somente a cópia validada é publicada em
`slot`; um consumidor depois copia `slot.len` bytes de payload para um buffer fixo.
Simultaneamente, um atacante disputa (race) com `shared->len`. Uma carga inventada inesperada
do compilador relê `shared->len` para a publicação em massa, então `slot.len`
carrega o valor superdimensionado do atacante mesmo que a verificação tenha passado —
reintroduzindo o TOCTOU contra o qual o programador tentava se defender, e
criando um estouro de buffer aparentemente impossível — do nada.

## Cause

> *Quando o C chega ao código de máquina, ele já foi remodelado pelo lowering do frontend,
> otimizações de IR, alocação de registradores e codegen do backend — um pipeline profundo e de múltiplos estágios
> tomando decisões que você não pode ver. Não há um único estágio para culpar. A
> carga inventada é uma propriedade emergente de todo o pipeline, não um bug em qualquer
> parte dele.*

Neste ponto: compiladores *podem* emitir cargas inventadas, e exatamente o idioma que deveria
prevenir o bug — snapshot, validar, usar — é o que o reintroduz. O próximo
passo (para saber se estamos realmente vulneráveis) é caracterizar *quando* isso
acontece. Acontece que isso é difícil.

Em [`cat-states/`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/), procuramos pelas provas de conceito que mostram
que isso é real — e que está em toda parte:

| Mecanismo | Toolchains | Alvos |
|---|---|---|
| [**Rematerialization**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#rematerialization-class-1) | GCC, Clang, ICX, ICC, MSVC | x86-64, i386, m68k, VAX, MSP430 |
| [**Width-mismatch reload**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#width-mismatch-reload-class-2) | GCC | ARM, MIPS, MIPS64, RV64, s390x |
| [**Bulk-vs-scalar overlap**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.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 |
| [**Cross-class reload**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#cross-class-reload-class-4) | GCC | x86-64, s390x |
| [**CISC mem-op fold**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#cisc-alu-mem-op-fold-class-7) | GCC, Clang | m68k, MSP430, s390x, VAX, 6502 |
| [**Byte-order reload**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#byte-order-divergent-reload-class-8) | GCC | s390x |

Cada PoC acima fixa um único ponto onde a carga *pode* aparecer;
[`alpha-lab/`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/) mapeia o espaço ao redor para descobrir onde ficam as bordas
— um pipeline de três estágios dirigido a partir de um único arquivo `.c`.
O [executor de matriz](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/matrix_runner.py) varre a matriz compilador ×
arquitetura × flags no [Compiler Explorer](https://godbolt.org);
o [detector de cargas](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/detect.py) executa cada binário resultante sob
[Unicorn](https://www.unicorn-engine.org/) e captura qualquer byte lido duas vezes; e
o [minimizador de flags](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/flag_search.py) faz delta-debug de cada ocorrência até o
conjunto mínimo de flags que transforma uma build segura em um TOCTOU de leitura dupla.

**O resultado**: nenhum compilador, flag ou passe isolado é o culpado — a leitura dupla
emerge da interação complexa de muitas camadas do compilador, cada uma tomando
decisões localmente válidas. O efeito é não linear: pequenas mudanças no código-fonte,
flags ou alvo podem [cascatear em resultados diferentes](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-imagemagick-7.1.2-25.md#candidate-1--readsunimage-sun_infolength-alloc-vs-copy). A única maneira confiável de
saber se uma determinada linha é vulnerável é [**compilá-la e olhar.**](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/README.md#same-source-different-outcome)

**O gato está vivo — e não está.** Até você compilar, um ponto de chamada que
captura, valida e usa uma cópia local não é *nem* seguro *nem* vulnerável —
é ambos, e o compilador, sua versão, o alvo e as flags decidem
qual. A build é a medição, e ela colapsa a superposição para um lado
ou para o outro. Este é um **TOCTOU de Schrödinger**: uma verificação sobre um valor que o
programador acreditava congelado, mas que o padrão C permite silenciosamente ao compilador
reler da memória controlada pelo atacante. A caixa permanece fechada até que alguém,
em algum lugar, escolha uma cadeia de ferramentas e a abra.

## Effect

> *O padrão aparece em quase toda parte — tecido no código mais cuidadosamente
> revisado do mundo por meio de C idiomático simples.*

O problema é **virtualmente intratável**. O mesmo trecho de código pode ser
vulnerável ou não vulnerável dependendo da combinação precisa de compilador ×
versão × arquitetura × flags — e há mais dessas combinações do que
átomos no universo observável. Delimitar isso mesmo para um único código-fonte é
uma busca quase sem esperança; fazer isso em todo o ecossistema é muito pior.

Até decidir se um *único* ponto de chamada é seguro resiste à inspeção: uma
possível barreira como o `copy_from_user` do kernel só
[elimina o bug](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/README.md#analysis)
depois que ~seis camadas de inlining, macros e bifurcações de `CONFIG`/recursos de CPU terminam
em um `asm` opaco — e a *mesma* linha de código-fonte não é barreira alguma em outras
configurações. Ler o ponto de chamada não nos diz nada.

O único caminho a seguir é a automação. Uma análise baseada em heurísticas foi executada em
alvos proeminentes de código aberto — hipervisores, runtimes de TEE/enclave, firmware,
subsistemas de kernel, bibliotecas de protocolo — e encontrou **300+ TOCTOUs de Schrödinger**
em **100+ projetos críticos de segurança**: locais onde o padrão C *permite*
ao compilador reler memória gravável por atacante entre uma verificação e seu uso.
A análise automatizada identifica as fronteiras de confiança, procura o
padrão de Schrödinger e avalia probabilidade/impacto/risco.

Os resultados mostram que cargas inventadas pelo compilador aparentemente inofensivas facilmente cascateiam
em consequências devastadoras.

O compilador não inventa uma *carga* tanto quanto a *capacidade* que essa carga entrega
a um atacante:

---

- **escape de VM inventado pelo compilador** — [QEMU](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact), [Xen](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-xen-ptwalk-RELEASE-4.21.1.md#candidate-1--guest_walk_tables-pte-walk), [bhyve](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-bhyve-release-15.0.0.md#candidate-1--ahci-prdt-byte-count-write-path-oob-write), [KVM](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-kvm-host.md#candidate-1--svm-nested-vmcb12-save-area-cache-flagship), [ACRN](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-acrn-v3.3.md#candidate-1--nested-ept-shadow-walk)
- **root inventado pelo compilador** — [siw](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline), [VMBus](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-hyperv-vmbus.md#candidate-2--msgtype-dispatch-index), [systemd](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-systemd-v260.md#candidate-1--sd_journal_enumerate_fields-sz-field-payload-size-alloc-vs-copy), [af-packet](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-af-packet.md#candidate-1--tp_len-tx-packet-length), [snd-pcm](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-snd-pcm-v7.0.md#candidate-1--snd_pcm_indirect_playback_transfer-appl_ptr-snapshot-used-for-diff-and-stored-baseline), [seL4](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sel4-15.0.0.md#candidate-1-flagship--untyped-retype-object-window)
- **persistência de plataforma inventada pelo compilador** — [edk2](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore), [coreboot](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-coreboot-26.03.md#candidate-1--smmstore_rawread_region-bufsize--com-buffer-mapping-overflow), [U-Boot](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-u-boot-v2026.04.md#candidate-1--virtqueue_get_buf-used-ring-id-primary), [OpenSBI](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-opensbi-v1.8.1.md#candidate-1--dbtr-update-trigger-index-primary)
- **violação de enclave inventada pelo compilador** — [SGX](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural), [Keystone](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-keystone-master-88c49ee.md#candidate-1--edge_call_get_ptr_from_offset--edge_call_ret_ptr-host-written-return-offsetsize), [OpenEnclave](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-openenclave-v0.19.15.md#candidate-1--sgx-ecall-context-ocall-buffer), [OP-TEE](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-optee-os-4.10.0.md#candidate-1--register_shm-raw-tmem-reads)

---

Cada um desses pode ser catastrófico por si só, mas a amplitude é o que incomoda:
a mesma forma aparece em todos os lugares em que a análise procura, em código que não compartilha
nada além do idioma:

| Alvo | Local | Impacto |
|---|---|---|
| **QEMU** | [`ahci_populate_sglist`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-qemu-v11.0.1.md#candidate-1--ahci-prdtl-highest-impact) | comprimento do PRDT AHCI da guest capturado uma vez → **leitura OOB / DMA do host direcionado pelo atacante** |
| **Linux / RDMA** | [`siw_rqe_get`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline) | `num_sge` do software-RDMA reutilizado → **escrita OOB no kernel** |
| **edk2 / UEFI** | [`SmmLockBoxRestore`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-edk2-edk2-stable202605.md#candidate-1--smmlockboxrestore) | comprimento do buffer SMM reutilizado → **escrita OOB no SMRAM** (anel -2) |
| **TPM 2.0** | [`CryptParameterDecryption`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-ms-tpm-20-ref-v1.83r1.md#candidate-1--cryptparameterdecryption-in-place-decrypt-length) | comprimento da descriptografia in-place reutilizado → **escrita OOB na raiz de confiança do TPM** |
| **seL4** | [`decodeUntypedInvocation`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sel4-15.0.0.md#candidate-1-flagship--untyped-retype-object-window) | janela de objeto do retype reutilizada → **comprometimento do kernel** |
| **Xen** | [`guest_walk_tables`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-xen-ptwalk-RELEASE-4.21.1.md#86-per-candidate-finding) | PTE da guest reutilizada na caminhada → **escalada de privilégio** |
| **SGX** | [edger8r ECALL bridge](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-intel-sgx-sdk-sgx_2.29.md#candidate-1--generated-ecall-ininout-copy-in-headline-structural) | comprimento de `[in]`/`[in,out]` reutilizado para `malloc`/`memcpy_s` → **estouro de heap na enclave** (todo ECALL) |
| **ARM TF-A** | [`spmc_ffa_fill_desc`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-tf-a-v2.15.0.md#candidate-1--spmc-ffa_mem_sharelend-send-path-primary-could--yes) | campo do descritor FF-A reutilizado para dimensionar `memcpy` → **estouro de heap no monitor seguro EL3** |
| **Linux / Hyper-V** | [Hyper-V VMBus `__vmbus_on_msg_dpc`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-hyperv-vmbus.md#candidate-2--msgtype-dispatch-index) | `msgtype` do host reutilizado para indexar a tabela de handlers → **chamada indireta descontrolada** no kernel da guest |
| **U-Boot** | [`virtqueue_get_buf`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-u-boot-v2026.04.md#candidate-1--virtqueue_get_buf-used-ring-id-primary) | `id` do used-ring virtio reutilizado como índice de array → **leitura/escrita OOB de heap** no bootloader |
| **glibc** | [`_dl_check_map_versions`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-glibc-glibc-2.42.md#candidate-1--_dl_check_map_versions-verneed-version-index-write) | índice de versão VERNEED do carregador dinâmico reutilizado como subscrito de escrita → **escrita OOB em `ld.so`** ao mapear uma biblioteca compartilhada criada sob medida |
| **systemd** | [`sd_journal_enumerate_fields`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-systemd-v260.md#candidate-1--sd_journal_enumerate_fields-sz-field-payload-size-alloc-vs-copy) | tamanho do campo do journal reutilizado entre alloc/copy → **escrita OOB de heap** em `journalctl`/`coredumpctl` (frequentemente como root) |
| **git** | [`read_table_of_contents`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-git-v2.54.0.md#candidate-1--read_table_of_contents-chunk-offset-to-start-pointer) | deslocamento de chunk do object-store reutilizado como base/tamanho de chunk → **leitura OOB** ao analisar um `.idx` / multi-pack-index / commit-graph criado sob medida (repo compartilhado / backend de forge) |
| **SQLite** | [`btreeComputeFreeSpace`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sqlite-version-3.53.2.md#candidate-1--btreecomputefreespace-freeblock-offset-pc-→-data-index) | deslocamento do freeblock da árvore B reutilizado como índice de página → **leitura OOB de uma página de banco de dados mapeada por `mmap`** |
| **FreeType** | [`ft_var_readpackedpoints`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-freetype-VER-2-14-3.md#candidate-1--ft_var_readpackedpoints-gvar-packed-point-count-n) | contagem de pontos empacotados da fonte variável reutilizada → **escrita OOB de heap** ao renderizar uma fonte criada sob medida (onipresente: Android / Chrome / desktop) |
| **libtiff** | [`NeXTDecode`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-libtiff-v4.7.1.md#candidate-1--nextdecode-literalspan-off--n-controlled-oob-write) | deslocamento/comprimento de span NeXT-RLE reutilizado → **escrita OOB de heap** ao decodificar um TIFF criado sob medida (modo de leitura `mmap`'d padrão) |
| **binutils / ld** | [`sframe_decode`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-binutils-binutils-2_46_1.md#candidate-1--sframe_decode-sfh_num_fdes-fde-table-alloc-vs-fill) | contagem de FDE do SFrame reutilizada como tamanho de alocação **e** limite de preenchimento → **escrita OOB de heap no linker** em um objeto criado sob medida |
| **ClamAV** | [`autoit` EA05 `csize`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-clamav-clamav-1.5.2.md#candidate-1--autoit-ea05-csize-alloc-vs-fill-heap-oob-write) | `csize` do AutoIt reutilizado como tamanho de alocação **e** comprimento de cópia → **escrita OOB de heap** no scanner |
| **YARA** | [`pe_parse_exports`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-yara-v4.5.7.md#candidate-1--pe_parse_exports-number_of_exports-loop-bound) | contagem de exportações do PE reutilizada como limite de loop → **leitura OOB** ao escanear uma amostra criada sob medida |
| **WAMR** | [`_vprintf_wa`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-wamr-WAMR-2.4.4.md#candidate-1--_vprintf_wa-s-handler-s_offset-string-address-rematerialization) | deslocamento de `%s` da guest relido além da arena do sandbox → **leitura OOB vazando memória do host para a guest wasm** |
| **ImageMagick** | [`ReadSUNImage`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-imagemagick-7.1.2-25.md#candidate-1--readsunimage-sun_infolength-alloc-vs-copy) | comprimento do SUN-raster reutilizado como tamanho de alocação **e** comprimento de cópia → **escrita OOB de heap → RCE** ao decodificar uma imagem criada sob medida (builds LTO) |
| **FreeBSD** | [`virtqueue_dequeue`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-freebsd-drivers-release-15.0.0.md#candidate-1--virtqueue_dequeue-used-ring-desc_idx) | `id` do used-ring virtio gravado pelo host reutilizado como índice de array sem limite → **double-free de descritor / UAF** no kernel |

Tudo está vulnerável. E nada está. Em todas as situações, o código-fonte
faz a coisa certa: captura a entrada não confiável, valida a cópia, usa a
cópia. Mas em cada uma, o padrão C permite silenciosamente ao compilador
*desfazer* opcionalmente esse processo e criar um TOCTOU do nada. Se um
determinado local é explorável *não é uma propriedade do código-fonte*: é decidido
pelo compilador, sua versão, a arquitetura, as flags, e colapsa de uma
maneira apenas quando você compila. Até lá, cada um é ambos — uma vulnerabilidade mantida em
superposição, indistinguível no nível do código-fonte de um código que é genuinamente
bom. Cada um é um TOCTOU de Schrödinger — e a tabela acima é como eles se parecem
em escala.

O que incomoda não é que esses projetos específicos tenham falhas — é
que o padrão aparece em quase todos os lugares em que a análise procura, tecido no [código
mais cuidadosamente revisado do mundo](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-sel4-15.0.0.md#82-executive-summary)
apenas por meio de C idiomático. Os 100+ repositórios são uma
**amostra, não o limite**: o mesmo bug latente quase certamente alcança o seu
próprio código.

A auditoria completa e a análise de impacto estão em [observer-effect/](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/)
e no seu [REPORT.md](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/REPORT.md).

## Solutions

> *Não há nenhuma.*

Mas aqui estão algumas coisas que podemos tentar mesmo assim.

A solução reflexo é tentar fixar a carga — [`volatile`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-1--volatile--read_once-access-site-latch), [`READ_ONCE`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-1--volatile--read_once-access-site-latch), um
[atomic](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-2--atomic--acquire-load), um [`"memory"`-clobber `barrier()`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier). Essas são corretas segundo a especificação e sobrevivem
a `-O3`, LTO e inlining; onde uma leitura simples é *[encontrada](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-kvm-host-sev-snp.md#candidate-1--snp_begin_psc-idx_end-loop-bound)*, elas são [o patch
correto](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/README.md#confirmed-in-the-wild). Infelizmente, elas tratam o ferimento, mas não a causa:

- **`volatile` se dissipa silenciosamente.** Ele [qualifica *o acesso ao lvalue*](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/README.md#the-volatile-cat-state), não
  o objeto, o ponteiro ou a região. Uma leitura `volatile T *p` através de um
  lvalue simples [dá proteção zero](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/volatile_lvalue_launder.c), e o
  qualificador é descartado **sem nenhum diagnóstico** quando [passa por `memcpy`'s
  `const void *`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/cat-states/volatile_memcpy_overlap.c) — não existe [`memcpy` que preserve volatile](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#false-friends--look-like-barriers-but-are-not). A barreira que você
  escreveu evapora na chamada que você não fez.

- **`READ_ONCE` não escala.** "Use `READ_ONCE`" na verdade significa: anotar
  *todo* acesso alcançável por atacante de [*todo* campo](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-rdma-rxe-siw.md#candidate-1--siw-siw_rqe_get-num_sge-headline), para sempre, e [posicionar uma
  barreira entre a leitura e *todos* os seus usos](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier). [Perca um](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-v7.0-io_uring.md#candidate-1--nvme_uring_cmd_io-nsid) e a disciplina é
  anulada. Ela [não pode ser imposta em escala](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-1--volatile--read_once-access-site-latch), e regride silenciosamente.

- **A corretude de uma `barrier()` vive a frames de distância da linha de código-fonte.** Decidir
  se um `copy_from_user(&local, uptr, n)` sequer carrega um clobber `"memory"`
  significa [rastrear cinco camadas inline e uma chamada out-of-line](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/README.md#analysis) de C genérico
  para asm específico da arquitetura, resolvendo um punhado de bifurcações de `CONFIG`/recursos de CPU/`__builtin`.
  E mesmo depois de encontrado, o clobber [não nomeia nenhuma leitura](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier): um passo fora do lugar ele [fixa
  nada](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#i-3--memory-clobber-compiler-barrier); um passo na direção contrária ele [*força* exatamente a recarga que deveria
  impedir](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#principles--shared-facts-the-cards-lean-on).

Mas mais importante: o código-fonte nunca pede uma recarga para começar. Esta é
a questão mais profunda. O programador escreveu `local.len` e *quis dizer* `local.len`: um
valor, lido uma vez. Se dizemos `x`, queremos dizer `x`, não "`x`, mas `y` se o compilador
preferir isso em vez disso." A recarga é inventada abaixo da máquina abstrata, então o
código que precisa da anotação [parece idêntico ao código que
não precisa](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/alpha-lab/README.md#same-source-different-outcome) — não há [nenhum
sinal no local de que uma barreira é
necessária](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md#proposed-levers-do-not-exist-in-usable-form-today).
Você não pode lembrar de proteger uma leitura que nunca escreveu.

O catálogo completo de defesas — com seus [pontos fortes](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-linux-binder-v7.0.md#executive-summary) e [falhas](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/audits/audit-libspdm-3.8.2.md#durability-assessment) — está no
[relatório de barreiras](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/BARRIERS.md).

## Open the box

> *O padrão de TOCTOU do nada está em toda parte. Verifique se o seu código tem isso.*

Verifique seu próprio código com
[`observer-effect/AUDIT-PROMPT.md`](https://github.com/xoreaxeaxeax/schrodingers-toctou/blob/HEAD/observer-effect/AUDIT-PROMPT.md), que irá
procurar as fronteiras de confiança, buscar o padrão de Schrödinger, podar com base em
barreiras compatíveis com a especificação e avaliar probabilidade/impacto/risco. Entregue-o ao seu
agente de codificação preferido com o código-fonte no contexto e aponte-o para um subsistema:```sh
cd ~/your-project          # the codebase you want audited
claude -p "$(cat path/to/observer-effect/AUDIT-PROMPT.md)
Audit drivers/net/ for invented-load TOCTOUs."

Não depende de mais nada neste repositório — copie o único arquivo e pronto.

Futuro

O Schrödinger's TOCTOU disseca uma instância específica de alguma otimização aleatória permitida pela especificação C de 500 páginas. Mas isso é apenas arranhar a superfície: há muito terreno a explorar. Este repositório continuará a investigar, capturar e catalogar as maneiras inesperadas pelas quais seu compilador favorito te prejudica — silenciosamente, legalmente e em todos os níveis de otimização.

"... se o gcc fizesse isso, grande parte do kernel iria pelos ares."

— Paul E. McKenney, LKML, 2009-04-16 · lore

"As pessoas adoram falar sobre 'C seguro', mas o pessoal de compiladores tem tentado ativamente tornar o C mais inseguro há décadas. O comitê de padrões C tem sido cúmplice."

— Linus Torvalds, 2025-02-21 · lore

"Eu preferiria muito uma opção de compilador que instrua o compilador a não fazer coisas extremamente estúpidas como esta, em vez de marcar cada load/store do kernel com volatile."

— Peter Zijlstra, 2015-06-17 · lore

"A especificação não passa de papel higiênico. A ÚNICA coisa que importa é o que o hardware real faz."

— Linus Torvalds, 2006-12-04 · lore

"Por essa argumentação, precisamos encher metade do kernel com _ONCE() … Podemos finalmente botar o pé no chão e dizer ao pessoal de compiladores e do comitê de padrões para parar com essa insanidade?

— Thomas Gleixner, 2019-08-16 · lore

"Compiladores que 'otimizam' coisas para tocar em campos que não são tocados pelo código-fonte são simplesmente merda intrinsecamente bugada. Não tenho o menor interesse em atender à insanidade deles... Afirmar que eles precisam ser marcados como volatile é um sintoma de um desenvolvedor de compilador doente."

— Linus Torvalds, 2014-12-04 · lore

"Insano? Provavelmente. Mas há caras de compilador que juram por isso."

— Paul E. McKenney, LKML, 2008-02-04 · lore

"É bom que eles tenham testado todos os caminhos de código, mas eles foram invariavelmente testados com um compilador que não faz questão de tentar gerar código "permitido, porém idiota". Portanto, os testes geralmente não encontrarão casos em que o compilador pode ter sido autorizado a fazer outra coisa. ... Pessoas de compilador que não percebem isso não são pessoas de compilador. São acadêmicos envolvidos com masturbação mental."

— Linus Torvalds, LKML, 2007-01-04 · lore

"Naturalmente, não são os compiladores estúpidos que me preocupam, mas sim os inteligentes..."

— Paul E. McKenney, LKML, 2013-10-09 · lore

"... já tivemos desenvolvedores de compiladores que dizem "se você ler as especificações, tudo bem". Não, não está tudo bem. Porque a realidade supera qualquer leitura capciosa de especificações."

— Linus Torvalds, LKML, 2019-08-16 · lore

"... a definição de 'compilador sensato' fica cada vez mais frouxa."

— Paul E. McKenney, LKML, 2013-09-24 · lore

Referências

  • Whitepaper: (em breve)
  • Slides: (em breve)
  • Apresentação: (em breve)

Autor

Schrödinger's TOCTOU é um esforço de pesquisa de Christopher Domas (@xoreaxeaxeax)


Experimento


Baixar ferramenta