]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
Improve resilience of the functable initialization; during functable init,
authorHans Kristian Rosbach <hk-git@circlestorm.org>
Fri, 10 Oct 2025 11:33:53 +0000 (13:33 +0200)
committerHans Kristian Rosbach <hk-github@circlestorm.org>
Tue, 21 Oct 2025 17:48:30 +0000 (19:48 +0200)
make sure none of the function pointers are nullpointers.

Up until now, zlib-ng and the application would have segfaulted either at the start
of processing, or at some point later depending on when a nullpointer call would happen
in the processing. In any case most likely after accepting data from the application.

Now, the deflateinit/inflateinit functions will error with Z_VERSION_ERROR, and
gzopen will return Z_STREAM_ERROR before actually processing any data.

Direct calls to functions like adler32 or crc32 will however print an error message
and call abort(), as these functions have no actual way of reporting errors.

Note: This should never happen with default builds of zlib-ng, only if it is run on
a cpu that is missing both the matching optimized and the generic fallback functions.
This can currently only happen if zlib-ng is compiled using custom cflags or by
editing the code.

functable.c
functable.h
zbuild.h

index 4481fdb9df12ba8970c69f2703129a4a1ca7cb7f..f8a122d8da7b8c920013c58926c06cd35633f38e 100644 (file)
 #  define FUNCTABLE_BARRIER() do { /* Empty */ } while (0)
 #endif
 
-static void force_init_empty(void) {
-    // empty
+/* Verify all pointers are valid before assigning, return 1 on failure
+ * This allows inflateinit/deflateinit functions to gracefully return Z_VERSION_ERROR
+ * if functable initialization fails.
+ */
+#define FUNCTABLE_VERIFY_ASSIGN(VAR, FUNC_NAME) \
+    if (!VAR.FUNC_NAME) { \
+        fprintf(stderr, "Zlib-ng functable failed initialization!\n"); \
+        return 1; \
+    } \
+    FUNCTABLE_ASSIGN(VAR, FUNC_NAME);
+
+/* Functable init & abort on failure.
+ * Abort is needed because some stub functions are reachable without first
+ * calling any inflateinit/deflateinit functions, and have no error propagation.
+ */
+#define FUNCTABLE_INIT_ABORT \
+    if (init_functable()) { \
+        fprintf(stderr, "Zlib-ng functable failed initialization!\n"); \
+        abort(); \
+    };
+
+// Empty stub, used when functable has already been initialized
+static int force_init_empty(void) {
+    return 0;
 }
 
-static void init_functable(void) {
+/* Functable initialization.
+ * Selects the best available optimized functions appropriate for the runtime cpu.
+ */
+static int init_functable(void) {
     struct functable_s ft;
     struct cpu_features cf;
 
@@ -324,91 +349,93 @@ static void init_functable(void) {
 
     // Assign function pointers individually for atomic operation
     FUNCTABLE_ASSIGN(ft, force_init);
-    FUNCTABLE_ASSIGN(ft, adler32);
-    FUNCTABLE_ASSIGN(ft, adler32_fold_copy);
-    FUNCTABLE_ASSIGN(ft, chunkmemset_safe);
-    FUNCTABLE_ASSIGN(ft, compare256);
-    FUNCTABLE_ASSIGN(ft, crc32);
-    FUNCTABLE_ASSIGN(ft, crc32_fold);
-    FUNCTABLE_ASSIGN(ft, crc32_fold_copy);
-    FUNCTABLE_ASSIGN(ft, crc32_fold_final);
-    FUNCTABLE_ASSIGN(ft, crc32_fold_reset);
-    FUNCTABLE_ASSIGN(ft, inflate_fast);
-    FUNCTABLE_ASSIGN(ft, longest_match);
-    FUNCTABLE_ASSIGN(ft, longest_match_slow);
-    FUNCTABLE_ASSIGN(ft, slide_hash);
+    FUNCTABLE_VERIFY_ASSIGN(ft, adler32);
+    FUNCTABLE_VERIFY_ASSIGN(ft, adler32_fold_copy);
+    FUNCTABLE_VERIFY_ASSIGN(ft, chunkmemset_safe);
+    FUNCTABLE_VERIFY_ASSIGN(ft, compare256);
+    FUNCTABLE_VERIFY_ASSIGN(ft, crc32);
+    FUNCTABLE_VERIFY_ASSIGN(ft, crc32_fold);
+    FUNCTABLE_VERIFY_ASSIGN(ft, crc32_fold_copy);
+    FUNCTABLE_VERIFY_ASSIGN(ft, crc32_fold_final);
+    FUNCTABLE_VERIFY_ASSIGN(ft, crc32_fold_reset);
+    FUNCTABLE_VERIFY_ASSIGN(ft, inflate_fast);
+    FUNCTABLE_VERIFY_ASSIGN(ft, longest_match);
+    FUNCTABLE_VERIFY_ASSIGN(ft, longest_match_slow);
+    FUNCTABLE_VERIFY_ASSIGN(ft, slide_hash);
 
     // Memory barrier for weak memory order CPUs
     FUNCTABLE_BARRIER();
+
+    return Z_OK;
 }
 
 /* stub functions */
-static void force_init_stub(void) {
-    init_functable();
+static int force_init_stub(void) {
+    return init_functable();
 }
 
 static uint32_t adler32_stub(uint32_t adler, const uint8_t* buf, size_t len) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.adler32(adler, buf, len);
 }
 
 static uint32_t adler32_fold_copy_stub(uint32_t adler, uint8_t* dst, const uint8_t* src, size_t len) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.adler32_fold_copy(adler, dst, src, len);
 }
 
 static uint8_t* chunkmemset_safe_stub(uint8_t* out, uint8_t *from, unsigned len, unsigned left) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.chunkmemset_safe(out, from, len, left);
 }
 
 static uint32_t compare256_stub(const uint8_t* src0, const uint8_t* src1) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.compare256(src0, src1);
 }
 
 static uint32_t crc32_stub(uint32_t crc, const uint8_t* buf, size_t len) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.crc32(crc, buf, len);
 }
 
 static void crc32_fold_stub(crc32_fold* crc, const uint8_t* src, size_t len, uint32_t init_crc) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     functable.crc32_fold(crc, src, len, init_crc);
 }
 
 static void crc32_fold_copy_stub(crc32_fold* crc, uint8_t* dst, const uint8_t* src, size_t len) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     functable.crc32_fold_copy(crc, dst, src, len);
 }
 
 static uint32_t crc32_fold_final_stub(crc32_fold* crc) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.crc32_fold_final(crc);
 }
 
 static uint32_t crc32_fold_reset_stub(crc32_fold* crc) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.crc32_fold_reset(crc);
 }
 
 static void inflate_fast_stub(PREFIX3(stream) *strm, uint32_t start) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     functable.inflate_fast(strm, start);
 }
 
 static uint32_t longest_match_stub(deflate_state* const s, Pos cur_match) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.longest_match(s, cur_match);
 }
 
 static uint32_t longest_match_slow_stub(deflate_state* const s, Pos cur_match) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     return functable.longest_match_slow(s, cur_match);
 }
 
 static void slide_hash_stub(deflate_state* s) {
-    init_functable();
+    FUNCTABLE_INIT_ABORT;
     functable.slide_hash(s);
 }
 
index 7e3b603686a3b1c18c11ad9311439730b42399c9..91308e56865d3b10f289efbfe09dee47617f12a5 100644 (file)
@@ -24,7 +24,7 @@
 #else
 
 struct functable_s {
-    void     (* force_init)         (void);
+    int      (* force_init)         (void);
     uint32_t (* adler32)            (uint32_t adler, const uint8_t *buf, size_t len);
     uint32_t (* adler32_fold_copy)  (uint32_t adler, uint8_t *dst, const uint8_t *src, size_t len);
     uint8_t* (* chunkmemset_safe)   (uint8_t *out, uint8_t *from, unsigned len, unsigned left);
@@ -45,7 +45,7 @@ Z_INTERNAL extern struct functable_s functable;
 
 /* Explicitly indicate functions are conditionally dispatched.
  */
-#  define FUNCTABLE_INIT functable.force_init()
+#  define FUNCTABLE_INIT if (functable.force_init()) {return Z_VERSION_ERROR;}
 #  define FUNCTABLE_CALL(name) functable.name
 #  define FUNCTABLE_FPTR(name) functable.name
 
index 0a61c1578de0b8fe4e2c95a23c7f9e554ee914fd..3770c21cf1344d63b50e6cb1da6a5716dc1ed0cc 100644 (file)
--- a/zbuild.h
+++ b/zbuild.h
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdio.h>
 
 /* Determine compiler version of C Standard */
 #ifdef __STDC_VERSION__
 
 /* Diagnostic functions */
 #ifdef ZLIB_DEBUG
-#  include <stdio.h>
    extern int Z_INTERNAL z_verbose;
    extern void Z_INTERNAL z_error(const char *m);
 #  define Assert(cond, msg) {int _cond = (cond); if (!_cond) z_error(msg);}