Build1 distinct publisher3 min readUpdated
A dev.to piece maps validate-then-commit payment flows onto a race condition class catalogued in 2006. The useful part is not the taxonomy but where the check has to live.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
A dev.to post by Oludele Oluwapelumi files validate-then-commit payment flows under the oldest entry in the race-condition catalogue: CWE-367, time-of-check to time-of-use, first documented in 2006 and, in the author's phrasing, still actively relevant two decades later [1]. If the mapping holds, the remediation is not a stricter validator but a different transaction boundary, and most teams running a "we validate first" architecture have never measured the gap between the two.
The pattern is defined narrowly: a program evaluates a precondition, then acts on a resource whose state it assumes is unchanged, with no atomicity guarantee between the two operations [2]. In the canonical C version, access() and fopen() are independent system calls, and nothing forces the kernel to guarantee that the file named at check time is the file opened at use time [3]. An attacker with write access to the containing directory swaps the target for a symlink to /etc/shadow inside the window, and the check will have been technically correct at the moment it ran [4]. Python reproduces the shape with os.path.exists() followed by open(), commonly enough that Semgrep ships a default rule for it [5].
The failure mode is worth stating precisely, because it is the part that transfers. Both the check and the use are individually correct; what is wrong is the system's belief about what the resource refers to, and it is wrong specifically because time passed and something else held write access during that interval [8]. Timing, not logic, is the vulnerability [8]. A balance read is a check. A debit is a use. Anything that can write the same row between them is the attacker in the diagram, including your own retry logic.
The prescribed fixes are all structural, not defensive: collapse check and use into one atomic operation, use O_CREAT with O_EXCL, call fstat() on an already-open descriptor instead of stat() on a path, or hold a lock such as flock() or fcntl(F_SETLK) across both steps [6]. The stated principle is never to trust a decision made against a resource you no longer have exclusive reference to [7]. Read across to a ledger, that is a conditional update, a row lock held from read to write, or serializable isolation, which is the same thing as saying the check belongs inside the transaction that mutates the balance.
Nobody should treat this as solved by seniority. CVE-2019-5736 was a TOCTOU race in runc that let a malicious container overwrite the host runc binary and escape [9], and CVE-2016-9806 was a race in Linux kernel netlink handling that enabled privilege escalation [10]; both landed a decade after CWE-367 was defined, in code reviewed far harder than most application code ever is [11].
The second lens is older than the CWE. Commitment ordering has been formalized in distributed database theory since 1990, with Yoav Raz's VLDB 1992 paper [12], which requires that the chronological order of commit events be compatible with the precedence order of conflicting operations, and holds that per-transaction correctness in isolation is not sufficient [13]. It was built for global serializability across autonomous resource managers with no shared clock, no shared concurrency control and no assumed coordination [14], which is a fair description of a payment stack assembled from a wallet service, a processor and a ledger. To the author's credit, the piece declines to assert that the specific failure it discusses meets the formal conditions of a commitment ordering violation, saying that would need proof it does not attempt [15].
What to watch is your own instrumentation. The material offers no payment-sector CVE and no measured window; its exploited examples are a container runtime and a kernel subsystem [16]. So the number to produce is local: elapsed time at p99 between the read that authorizes and the write that commits, and whether any lock or predicate spans them. If nothing does, the window exists whether or not anyone has found it.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
Time-of-check to time-of-use (TOCTOU) is a formally catalogued class of race condition, CWE-367, first documented in 2006 and described as still actively relevant two decades later.
The CWE-367 pattern: a program evaluates a precondition, then acts on a resource whose state it assumes is unchanged, without any atomicity guarantee between the two operations.
access() and fopen() are two independent system calls; nothing forces the kernel to guarantee that the file referenced at access() time is the same file referenced at fopen() time.
An attacker with write access to the containing directory can swap the target for a symlink to /etc/shadow inside the race window, and the check will have been technically correct at the moment it ran.
Python has the identical pattern (os.path.exists(path) followed by open(path)), and it is common enough that Semgrep ships a default rule for it.
The prescribed fix is to collapse check and use into a single atomic operation: O_CREAT | O_EXCL for atomic file creation, fstat() on an already-open file descriptor instead of stat() on a path, or a held lock (flock(), fcntl(F_SETLK)) spanning both operations.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Strong on the vulnerability class, thin on the payment mapping
The generic half is well grounded and checkable: a catalogued weakness class with a date, annotated non-atomic syscall sequences, a named static-analysis rule, two named exploited CVEs with stated impact, and a precise VLDB 1992 citation for commitment ordering. The story's actual assertion - that a validate-then-debit payment flow is an instance of this class - rests on one self-published post, three unnamed anecdotal conversations, and code the author reconstructed himself, with no payment incident, no measured window and an explicit refusal to attempt the formal proof.
Tooling and exploit history exist; payment-domain uptake unobserved
There are real adoption signals for the underlying class: a default Semgrep rule means detection is shipped in commodity tooling, and two CVEs show exploitation in a container runtime and the Linux kernel. There is no observation of the payment-specific remediation being deployed anywhere - no named operator, no rollout, no usage disclosure - so adoption of the story's actual subject is unmeasured beyond the generic pattern.
Headline mapping runs ahead of the payment evidence
The framing asserts that a balance check preceding a debit is CWE-367 rather than a guardrail, and imports a 1990s serializability result as a lens, while the supporting payment material is three anecdotal conversations and author-written illustrative code. The overstatement is bounded rather than severe because the author inserts an explicit precision paragraph disclaiming the formal commitment-ordering mapping, and because the syscall-level claims and CVEs are independently verifiable.
Self-published practitioner post; visibility incentive, no vendor stake visible
The material is a single-author post on a developer publishing platform. No product, vendor, pricing or sponsorship appears in the supplied text, so there is no commercial pull toward a particular conclusion; the operative incentive is authorship visibility, which favours a strong headline framing and the borrowing of formal authority from a canonical CWE and a classic VLDB paper. Mitigating this, the author credits named and unnamed engineers to the concept rather than claiming their code, and volunteers the limits of his own analogy.
One publisher, one author, no corroboration
Every claim in the cluster traces to a single source item from a single publisher. The catalogued and CVE-level facts would be easy to corroborate elsewhere but no second source is supplied here, and the payment-flow thesis - the part the story is actually about - has no independent confirmation at all. Confidence in the generic technical content is high; confidence in the cluster as a whole is limited by its single-source structure.
build
Force the tool call, then hand Lightsail a long-lived key1 distinct publisher
build
Stop timing your GraphQL tests and start counting loader calls1 distinct publisher
build
Your 90% Cache Hit Ratio Is a Lagging Indicator. Alert on Cold Misses Per Key1 distinct publisher
build
Before you spend quota on an agent skill, make it pass an eval harness1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 15, 2026