Skip to content

Research notes / Data integrity

CRC-32 vs CRC-32C: which bit errors escape detection?

19 October 2026 / Primary-source synthesis and deterministic computation

A 32-bit checksum does not imply a universal one-in-four-billion chance of missing corruption. The polynomial, protected length and error pattern matter. We construct changed 4 KiB payloads that preserve CRC-32 but fail CRC-32C, and another family with the opposite result.

On this page8 sections

What does a matching CRC actually exclude?

Our identity and lost-write analysis explains why valid bytes can belong to the wrong address or generation. Here the address, length and stored reference checksum stay fixed. The bytes really change. The narrower question is which changes the checksum function cannot distinguish.

RFC 3385 analyzes error detection using explicit channel models, not a universal storage-failure probability. Its near-2-32 argument relies on a distribution over possible received words. A selected generator-shaped error is not a uniform draw from that space. [1, section 3]

These are reproducible illustrations of established coding theory, not a newly discovered CRC flaw. The examples provide regression fixtures for understanding a checker's contract.

Thirty changed payloads, unchanged reference checksums

We use three 4096-byte payloads: all zeroes, all FF bytes, and 00 through FF repeated sixteen times. Each of two patterns is applied at processing-bit offsets 0, 1, 7, 2048 and 32735. Bit zero is the least-significant bit of byte zero. The last offset reaches the final payload bit without crossing its boundary.

The complete generator polynomials are 0x104C11DB7 for IEEE CRC-32 and 0x11EDC6F41 for CRC-32C. The leading bit represents x32; these are not the reflected constants used in right-shifting implementations. We feed each polynomial's coefficients from highest to lowest degree into the stated processing-bit order. [4] [2, section 13.1]

Zero-filled 4096-byte payload, offset 0. Values are numerical CRC results in hexadecimal.
PatternChanged bitsCRC-32 before / afterCRC-32C before / after
IEEE generator15c71c0011
c71c0011

Not detected
98f94189
fd1d1cfd

Detected
Castagnoli generator18c71c0011
1ccf4c61

Detected
98f94189
98f94189

Not detected

All fifteen placements and payloads for each pattern give the same detection relationship. The IEEE-shaped pattern changes 15 bits; the Castagnoli-shaped pattern changes 18. Both span 33 processing bits. Neither pattern is claimed to have the fewest possible changed bits. The full result records every changed position, both CRC values and before/after SHA-256 hashes.

We never recompute the stored reference to make a changed payload pass. We recompute only the candidate's CRC, then compare it with the original reference. This distinction separates an actual collision from simply replacing the checksum alongside the data.

Why a 33-bit pattern defeats a 32-bit check

In polynomial arithmetic over GF(2), addition is XOR. Let G(x) be the degree-32 generator and E(x) the difference between the original and changed messages. An error proportional to G(x), including xjG(x), has zero remainder modulo G(x). The usual appended x32 factor does not change that divisibility. Fixed initialization and final XOR cancel when comparing equal-length messages. This explains our observed collisions.

A burst span counts the interval from the first changed bit through the last, including unchanged bits inside it. It does not count only flipped bits. For either generator here, every nonzero burst spanning at most 32 processing bits is detected: after removing the position shift, its degree is below 32 and cannot be a nonzero multiple of G. The constant term 1 ensures that shifting cannot introduce the missing factor.

Our 33-bit examples sit immediately beyond that span guarantee. A physical fault may be reordered by interleaving, encoding or other transformations before these bits reach a CRC. Do not equate processing-bit adjacency with measured physical damage.

The CRC-32C generator is divisible by x + 1. Evaluating at x = 1 therefore shows that every undetectable error must have even weight; all odd numbers of bit inversions are detected under this fixed-length model. The IEEE generator lacks that factor, and our 15-bit example demonstrates the difference. This property alone does not rank overall detection quality. Koopman and Chakravarty explicitly illustrate the trade-off between odd-weight detection and other error patterns. [3, section 4.1]

Can one change pass both CRCs?

Yes. Our additional construction multiplies the two generators over GF(2), using XOR rather than arithmetic carry. The product 0x11A6EC5C893B4ED77 is divisible by each generator. In the same fixed-length, reflected-bit convention, XORing its coefficients into a payload leaves both reference CRCs unchanged.

Both references remain fixed; zero-filled 4096-byte payload at processing-bit offset 0
Changed bits / spanCRC-32 before / afterCRC-32C before / after
36 / 65 bitsc71c0011
c71c0011
98f94189
98f94189

The pattern changes 36 bits across a 65-bit span. We verify fifteen cases: the same three payloads at offsets 0, 1, 7, 2048 and 32703. The last placement ends at bit 32767. All fifteen payload hashes change while both CRC values match their original references. A separate coefficient-convolution test checks the product, and polynomial division checks divisibility by both generators.

This is another constructed example, not evidence that two CRCs provide no additional detection. Nor does the existence of a common collision prove statistical dependence under every error distribution. It shows why a probability for the pair requires a specified distribution and joint analysis: two 32-bit labels alone do not establish an unconditional 2-64 miss rate. We do not search for the lowest-weight shared collision or claim this product is a new coding-theory result.

Length can change the guarantee without changing the CRC

To make length dependence independently checkable, we also enumerate a separate four-bit teaching code with G(x) = x4 + x + 1. We encode every nonzero data word and find the smallest number of 1 bits in the resulting codeword. For this linear code, that is its minimum Hamming distance.

Exact enumeration for a four-bit CRC, not estimates for CRC-32 or CRC-32C
Data bitsTotal bitsNonzero codewordsMinimum distance
48153
111520473
121640952

At eleven data bits, all one- and two-bit errors in the 15-bit codeword are detectable. At twelve data bits, a two-bit counterexample fits: x15 + 1 is divisible by G. The CRC width has not changed. This is an exhaustive statement about this small code, not a claim that a 32-bit checksum has the same threshold.

The DSN 2004 paper evaluates polynomial choices against message lengths and error models. Its search concerns 3- to 16-bit CRCs and data words up to 2048 bits, not measurements of HDD or SSD corruption. We use its methodological distinction; we do not present our fixtures as a reproduction of its full search. [3]

Reproduce the result and its controls

The Python experiment uses the standard library only. CRC-32 is computed with zlib.crc32; CRC-32C uses an explicitly parameterized teaching implementation. The tests check five published iSCSI vectors, including byte order, and independently compare the IEEE implementation with zlib. [2, Appendix A.4] [5]

Additional controls include the fifteen dual-CRC cases above, carryless-product checks, single-bit changes, selected 32-bit-span changes, out-of-range inputs, polynomial divisibility, and all 2048 burst shapes of span 1 through 12 at four selected shifts for both generators. Those 16,384 algebraic checks do not enumerate every 32-bit burst. The small-code distance test independently enumerates all nonzero error masks of each selected total length.

python3 -B -m unittest -v test_experiment.py
python3 -B experiment.py --output results.json

The generated JSON includes source-code hashes, runtime versions, generation time, parameters and limitations. Results are deterministic; generation time and runtime metadata can differ. No random seed, private dataset or hardware is required.

What to record in a storage integrity contract

Name the polynomial, initialization, reflection, final XOR, serialization, covered bytes and maximum length. State whether the fault model concerns isolated inversions, bursts, misplaced records or hostile changes. Specify where the trusted reference resides and what a mismatch triggers.

Do not use these fixtures to estimate field collision rates, compare device reliability, infer a 32-bit code's minimum distance, or replace an on-disk checksum algorithm without a format migration. CRCs are not authentication mechanisms. The common-multiple example above also illustrates why combining two CRCs requires analyzing their joint detection coverage. [1, section 10]

For distinct system-level limits, continue with errors versus erasures during recovery and FITS checksums and reordered pixels. These mechanisms require separate evidence.

Primary sources

  1. Sheinwald et al. RFC 3385: iSCSI CRC/Checksum Considerations, sections 2-3 and 10 (2002)
  2. Chadalapaka et al. RFC 7143: iSCSI consolidated specification, section 13.1 and Appendix A.4 (2014)
  3. Koopman and Chakravarty. CRC Polynomial Selection For Embedded Networks. DSN 2004, sections 2-4
  4. Deutsch. RFC 1952: GZIP format, CRC32 field and sample CRC code (1996)
  5. Python zlib documentation: crc32 return value and checking role

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. CRC-32 vs CRC-32C: which bit errors escape detection?. Hesela. 2026-10-19. https://hesela.com/analyses/crc-polynomials-error-patterns/