From 8e32ba6efadcf65ba5e01e5883146eaa005dbe8a Mon Sep 17 00:00:00 2001 From: Aurelien DARRAGON Date: Tue, 11 Aug 2026 18:40:05 +0200 Subject: [PATCH] IMPORT: slz/uslz: add a method to impose an envelope format (gzip or zlib) 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 | 4 ++++ src/uslz.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/include/import/slz.h b/include/import/slz.h index 69c74747d..326a11606 100644 --- a/include/import/slz.h +++ b/include/import/slz.h @@ -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, diff --git a/src/uslz.c b/src/uslz.c index bff3eb78a..f8e9db05c 100644 --- a/src/uslz.c +++ b/src/uslz.c @@ -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. 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(). -- 2.47.3