proofs.rs

Reassigning Crc::algorithm can leave the lookup table inconsistent

Reassigning Crc::algorithm can leave the lookup table inconsistent

Found in Report #12 v1 · Updated 2026-11-01 02:30 UTC

Description

Reassigning the public Crc::algorithm field on a table-backed instance can make checksum and a finalized digest disagree with a fresh instance constructed from the field's new algorithm. The assignment is possible in safe Rust, but it is unclear whether changing the field after construction is intended to be supported.

Steps to Reproduce

Use crc 3.4.0:

[dependencies]
crc = "=3.4.0"

Run this program with cargo run:

use crc::{Algorithm, Crc, Table};

static A: Algorithm<u8> = Algorithm {
    width: 8,
    poly: 0x07,
    init: 0,
    refin: false,
    refout: false,
    xorout: 0,
    check: 0,
    residue: 0,
};
static B: Algorithm<u8> = Algorithm { poly: 0x31, ..A };

fn main() {
    let mut changed = Crc::<u8, Table<1>>::new(&A);
    changed.algorithm = &B;
    let fresh = Crc::<u8, Table<1>>::new(&B);
    assert_eq!(changed.checksum(&[1]), fresh.checksum(&[1]));
}
Expected Behavior

If reassignment is supported, I would expect the instance to produce the same checksum as a fresh instance using its current algorithm. Otherwise, could the API prevent reassignment or document its effect?

Actual Behavior

The assertion fails: changed.checksum(&[1]) returns 0x07, while the fresh instance returns 0x31. The same discrepancy occurs with a finalized digest and with Table<16> for this one-byte input.

Environment

crc 3.4.0; reproduced in debug and release builds with Rust 1.91.1 on macOS arm64.

Additional Context

Crc::new builds the lookup table from the original algorithm, while assigning algorithm does not rebuild it. Constructing a new Crc with the desired algorithm avoids the inconsistency. The Table<16> observation uses the one-byte tail path.

Change history
  1. v1 · created · 2026-11-01 02:30 UTC
    {
      "title": "Reassigning Crc::algorithm can leave the lookup table inconsistent",
      "body": "## Description\n\nReassigning the public `Crc::algorithm` field on a table-backed instance can make `checksum` and a finalized digest disagree with a fresh instance constructed from the field's new algorithm. The assignment is possible in safe Rust, but it is unclear whether changing the field after construction is intended to be supported.\n\n## Steps to Reproduce\n\nUse `crc` 3.4.0:\n\n```toml\n[dependencies]\ncrc = \"=3.4.0\"\n```\n\nRun this program with `cargo run`:\n\n```rust\nuse crc::{Algorithm, Crc, Table};\n\nstatic A: Algorithm<u8> = Algorithm {\n    width: 8,\n    poly: 0x07,\n    init: 0,\n    refin: false,\n    refout: false,\n    xorout: 0,\n    check: 0,\n    residue: 0,\n};\nstatic B: Algorithm<u8> = Algorithm { poly: 0x31, ..A };\n\nfn main() {\n    let mut changed = Crc::<u8, Table<1>>::new(&A);\n    changed.algorithm = &B;\n    let fresh = Crc::<u8, Table<1>>::new(&B);\n    assert_eq!(changed.checksum(&[1]), fresh.checksum(&[1]));\n}\n```\n\n## Expected Behavior\n\nIf reassignment is supported, I would expect the instance to produce the same checksum as a fresh instance using its current algorithm. Otherwise, could the API prevent reassignment or document its effect?\n\n## Actual Behavior\n\nThe assertion fails: `changed.checksum(&[1])` returns `0x07`, while the fresh instance returns `0x31`. The same discrepancy occurs with a finalized digest and with `Table<16>` for this one-byte input.\n\n## Environment\n\n`crc` 3.4.0; reproduced in debug and release builds with Rust 1.91.1 on macOS arm64.\n\n## Additional Context\n\n`Crc::new` builds the lookup table from the original algorithm, while assigning `algorithm` does not rebuild it. Constructing a new `Crc` with the desired algorithm avoids the inconsistency. The `Table<16>` observation uses the one-byte tail path.",
      "validity": "reported",
      "upstream_status": "not_reported"
    }

Read and join the discussion on the report →