The base64-decoder doesn't work on 64-bit big-endian architectures:
authorAlexander Zangerl <exmh@bin.snafu.priv.at>
Thu, 14 Jun 2012 12:31:40 +0000 (07:31 -0500)
committerDavid Levine <levinedl@acm.org>
Thu, 14 Jun 2012 12:31:40 +0000 (07:31 -0500)
mhstore and co. write files of the correct length but which consist
only of \0s.

The culprit is uip/mhparse.c, which contains a pretty ugly base64
decoder (in two places). that thing allocates a "long int" as a
container for the 24 bits that you get from four base-64 chars. the
container is then accessed both as a single entity and as a byte
array, which naturally depends on how wide the long int is and in what
order things end up in there.

The code distinguishes between big- and little-endian systems but
wrongly assumes that sizeof(long int) == 4, which isn't universally
true (quel surprise...).

The attached patch fixes the issue, but in the long run we should
insist on posix and use a64l().

uip/mhparse.c

index 4c3a194..91cc411 100644 (file)
@@ -1737,10 +1737,15 @@ openBase64 (CT ct, char **file)
     CE ce;
     MD5_CTX mdContext;
 
+    /* the decoder works on the least-significant three bytes of the bits integer,
+       but their position in memory depend on both endian-ness and size of 
+       long int... for little-endian architectures the size is irrelevant, for
+       big-endian archs it's crucial... ideally we'd adopt posix and use a64l instead
+       of this mess. */
     b  = (unsigned char *) &bits;
-    b1 = &b[endian > 0 ? 1 : 2];
-    b2 = &b[endian > 0 ? 2 : 1];
-    b3 = &b[endian > 0 ? 3 : 0];
+    b1 = &b[endian > 0 ? sizeof(bits)==8?5:1 : 2];
+    b2 = &b[endian > 0 ? sizeof(bits)==8?6:2 : 1];
+    b3 = &b[endian > 0 ? sizeof(bits)==8?7:3 : 0];
 
     ce = ct->c_cefile;
     if (ce->ce_fp) {
@@ -2825,10 +2830,16 @@ readDigest (CT ct, char *cp)
     unsigned char *dp, value, *ep;
     unsigned char *b, *b1, *b2, *b3;
 
-    b  = (unsigned char *) &bits,
-    b1 = &b[endian > 0 ? 1 : 2],
-    b2 = &b[endian > 0 ? 2 : 1],
-    b3 = &b[endian > 0 ? 3 : 0];
+    /* the decoder works on the least-significant three bytes of the bits integer,
+       but their position in memory depend on both endian-ness and size of 
+       long int... for little-endian architectures the size is irrelevant, for
+       big-endian archs it's crucial... ideally we'd adopt posix and use a64l instead
+       of this mess. */
+    b  = (unsigned char *) &bits;
+    b1 = &b[endian > 0 ? sizeof(bits)==8?5:1 : 2];
+    b2 = &b[endian > 0 ? sizeof(bits)==8?6:2 : 1];
+    b3 = &b[endian > 0 ? sizeof(bits)==8?7:3 : 0];
+
     bitno = 18;
     bits = 0L;
     skip = 0;