Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 7fe3a11

Browse files
authored
[crc64] Fix a subtle bug in CRC64 splice-by-8 implementation (#627)
Commit 9b88ce7 implemented a splice-by-8 version of the CRC64 algorithm which processes 8 bytes per loop iteration. However, the code had a subtle bug which resulted in the same CRC calculated for different strings and made some of the Xamarin.Android tests to fail. The following strings yielded the same checksum value: * `obj/Debug/lp/10/jl/bin/classes.jar` * `obj/Debug/lp/11/jl/bin/classes.jar` * `obj/Debug/lp/12/jl/bin/classes.jar` The reason for this was subtle (a stupid oversight on my part, really): the loop fetched values from the input array by using an index into the array: crc ^= (ulong)aptr[idx]; However, with `aptr` being declared as `byte* aptr` the indexing operation returned a single *byte* instead of the required 64-bit word (8 bytes) and, thus, on each iteration of the loop 7 bytes of the input arrays were ignored in calculation, thus causing the collisions. The fix is to cast `aptr` to `ulong*` and *then* index it, extracting the required 8 bytes. This commit also adds a test to check for this issue.
1 parent 9b88ce7 commit 7fe3a11

5 files changed

Lines changed: 60 additions & 22 deletions

File tree

‎src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/Crc64.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ unsafe protected override void HashCore (byte [] array, int ibStart, int cbSize)
6161
fixed (ulong*tptr=Table){
6262
fixed (byte*aptr=array){
6363
while(len>=8){
64-
crc^=(ulong)aptr[idx];
64+
crc^=*((ulong*)(aptr+idx));
6565
crc=
6666
tptr[7*256+(crc&0xff)]^
6767
tptr[6*256+((crc>>8)&0xff)]^

‎tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/Crc64Tests.cs‎

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
usingSystem.Text;
2+
usingSystem.Security.Cryptography;
23
usingJava.Interop.Tools.JavaCallableWrappers;
34
usingNUnit.Framework;
45

@@ -34,13 +35,50 @@ public void Hello ()
3435
publicvoidXmlDocument()
3536
{
3637
varactual=ToHash("System.Xml.XmlDocument, System.Xml");
37-
Assert.AreEqual("b9c1bdfc7cd47543",actual);
38+
Assert.AreEqual("348bbd9fecf1b865",actual);
3839
}
3940

4041
[Test]
4142
publicvoidCollision()
4243
{
4344
Assert.AreNotEqual(ToHash(""),ToHash(newbyte[32]));
4445
}
46+
47+
[Test]
48+
publicvoidAllBytesAreProcessed()
49+
{
50+
// Slicing processes 8 bytes (a 64-bit word) at a time, and if any of the bytes are skipped we will have a
51+
// collision here.
52+
string[]inputs={
53+
"obj/Debug/lp/10/jl/bin/classes.jar",
54+
"obj/Debug/lp/11/jl/bin/classes.jar",
55+
"obj/Debug/lp/12/jl/bin/classes.jar",
56+
};
57+
58+
string[]expected={
59+
"419a37c9bcfddf3c",
60+
"6ea5e242b7cc24a7",
61+
"74770a86f8b97020",
62+
};
63+
64+
string[]outputs=newstring[inputs.Length];
65+
66+
for(inti=0;i<inputs.Length;i++){
67+
byte[]bytes=Encoding.UTF8.GetBytes(inputs[i]);
68+
using(HashAlgorithmhashAlg=newCrc64()){
69+
byte[]hash=hashAlg.ComputeHash(bytes);
70+
outputs[i]=ToHash(hash);
71+
Assert.AreEqual(expected[i],outputs[i],$"hash {i} differs");
72+
}
73+
}
74+
75+
for(inti=0;i<outputs.Length;i++){
76+
for(intj=0;j<outputs.Length;j++){
77+
if(j==i)
78+
continue;
79+
Assert.AreNotEqual(outputs[i],outputs[j],$"Outputs {i} and {j} are identical");
80+
}
81+
}
82+
}
4583
}
4684
}

‎tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public void GenerateIndirectApplication (
124124
)
125125
{
126126
varactual=Generate(typeof(IndirectApplication),applicationJavaClass);
127-
varexpected=@"package c64r2b57424eba1fa9d27;
127+
varexpected=@"package c64r2197ae30a36756915;
128128
129129
130130
public class IndirectApplication
@@ -175,7 +175,7 @@ public void monodroidClearReferences ()
175175
publicvoidGenerateExportedMembers()
176176
{
177177
varactual=Generate(typeof(ExportsMembers));
178-
varexpected=@"package c64r2b57424eba1fa9d27;
178+
varexpected=@"package c64r2197ae30a36756915;
179179
180180
181181
public class ExportsMembers
@@ -187,7 +187,7 @@ extends java.lang.Object
187187
public static final String __md_methods;
188188
static {
189189
__md_methods =
190-
""n_GetInstance:()Lc64r2b57424eba1fa9d27/ExportsMembers;:__export__\n"" +
190+
""n_GetInstance:()Lc64r2197ae30a36756915/ExportsMembers;:__export__\n"" +
191191
""n_GetValue:()Ljava/lang/String;:__export__\n"" +
192192
""n_methodNamesNotMangled:()V:__export__\n"" +
193193
""n_CompletelyDifferentName:(Ljava/lang/String;I)Ljava/lang/String;:__export__\n"" +
@@ -198,17 +198,17 @@ extends java.lang.Object
198198
}
199199
200200
201-
public static c64r2b57424eba1fa9d27.ExportsMembers STATIC_INSTANCE = GetInstance ();
201+
public static c64r2197ae30a36756915.ExportsMembers STATIC_INSTANCE = GetInstance ();
202202
203203
204204
public java.lang.String VALUE = GetValue ();
205205
206-
public static c64r2b57424eba1fa9d27.ExportsMembers GetInstance ()
206+
public static c64r2197ae30a36756915.ExportsMembers GetInstance ()
207207
{
208208
return n_GetInstance ();
209209
}
210210
211-
private static native c64r2b57424eba1fa9d27.ExportsMembers n_GetInstance ();
211+
private static native c64r2197ae30a36756915.ExportsMembers n_GetInstance ();
212212
213213
public java.lang.String GetValue ()
214214
{
@@ -271,7 +271,7 @@ public void monodroidClearReferences ()
271271
publicvoidGenerateInnerClass()
272272
{
273273
varactual=Generate(typeof(ExampleOuterClass));
274-
varexpected=@"package c64r2b57424eba1fa9d27;
274+
varexpected=@"package c64r2197ae30a36756915;
275275
276276
277277
public class ExampleOuterClass

‎tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaNativeTypeManagerTests.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void TearDown ()
2929
publicvoidCrc64()
3030
{
3131
JavaNativeTypeManager.PackageNamingPolicy=PackageNamingPolicy.LowercaseCrc64;
32-
Assert.AreEqual("c64r279bf423bcb581100",JavaNativeTypeManager.GetPackageName(typeof(string)));
32+
Assert.AreEqual("c64r2b74743e9328eed0a",JavaNativeTypeManager.GetPackageName(typeof(string)));
3333
}
3434

3535
[Test]

‎tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/TypeNameMapGeneratorTests.cs‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,12 @@ public void WriteJavaToManaged ()
5959
"value-offset="+offset+"\u0000"+
6060
GetJ2MEntryLine(typeof(ActivityName),"activity/Name",offset,length)+
6161
GetJ2MEntryLine(typeof(ApplicationName),"application/Name",offset,length)+
62-
GetJ2MEntryLine(typeof(DefaultName),"c64r2b57424eba1fa9d27/DefaultName",offset,length)+
63-
GetJ2MEntryLine(typeof(DefaultName.A),"c64r2b57424eba1fa9d27/DefaultName_A",offset,length)+
64-
GetJ2MEntryLine(typeof(DefaultName.A.B),"c64r2b57424eba1fa9d27/DefaultName_A_B",offset,length)+
65-
GetJ2MEntryLine(typeof(DefaultName.C.D),"c64r2b57424eba1fa9d27/DefaultName_C_D",offset,length)+
66-
GetJ2MEntryLine(typeof(ExampleOuterClass),"c64r2b57424eba1fa9d27/ExampleOuterClass",offset,length)+
67-
GetJ2MEntryLine(typeof(ExampleOuterClass.ExampleInnerClass),"c64r2b57424eba1fa9d27/ExampleOuterClass$ExampleOuterClass_ExampleInnerClass",offset,length)+
62+
GetJ2MEntryLine(typeof(DefaultName),"c64r2197ae30a36756915/DefaultName",offset,length)+
63+
GetJ2MEntryLine(typeof(DefaultName.A),"c64r2197ae30a36756915/DefaultName_A",offset,length)+
64+
GetJ2MEntryLine(typeof(DefaultName.A.B),"c64r2197ae30a36756915/DefaultName_A_B",offset,length)+
65+
GetJ2MEntryLine(typeof(DefaultName.C.D),"c64r2197ae30a36756915/DefaultName_C_D",offset,length)+
66+
GetJ2MEntryLine(typeof(ExampleOuterClass),"c64r2197ae30a36756915/ExampleOuterClass",offset,length)+
67+
GetJ2MEntryLine(typeof(ExampleOuterClass.ExampleInnerClass),"c64r2197ae30a36756915/ExampleOuterClass$ExampleOuterClass_ExampleInnerClass",offset,length)+
6868
GetJ2MEntryLine(typeof(InstrumentationName),"instrumentation/Name",offset,length)+
6969
GetJ2MEntryLine(typeof(AbstractClass),"my/AbstractClass",offset,length)+
7070
GetJ2MEntryLine(typeof(ExampleActivity),"my/ExampleActivity",offset,length)+
@@ -139,14 +139,14 @@ public void WriteManagedToJava ()
139139
GetM2JEntryLine(typeof(AbstractClassInvoker),"my/AbstractClass",offset,length)+
140140
GetM2JEntryLine(typeof(ActivityName),"activity/Name",offset,length)+
141141
GetM2JEntryLine(typeof(ApplicationName),"application/Name",offset,length)+
142-
GetM2JEntryLine(typeof(DefaultName.A.B),"c64r2b57424eba1fa9d27/DefaultName_A_B",offset,length)+
143-
GetM2JEntryLine(typeof(DefaultName.A),"c64r2b57424eba1fa9d27/DefaultName_A",offset,length)+
144-
GetM2JEntryLine(typeof(DefaultName.C.D),"c64r2b57424eba1fa9d27/DefaultName_C_D",offset,length)+
145-
GetM2JEntryLine(typeof(DefaultName),"c64r2b57424eba1fa9d27/DefaultName",offset,length)+
142+
GetM2JEntryLine(typeof(DefaultName.A.B),"c64r2197ae30a36756915/DefaultName_A_B",offset,length)+
143+
GetM2JEntryLine(typeof(DefaultName.A),"c64r2197ae30a36756915/DefaultName_A",offset,length)+
144+
GetM2JEntryLine(typeof(DefaultName.C.D),"c64r2197ae30a36756915/DefaultName_C_D",offset,length)+
145+
GetM2JEntryLine(typeof(DefaultName),"c64r2197ae30a36756915/DefaultName",offset,length)+
146146
GetM2JEntryLine(typeof(ExampleActivity),"my/ExampleActivity",offset,length)+
147147
GetM2JEntryLine(typeof(ExampleInstrumentation),"my/ExampleInstrumentation",offset,length)+
148-
GetM2JEntryLine(typeof(ExampleOuterClass.ExampleInnerClass),"c64r2b57424eba1fa9d27/ExampleOuterClass$ExampleOuterClass_ExampleInnerClass",offset,length)+
149-
GetM2JEntryLine(typeof(ExampleOuterClass),"c64r2b57424eba1fa9d27/ExampleOuterClass",offset,length)+
148+
GetM2JEntryLine(typeof(ExampleOuterClass.ExampleInnerClass),"c64r2197ae30a36756915/ExampleOuterClass$ExampleOuterClass_ExampleInnerClass",offset,length)+
149+
GetM2JEntryLine(typeof(ExampleOuterClass),"c64r2197ae30a36756915/ExampleOuterClass",offset,length)+
150150
GetM2JEntryLine(typeof(InstrumentationName),"instrumentation/Name",offset,length)+
151151
GetM2JEntryLine(typeof(NonStaticOuterClass.NonStaticInnerClass),"register/NonStaticOuterClass$NonStaticInnerClass",offset,length)+
152152
GetM2JEntryLine(typeof(NonStaticOuterClass),"register/NonStaticOuterClass",offset,length)+

0 commit comments

Comments
 (0)