# SCSI GET LBA STATUS: offline capture framing
Date: 2026-10-21. Original Hesela fixture study, not a device experiment.

## Question and scope
Can a successful offline decode establish that the captured response is complete?
We passed identical, synthetic bytes to two sg_get_lba_status executables with
--inhex=FIXTURE --maxlen=256 --verbose. No DEVICE argument was supplied.
No UNMAP, discard, hardware access, erasure or performance test occurred.

## Design
The complete fixture is 40 bytes: an 8-byte header and two 16-byte descriptors.
Its parameter-data-length is 36 (the total excludes the first four bytes).
The extents start at LBAs 0 and 16, each cover 16 logical blocks, and have
provisioning codes 1 and 3. The header has RTP=1 and completion condition 3.
These are invented fields, not observations of a medium.

For the truncation sweep, retain each prefix of length 0 through 40, leaving
the header's length and completion claim unchanged whenever those bytes survive.
Eight further controls vary status (0,2,4,15), completion (0,1,2), or RTP (0).
There are 49 fixtures per executable, not 98 independent hardware samples.
No sampling-based confidence interval or population failure-rate estimate applies.

The independent framing check counts complete descriptors from actual bytes,
capped by the header length, rather than from decoder text. Its strict control
requires actual length = advertised length, at least eight bytes, and a whole
number of descriptors. All 40 shortened prefixes fail that strict control.
A captured allocation-limited response can legitimately be partial: rejecting it
as incomplete does not accuse the device of violating its protocol. Preserve
partial data separately; do not promote it to a complete inventory.
Passing this check does NOT validate fields, scan coverage, authenticity,
snapshot consistency, sanitization, or physical allocation.

## Executables
results.json: Ubuntu sg3-utils 1.46-3ubuntu4.1, tool version 1.21 20190913.
results-pinned.json: author source commit
1929463554c5dc749c5a89bcefb2c88a08d45575, tool version 1.46 20260613.
Package version, individual utility version and commit are different identifiers.
The latter was built with GCC 13.3.0, CMake 3.28.3, Release, BUILD_SHARED_LIBS=OFF,
target sg_get_lba_status, two build workers. No system installation occurred.
Results record Python/platform and SHA-256 of each actual executable.

Source archive:
https://codeload.github.com/doug-gilbert/sg3_utils/tar.gz/1929463554c5dc749c5a89bcefb2c88a08d45575
SHA-256 fb056551daa844c63665008efe97950dbd7440f180c15ea6da645377d86adb51
Decoder SHA-256 a4cc4de1f16aa639a17830199bdfd4a90f751d4bf511b6430a5b1963e8aec5a7
Manual SHA-256 8eb6ddbc76f2356154067ef42260c38e01c713ca0e63c834c9d114b998396b17
Pinned decoder:
https://github.com/doug-gilbert/sg3_utils/blob/1929463554c5dc749c5a89bcefb2c88a08d45575/src/sg_get_lba_status.c
The input path obtains in_len; the response path bounds decoding by maxlen
and the advertised length, rather than reducing that bound to in_len.
This source observation explains the selected outputs; it is not a claim about
all sg3_utils versions, other command modes, or an exploitable security flaw.

## Results
Each executable returned exit code 0 for prefixes of lengths 4 through 39:
36 of the 40 deliberately incomplete prefixes. Both printed two descriptor rows
for an eight-byte, header-only capture and for a 24-byte, one-descriptor capture.
The absent rows had zero length. The full 40-byte control decoded the two
intended extents. Code 15 remained numerically identifiable, not deallocated.
A complete frame with completion condition 1 still describes an allocation-length
stop, demonstrating why byte completeness and scan coverage are separate tests.
The preserved completion field in a truncated fixture is not fresh evidence
that a device completed anything.

## Reproduction
Run from a directory containing runner.py and test_runner.py:
python3 runner.py --tool /usr/bin/sg_get_lba_status --output replay-old.json
python3 runner.py --tool /path/to/pinned/sg_get_lba_status --output replay-pinned.json
python3 -m unittest -v test_runner.py

To build the pinned source after inspecting its build scripts:
cmake -S sg3_utils-COMMIT -B sg-build -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build sg-build --target sg_get_lba_status -j2

Replay timestamps and environment metadata may differ. Compare case inputs,
framing assessments, exit codes and stdout/stderr, not the entire file hash.
runner.validate_frame(bytes) is a deliberately strict ingestion guard, not a
general SCSI semantic validator. No seed is needed: fixtures are deterministic.

## Rights and related evidence
Hesela's runner and tests: MIT; original fixture bytes and recorded observations:
CC0-1.0. Upstream software is not redistributed; its licenses remain upstream.
Source code is BSD-2-Clause with separately licensed package components.
No paper or manufacturer PDF is redistributed.

Article: https://hesela.com/analyses/discard-is-not-zeroing/#offline-capture
Definition: https://hesela.dev/terms/scsi-get-lba-status/
Wei et al., FAST 2011, sections 2.2, 3.1 and 3.2:
https://static.usenix.org/event/fast11/tech/full_papers/Wei.pdf
That study's raw-flash validation crosses a different evidence boundary.
This fixture experiment neither replicates it nor measures remaining flash data.
