]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: slz: avoid undefined shifts when building the word byte by byte
authorWilly Tarreau <w@1wt.eu>
Sun, 26 Jul 2026 22:13:30 +0000 (00:13 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 28 Jul 2026 14:14:53 +0000 (16:14 +0200)
On the architectures that do not define UNALIGNED_FASTER, the 32-bit word
compared against the reference table is assembled one byte at a time:

    word = ((unsigned char)in[pos] << 8) +
           ((unsigned char)in[pos + 1] << 16) +
           ((unsigned char)in[pos + 2] << 24);

Unexpectedly, an unsigned char is promoted to a *signed* int when
shifting, so shifting a value >= 128 by 24 places can overflow it, which
is undefined behaviour depending on build options. It happens to produce
the expected result with the usual compilers, but ubsan on an i386 build
reports it for about half of input bytes:

  src/slz.c:482:107: runtime error: left shift of 220 by 24 places cannot
                     be represented in type 'int'

Let's just properly cast the uchars to u32 before shifting (this does
not change the produced code at all).

This must be backported to 1.2.

This is libslz upstream commit fbbb46aa54a4d330c6037f72693e0f79c7853354.

Note: in practice it doesn't happen with the compilers and build options
      we support in haproxy but the fix is trivial so better fix this to
      clean up the code base and make ubsan happy.

src/slz.c

index 627f195756368eae417ed57f8b2f66e3e515cacb..564e146c8465162d8d30b70b62e5c4e436a45c12 100644 (file)
--- a/src/slz.c
+++ b/src/slz.c
@@ -573,11 +573,11 @@ long slz_rfc1951_encode(struct slz_stream *strm, unsigned char *out, const unsig
 
 #ifndef UNALIGNED_FASTER
        if (rem >= 4)  // <word> is only used inside the loop below, hence the test for >= 4
-               word = ((unsigned char)in[pos] << 8) + ((unsigned char)in[pos + 1] << 16) + ((unsigned char)in[pos + 2] << 24);
+               word = ((uint32_t)(unsigned char)in[pos] << 8) + ((uint32_t)(unsigned char)in[pos + 1] << 16) + ((uint32_t)(unsigned char)in[pos + 2] << 24);
 #endif
        while (rem >= 4) {
 #ifndef UNALIGNED_FASTER
-               word = ((unsigned char)in[pos + 3] << 24) + (word >> 8);
+               word = ((uint32_t)(unsigned char)in[pos + 3] << 24) + (word >> 8);
 #else
                word = *(uint32_t *)&in[pos];
 #endif
@@ -790,7 +790,7 @@ long slz_rfc1951_encode(struct slz_stream *strm, unsigned char *out, const unsig
 #ifdef UNALIGNED_LE_OK
                        word = *(uint32_t *)&in[pos - 1];
 #else
-                       word = ((unsigned char)in[pos] << 8) + ((unsigned char)in[pos + 1] << 16) + ((unsigned char)in[pos + 2] << 24);
+                       word = ((uint32_t)(unsigned char)in[pos] << 8) + ((uint32_t)(unsigned char)in[pos + 1] << 16) + ((uint32_t)(unsigned char)in[pos + 2] << 24);
 #endif
                }
 #endif