Skip to content

Research notes / Data integrity

Protection information: when success skips the checksum

/ Source audit and controlled code experiment

A successful integrity-verification return can mean a check was skipped. In the pinned Linux function examined here, escape tags bypass a block's guard check. Recording only success loses that distinction.

Start with the format and verification point

The eight-byte tuple examined here contains a 16-bit guard, a 16-bit application tag and a 32-bit reference tag. This is a specific format, not a universal description of all protection information. [1] NVMe 2.0 introduced additional 16-byte formats with wider guards and configurable tag fields; the 32-bit examples below must not be transferred to those formats. [2]

Our existing checksum, identity and freshness analysis distinguishes correct bytes from the right location and the latest version. This experiment adds a prior question: did the selected check actually run?

Two escape conditions, not one

In Linux v6.12's t10_pi_verify(), with reference checking enabled, application tag 0xffff skips both reference and guard comparisons for that interval. Without reference checking, skipping requires both application tag 0xffff and reference tag 0xffffffff. The loop then advances to the next interval. [3]

The historical NVMe 1.4c specification separately describes this distinction: Type 1/2 use the application-tag escape; Type 3 uses the pair. These conditions override the command's PRCHK selection. This is a documented protocol behavior, not evidence of a new vulnerability. The Linux helper is not a complete emulator of those NVMe commands. [4, section 8.3, p. 319]

Do not confuse the escape value with an application-tag comparison mask, or infer the active protection type from a device's general capability label. For a real command, retain its format, protection type, requested checks and returned tags.

Execute the branch, count the checks

We extracted the unchanged verifier and its iterator and tuple structures from Linux commit adc218676eef25575469234709c2d87185ca223a. Minimal user-space adapters replace kernel types and logging. The checksum function is deliberately a stub returning 0x1234; every call increments a counter. A stored guard of zero is therefore wrong relative to this declared oracle.

All 16 selected fixtures matched their expected return and call count. They use 512-byte data intervals and eight-byte PI fields. Twelve fixtures place PI at offset zero; four add an eight-byte metadata prefix, giving a 16-byte metadata stride. These are deterministic control-flow observations, not measured corruption rates. No storage device was accessed.

Extracted Linux verifier with checksum stub
FixtureReturnChecksum calls
Matching guard and referenceAccepted1
Wrong guard, matching referenceProtection error1
Matching guard, wrong referenceProtection error0
Escape with wrong guard and referenceAccepted0
0xfffe is not an escapeProtection error1
Reference comparison disabledAccepted1
No reference check; only app escapeProtection error1
No reference check; both escapesAccepted0
Seed 7 + 2^32, reference 7Accepted1
Escaped block, then wrong guardProtection error1
Escaped block, then valid blockAccepted1
Wrong guard, then escaped blockProtection error1
8-byte prefix; matching guard and referenceAccepted2
8-byte prefix; wrong guardProtection error2
8-byte prefix; escaped intervalAccepted0
8-byte prefixes; two valid intervalsAccepted4

The escaped wrong-guard case is accepted with zero checksum calls. By contrast, the two-block escaped-then-wrong-guard case is rejected after one call. Escape applies to an interval; it does not make the rest of a request unchecked. A wrong reference can also return an error before a checksum call, so a zero call count alone does not mean acceptance.

Checksum calls are not checked blocks

The added prefix fixtures expose another counting boundary. One accepted interval produces two checksum calls: one for its data and one for the metadata before PI. Two accepted intervals produce four calls; an escaped interval produces none. The guard is compared once per non-escaped interval that reaches that point. These fixture outcomes measure control flow, not actual checksum coverage correctness.

For telemetry, retain the interval count and metadata layout alongside call counts. Our fixed-value stub cannot validate the accumulated CRC or detect changed prefix bytes. A nonzero count is evidence that the stub ran, not proof that an actual CRC is correct.

An escape need not indicate corruption

NVMe 1.4c also specifies escape-valued application and reference tags for reads of deallocated or unwritten logical blocks. The guard may be all ones or a computed CRC. An enabled, supported deallocated-block error can instead abort the request. This is historical command-set behavior, not a promise that every current format returns the same bytes. [4, section 6.7.1, p. 260]

An escape therefore does not, by itself, diagnose corrupted media or an attack. Preserve allocation state and error-recovery settings as well as tags. See why discard is not zeroing.

Reference equality is not full identity

The tested helper compares the reference with the seed's low 32 bits. Consequently, seeds 7 and 4,294,967,303 share reference value 7. The alias fixture is accepted without a checksum collision: the guard already matches the stub. This demonstrates the comparison's finite width, not a device misdirection exploit. [3]

It does not establish that blocks on a deployed stack are interchangeable. Address remapping, namespaces and other validation can add context. Nor does a matching location establish freshness. Our inference is narrower: a verifier with no independently expected version cannot prove that a valid pair is the latest committed pair. See the existing old-record example.

Keep detection populations separate

Bairavasundaram and colleagues' FAST 2008 field study separates checksum mismatches, block-identity discrepancies and parity inconsistencies. In the studied systems, checks occurred on different operations: disk reads, filesystem reads and scrubs. Those are different observation opportunities, not interchangeable error counters.[6, section 2 and Table 1]

The paper does not measure the frequency of PI escapes. We do not transfer its historical fleet rates to this helper or to current SSDs. The connection is methodological: name the check, its trigger and the population actually examined before interpreting a success or failure count.

Reproduce and inspect the evidence

The machine-readable result records source hashes, compiler, execution environment and fixture outcomes. Place the Python runner, adapters and C fixtures in one directory. The runner downloads only two pinned upstream files when the cache is empty and rejects a source-hash mismatch.

python3 runner.py --source-dir ./linux-source-cache --output ./result.json

Python 3, a C compiler and POSIX networking headers are required. Add --sanitize for address and undefined-behavior checks on a supporting compiler. The harness uses GPL-2.0-only; upstream files retain their own SPDX notices. No third-party specification is redistributed.

This does not test actual CRC calculations, application-tag masks, driver selection, reference remapping, extended PI, DMA or controller behavior. It does not prove that the running host kernel took this path. The host used for compilation ran a different kernel; the tested function comes from the pinned v6.12 source.

What to retain in an integrity incident

Linux's integrity framework can generate metadata below the application and can toggle generation and verification. Its documentation contains historical design context, not a current driver-support matrix. Identify the actual generation and checking points before calling the path end-to-end. [5]

  1. Record the format, interval size, device and software versions, and enabled checks.
  2. Preserve guard, application and reference tags alongside the relevant data and request context.
  3. Separate checks performed, checks skipped and failures; do not collapse them into one success field.
  4. Keep address identity, expected version and power-loss durability as separate claims.

These are evidence requirements, not instructions to inject malformed I/O into a live device. A future hardware study needs disposable media, a bounded command matrix and explicit recovery controls.

Structured concepts: protection information, end-to-end protection and io_uring integrity metadata.

Primary sources

Reviewed 17 October 2026. This synthesis connects a historical protocol rule, pinned implementation and executable branch evidence. It does not certify any product.

Published by Hesela team · Published · Updated

Report a correction · Editorial standards · Reuse guidance

Change history

: first published.

: latest recorded update. A detailed change note is not available for this revision.

New-publication RSS · Correction RSS

Cite this page
Hesela team. Protection information: when success skips the checksum. Hesela. 2026-10-17. https://hesela.com/analyses/protection-information-escape-tags/