Bug
DemAwareDecoder accepts a DEM with more than 64 observables and then packs
predictions with a u64 bitmask, so observable indices >= 64 wrap onto low bits.
PyDemAwareDecoder::from_dem builds a DemCheckMatrix and never calls
ensure_observables_fit_u64 — that guard appears zero times in
python/pecos-rslib/src/decoder_bindings.rs.
- At decode time it calls the narrow packer
DemCheckMatrix::observables_mask_from_correction
(python/pecos-rslib/src/decoder_bindings.rs:2417-2419), which does
mask |= 1 << i into a u64
(crates/pecos-decoder-core/src/dem.rs:629-639).
- For
i >= 64 that shift panics in debug builds and is masked (i % 64) in
release builds, so observable 64 silently sets the bit for observable 0.
The wide-safe sibling already exists immediately below it:
observables_obsmask_from_correction (crates/pecos-decoder-core/src/dem.rs:646-657),
which is what the string-registry wrappers use
(python/pecos-rslib/src/fault_tolerance_bindings.rs:2168).
Other matching decoders reject wide DEMs loudly instead: Tesseract errors at
construction (crates/pecos-tesseract/src/decoder.rs:187-198) and Fusion Blossom
rejects observable index >= 64 at edge insertion
(crates/pecos-fusion-blossom/src/decoder.rs:689-697).
Expected
Either make DemAwareDecoder wide (use observables_obsmask_from_correction
and widen the result getter), or reject >64-observable DEMs at construction with
the same actionable message the other narrow decoders use. Silent bit-wrapping
is the one outcome that must not remain.
Severity
severity:critical per the repository definition: silently wrong results in
release builds, with no error surfaced. Blast radius is limited to DEMs with
more than 64 observables decoded through DemAwareDecoder (the BP+OSD entry
point documented in docs/workflows/guppy-dem-decoding.md).
Provenance
Found by an adversarial design-review panel on
pecos-docs/design/construction-and-decoding-api-shape.md, then verified
directly against the code.
Bug
DemAwareDecoderaccepts a DEM with more than 64 observables and then packspredictions with a
u64bitmask, so observable indices >= 64 wrap onto low bits.PyDemAwareDecoder::from_dembuilds aDemCheckMatrixand never callsensure_observables_fit_u64— that guard appears zero times inpython/pecos-rslib/src/decoder_bindings.rs.DemCheckMatrix::observables_mask_from_correction(
python/pecos-rslib/src/decoder_bindings.rs:2417-2419), which doesmask |= 1 << iinto au64(
crates/pecos-decoder-core/src/dem.rs:629-639).i >= 64that shift panics in debug builds and is masked (i % 64) inrelease builds, so observable 64 silently sets the bit for observable 0.
The wide-safe sibling already exists immediately below it:
observables_obsmask_from_correction(crates/pecos-decoder-core/src/dem.rs:646-657),which is what the string-registry wrappers use
(
python/pecos-rslib/src/fault_tolerance_bindings.rs:2168).Other matching decoders reject wide DEMs loudly instead: Tesseract errors at
construction (
crates/pecos-tesseract/src/decoder.rs:187-198) and Fusion Blossomrejects observable index >= 64 at edge insertion
(
crates/pecos-fusion-blossom/src/decoder.rs:689-697).Expected
Either make
DemAwareDecoderwide (useobservables_obsmask_from_correctionand widen the result getter), or reject >64-observable DEMs at construction with
the same actionable message the other narrow decoders use. Silent bit-wrapping
is the one outcome that must not remain.
Severity
severity:criticalper the repository definition: silently wrong results inrelease builds, with no error surfaced. Blast radius is limited to DEMs with
more than 64 observables decoded through
DemAwareDecoder(the BP+OSD entrypoint documented in
docs/workflows/guppy-dem-decoding.md).Provenance
Found by an adversarial design-review panel on
pecos-docs/design/construction-and-decoding-api-shape.md, then verifieddirectly against the code.