Reassigning Crc::algorithm can leave the lookup table inconsistent
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
- 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" }