Bug #13713
Ceph CRC32 algorithm appears broken
0%
Description
I was looking at wireshark packets and I wanted to see if I could verify the crc checksums that Ceph is generating and receiving. The code shows that Ceph is using the castagnoli crc32 variant. I wasn't able to reproduce the checksum so I fell back to looking at Ceph's unit tests and tried to reproduce this: https://github.com/ceph/ceph/blob/master/src/test/common/test_crc32c.cc#L21
I have example code here in Rust: https://gist.github.com/cholcombe973/b0846a814be0a9e885f8
and in Python that produce different results: https://gist.github.com/cholcombe973/a0af818d212e58ae151c
Is the unit test for this file using generated output from the crc32 algorithm in the code base or a known good hash?
I'm guessing I am doing something wrong here but I'm not entirely sure what.
History
#1 Updated by Nathan Cutler about 8 years ago
- Tracker changed from Tasks to Bug
- Project changed from Stable releases to Ceph
#2 Updated by Chris Holcombe about 8 years ago
I found a mailing list showing a known good crc for castagnoli: http://www.pdl.cmu.edu/mailinglists/ips/mail/msg04970.html
The crc's he gives there works with the Rust + Python libraries. I believe now that the rust and python libraries are correct. I still can't figure out how to reproduce ceph's crc values.
I also updated my rust example: https://play.rust-lang.org/?gist=6bca7e3630d48f9225e5&version=stable
It now initializes the crc algorithm with 0 just like Ceph does.
#3 Updated by Chris Holcombe about 8 years ago
Chris Holcombe wrote:
I found a mailing list showing a known good crc for castagnoli: http://www.pdl.cmu.edu/mailinglists/ips/mail/msg04970.html
The crc's he gives there works with the Rust + Python libraries. I believe now that the rust and python libraries are correct. I still can't figure out how to reproduce ceph's crc values.I also updated my rust example: https://play.rust-lang.org/?gist=3e51845d5541002da559&version=stable
It now initializes the crc algorithm with 0 just like Ceph does.
#4 Updated by Chris Holcombe about 8 years ago
I'm almost sure at this point that the ceph crc calculation is incorrect.
I wrote a short C program utilizing the intel baseline code https://github.com/ceph/ceph/blob/master/src/common/crc32c_intel_baseline.c that sage told me to try out. The intel baseline code is producing crc's that disagree with what Mark Bakke <mbakke@cisco.com> says is the known good hash for 32 bytes of 00h.
I also tried another python library which 1) agree's with my previous rust/python findings and 2) disagrees with Ceph's crc calculation: https://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/api/files/crc32c.py?r=170
#5 Updated by Chris Holcombe about 8 years ago
Here's a code snippet of python using google's crc32c python library https://code.google.com/p/googleappengine/source/browse/trunk/python/google/appengine/api/files/crc32c.py?r=170
>>> import crc >>> x = [0 for x in range(0,32)] >>> x [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] >>> len(x) 32 >>> crc.crc(x) 2324772522L
That crc agrees with the mailing list and disagrees with Ceph's crc
#6 Updated by Dan van der Ster about 8 years ago
I checked the partial crc after each iteration in google's python implementation and found that the crc of the last iteration matches ceph's [1]:
from crc32c import crc
crc('foo bar baz')
crc 1197962378
crc 3599162226
crc 2946501991
crc 2501826906
crc 3132034983
crc 3851841059
crc 2745946046
crc 1047783679
crc 767476524
crc 4269731756
crc 4119623852
After finalize (crc ^ 0xFFFFFFFF) the crc becomes:
175343443L
So it looks like ceph_crc32c just isn't doing that final XOR step.
4119623852 ^ 0xFFFFFFFF
175343443
[1] From test_crc32c.cc
const char *a = "foo bar baz";
ASSERT_EQ(4119623852u, ceph_crc32c(0, (unsigned char *)a, strlen(a)));
#7 Updated by Dan van der Ster about 8 years ago
(Sorry for the formatting on the last message -- I think it's readable anyway).
I read some more and found that crc32 implementations vary whether they xor the output with ~0. Linux's approach matches Ceph's -- don't XOR the output -- let the user decide. See https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/lib/crc32.c#n15
* There are various incantations of crc32(). Some use a seed of 0 or ~0. * Some xor at the end with ~0. The generic crc32() function takes * seed as an argument, and doesn't xor at the end. Then individual * users can do whatever they need. * drivers/net/smc9194.c uses seed ~0, doesn't xor with ~0. * fs/jffs2 uses seed 0, doesn't xor with ~0. * fs/partitions/efi.c uses seed ~0, xor's with ~0.
Also, iscsi itself seems not to xor the output: https://github.com/radii/fio/blob/master/crc/crc32c.c#L123
#8 Updated by Chris Holcombe about 8 years ago
Thanks! This was exactly the answer I was hoping for. I thought I might be missing something but I couldn't quite figure out what. Maybe I'll submit a pull request to the developer docs to make it more clear what is going on and why it's not obvious how to reproduce the crc hash.
#9 Updated by Sage Weil about 8 years ago
- Status changed from New to Rejected