]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
IMPORT: slz/uslz: add a method to impose an envelope format (gzip or zlib)
authorAurelien DARRAGON <adarragon@haproxy.com>
Tue, 11 Aug 2026 16:40:05 +0000 (18:40 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Wed, 12 Aug 2026 07:14:07 +0000 (09:14 +0200)
This is actually a way to refuse non-matching formats and not letting the
decoder auto-detect a format. This is done using uslz_init_fmt() with a
4th argument instead of uslz_init():

  - SLZ_FMT_DEFLATE skips detection entirely, since the first bits of the
    stream are already the first block header (deflate has no envelpoe).

  - SLZ_FMT_GZIP and SLZ_FMT_ZLIB parse their envelope as before, but the
    stream is now rejected with E_CORRUPT if it does not carry the
    announced one, instead of falling back to raw deflate and returning
    garbage. That might matter when the format comes from the outside, an
    HTTP Content- Encoding for instance, where accepting a different
    envelope is wrong (though would likely have to be accepted anyway).

The main purpose is in fact to use this in tests to validate specific
envelopes, something that is currently not possible (i.e. if zenc lies,
zdec silently adapts).

This is libslz upstream commit d2829de469741f0c664fcc3e29d6152ab6993ef4

include/import/slz.h
src/uslz.c

index 69c74747d22ff2e0c8fa95f88b97515941cb33b3..326a116066895bef980f95305cbf1f986c206a7a 100644 (file)
@@ -203,6 +203,8 @@ static inline int slz_flush(struct slz_stream *strm, void *buf)
 #define USLZ_FL_ZLIB        0x0002
 #define USLZ_FL_FINAL       0x0004   /* current block is the last one. */
 #define USLZ_FL_COMPLETE    0x0008   /* last block is completely treated, marks end of the decompressed stream */
+#define USLZ_FL_EXP_GZIP    0x0010   /* caller explicitly imposed the gzip envelope */
+#define USLZ_FL_EXP_ZLIB    0x0020   /* caller explicitly imposed the zlib envelope */
 
 
 enum uslz_stream_state {
@@ -348,6 +350,8 @@ enum uslz_decode_ret {
 };
 
 int uslz_init(struct uslz_stream *strm, unsigned char *output_buffer, long output_size);
+int uslz_init_fmt(struct uslz_stream *strm, unsigned char *output_buffer, long output_size,
+                  int format);
 enum uslz_decode_ret uslz_decode(struct uslz_stream *state,
                                  const unsigned char *compressed_data, long compressed_size,
                                  unsigned char **decoded_data, long *decoded_size,
index bff3eb78ad3dd2fdf179728b3691cf78c76a0add..f8e9db05c3dfb4bc7759d0ffd44644b5a2a3498d 100644 (file)
@@ -1391,6 +1391,15 @@ enum uslz_decode_ret uslz_decode(struct uslz_stream *state,
                        goto next_block;
                }// else raw format without pending bytes
 
+               /* The caller may have imposed an envelope. Refuse a stream
+                * that does not carry it rather than silently decoding it as
+                * something else, which for a raw deflate fallback would
+                * produce garbage instead of an error.
+                */
+               if (((state->flags & USLZ_FL_EXP_GZIP) && !(state->flags & USLZ_FL_GZIP)) ||
+                   ((state->flags & USLZ_FL_EXP_ZLIB) && !(state->flags & USLZ_FL_ZLIB)))
+                       return USLZ_DECODE_E_CORRUPT;
+
                state->state = USLZ_ST_HEADER;
        }
 
@@ -1480,6 +1489,51 @@ int uslz_init(struct uslz_stream *state,
        return 1;
 }
 
+/* Same as uslz_init() but tells the decoder which envelope to expect, instead
+ * of detecting it from the first bytes of the stream. <format> is one of the
+ * SLZ_FMT_* values:
+ *
+ *   SLZ_FMT_DEFLATE  raw deflate (rfc1951). This one cannot be detected: it
+ *                    has no header at all, so it is only ever reached as the
+ *                    fallback when the stream looks like neither gzip nor
+ *                    zlib. A raw stream whose first two bytes happen to form
+ *                    a valid zlib header would be mis-detected, so a caller
+ *                    that knows it is decoding raw deflate should say so.
+ *   SLZ_FMT_GZIP     gzip (rfc1952)
+ *   SLZ_FMT_ZLIB     zlib (rfc1950)
+ *
+ * For the latter two the envelope is still parsed as usual, and the stream is
+ * now rejected with USLZ_DECODE_E_CORRUPT if it does not carry the announced
+ * one. That matters when the format comes from an outside source, for instance
+ * an HTTP Content-Encoding, where accepting a different envelope is wrong.
+ *
+ * Returns 1 on success and 0 on failure, including for an unknown format.
+ */
+int uslz_init_fmt(struct uslz_stream *state, unsigned char *output_buffer,
+                  long output_size, int format)
+{
+       if (!uslz_init(state, output_buffer, output_size))
+               return 0;
+
+       switch (format) {
+       case SLZ_FMT_DEFLATE:
+               /* nothing to detect nor to skip, the first bits are already
+                * the first block header.
+                */
+               state->state = USLZ_ST_HEADER;
+               break;
+       case SLZ_FMT_GZIP:
+               state->flags |= USLZ_FL_EXP_GZIP;
+               break;
+       case SLZ_FMT_ZLIB:
+               state->flags |= USLZ_FL_EXP_ZLIB;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
 /* prepare the static huffman decoding table, which is a 9bits direct
  * lookup array to enable fast static huffman decoding in
  * gethuff_fixed().