modm::math::crc8_ccitt_update never examines bit 7 of its input. On non-AVR targets the shift happens before the test, and on a uint8_t that discards the bit being tested.
src/modm/math/utils/crc.hpp:
inline uint8_t
crc8_ccitt_update(uint8_t crc, uint8_t data)
{
#ifdef __AVR__
return _crc8_ccitt_update(crc, data);
#else
data ^= crc;
for (uint8_t ii = 0; ii < 8; ii++)
{
data <<= 1; // <-- bit 7 is gone here
if (data & 0x80) data ^= 0x07; // <-- so this tests the pre-shift bit 6
}
return data;
#endif
}
The avr-libc assembly it replaces:
1: lsl %0 ; shift left, MSB -> CARRY
brcc 2f ; branch on CARRY, i.e. the *pre-shift* MSB
eor %0, %2
lsl preserves the departing bit in the carry flag, so brcc still tests bit 7 of the value before the shift. C has no carry flag, so data <<= 1 simply loses that bit and the following if (data & 0x80) reads the pre-shift bit 6 instead.
The AVR path is correct; only the #else branch is affected.
Impact
crc8_ccitt("123456789") returns 0x9B. CRC-8 with polynomial 0x07 and init 0xFF is 0xFB.
Because one input bit is ignored:
crc8_ccitt_update(crc, d) == crc8_ccitt_update(crc, d ^ 0x80) for all 32768 (crc, d) pairs
- only 128 of 256 output values are reachable
- for an n-byte message, 2ⁿ distinct messages share a CRC
Suggested fix
Capture the MSB before shifting, which is what the carry flag does on AVR:
data ^= crc;
for (uint8_t ii = 0; ii < 8; ii++)
{
const bool msb = (data & 0x80) != 0;
data <<= 1;
if (msb) data ^= 0x07;
}
return data;
Notes
- Tested against
develop at 2390135.
- Checked crc16_ccitt and crc32 against their published check values (0x6F91 CRC-16/MCRF4XX and 0xCBF43926 CRC-32/ISO-HDLC); both are correct and MSB-sensitive. Only crc8_ccitt is affected.
modm::math::crc8_ccitt_updatenever examines bit 7 of its input. On non-AVR targets the shift happens before the test, and on auint8_tthat discards the bit being tested.src/modm/math/utils/crc.hpp:The avr-libc assembly it replaces:
lslpreserves the departing bit in the carry flag, sobrccstill tests bit 7 of the value before the shift. C has no carry flag, sodata <<= 1simply loses that bit and the followingif (data & 0x80)reads the pre-shift bit 6 instead.The AVR path is correct; only the
#elsebranch is affected.Impact
crc8_ccitt("123456789")returns0x9B. CRC-8 with polynomial0x07and init0xFFis0xFB.Because one input bit is ignored:
crc8_ccitt_update(crc, d) == crc8_ccitt_update(crc, d ^ 0x80)for all 32768(crc, d)pairsSuggested fix
Capture the MSB before shifting, which is what the carry flag does on AVR:
Notes
developat2390135.