aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Adler <git@madler.net>2026-01-03 01:07:40 -0600
committerMark Adler <git@madler.net>2026-01-05 15:03:04 -0600
commitfd366384cf324d750596feb03be44ddf4d1e6acd (patch)
tree0c1d8eaa0538f8681ae0ce7a4e02ba71ff07d4fd
parentcab7352dc71048f130a7d4e0b7fd773909761133 (diff)
downloadzlib-fd366384cf324d750596feb03be44ddf4d1e6acd.tar.gz
zlib-fd366384cf324d750596feb03be44ddf4d1e6acd.tar.bz2
zlib-fd366384cf324d750596feb03be44ddf4d1e6acd.zip
Prevent the use of insecure functions without an explicit request.
ZLIB_INSECURE must be defined in order to compile code that uses the insecure functions vsprintf() or sprintf(). This would occur only if the standard vsnprintf() or snprintf() functions are not available. Providing the --insecure option to ./configure will define ZLIB_INSECURE. A flag is added to zlibCompileFlags() to indicate that gzprintf() is not implemented due to the need for the use of an insecure function, but ZLIB_INSECURE was not defined.
-rw-r--r--FAQ18
-rwxr-xr-xconfigure17
-rw-r--r--gzwrite.c28
-rw-r--r--zlib.h26
-rw-r--r--zutil.c48
5 files changed, 95 insertions, 42 deletions
diff --git a/FAQ b/FAQ
index df12668..b6b11bd 100644
--- a/FAQ
+++ b/FAQ
@@ -258,15 +258,15 @@ The latest zlib FAQ is at http://zlib.net/zlib_faq.html
25833. Does zlib have any security vulnerabilities? 25833. Does zlib have any security vulnerabilities?
259 259
260 The only one that we are aware of is potentially in gzprintf(). If zlib is 260 The only one that we are aware of is potentially in gzprintf(). If zlib is
261 compiled to use sprintf() or vsprintf(), then there is no protection 261 compiled to use sprintf() or vsprintf(), which requires that ZLIB_INSECURE
262 against a buffer overflow of an 8K string space (or other value as set by 262 be defined, then there is no protection against a buffer overflow of an 8K
263 gzbuffer()), other than the caller of gzprintf() assuring that the output 263 string space (or other value as set by gzbuffer()), other than the caller
264 will not exceed 8K. On the other hand, if zlib is compiled to use 264 of gzprintf() assuring that the output will not exceed 8K. On the other
265 snprintf() or vsnprintf(), which should normally be the case, then there is 265 hand, if zlib is compiled to use snprintf() or vsnprintf(), which should
266 no vulnerability. The ./configure script will display warnings if an 266 normally be the case, then there is no vulnerability. The ./configure
267 insecure variation of sprintf() will be used by gzprintf(). Also the 267 script will display warnings if an insecure variation of sprintf() will be
268 zlibCompileFlags() function will return information on what variant of 268 used by gzprintf(). Also the zlibCompileFlags() function will return
269 sprintf() is used by gzprintf(). 269 information on what variant of sprintf() is used by gzprintf().
270 270
271 If you don't have snprintf() or vsnprintf() and would like one, you can 271 If you don't have snprintf() or vsnprintf() and would like one, you can
272 find a good portable implementation in stb_sprintf.h here: 272 find a good portable implementation in stb_sprintf.h here:
diff --git a/configure b/configure
index 1d3d660..872c50a 100755
--- a/configure
+++ b/configure
@@ -92,6 +92,7 @@ warn=0
92debug=0 92debug=0
93address=0 93address=0
94memory=0 94memory=0
95insecure=0
95unknown=0 96unknown=0
96old_cc="$CC" 97old_cc="$CC"
97old_cflags="$CFLAGS" 98old_cflags="$CFLAGS"
@@ -118,7 +119,7 @@ case "$1" in
118 -h* | --help) 119 -h* | --help)
119 echo 'usage:' | tee -a configure.log 120 echo 'usage:' | tee -a configure.log
120 echo ' configure [--const] [--zprefix] [--prefix=PREFIX] [--eprefix=EXPREFIX]' | tee -a configure.log 121 echo ' configure [--const] [--zprefix] [--prefix=PREFIX] [--eprefix=EXPREFIX]' | tee -a configure.log
121 echo ' [--static] [--64] [--libdir=LIBDIR] [--sharedlibdir=LIBDIR]' | tee -a configure.log 122 echo ' [--insecure] [--static] [--64] [--libdir=LIBDIR] [--sharedlibdir=LIBDIR]' | tee -a configure.log
122 echo ' [--includedir=INCLUDEDIR] [--archs="-arch i386 -arch x86_64"]' | tee -a configure.log 123 echo ' [--includedir=INCLUDEDIR] [--archs="-arch i386 -arch x86_64"]' | tee -a configure.log
123 exit 0 ;; 124 exit 0 ;;
124 -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;; 125 -p*=* | --prefix=*) prefix=`echo $1 | sed 's/.*=//'`; shift ;;
@@ -146,6 +147,7 @@ case "$1" in
146 --sanitize) address=1; shift ;; 147 --sanitize) address=1; shift ;;
147 --address) address=1; shift ;; 148 --address) address=1; shift ;;
148 --memory) memory=1; shift ;; 149 --memory) memory=1; shift ;;
150 --insecure) insecure=1; shift ;;
149 *) unknown=1; echo "unknown option ignored: $1" | tee -a configure.log; shift;; 151 *) unknown=1; echo "unknown option ignored: $1" | tee -a configure.log; shift;;
150 esac 152 esac
151done 153done
@@ -256,6 +258,9 @@ if test "$gcc" -eq 1 && ($cc -c $test.c) >> configure.log 2>&1; then
256 if test $memory -eq 1; then 258 if test $memory -eq 1; then
257 CFLAGS="${CFLAGS} -g -fsanitize=memory -fno-omit-frame-pointer" 259 CFLAGS="${CFLAGS} -g -fsanitize=memory -fno-omit-frame-pointer"
258 fi 260 fi
261 if test $insecure -eq 1; then
262 CFLAGS="${CFLAGS} -DZLIB_INSECURE"
263 fi
259 if test $debug -eq 1; then 264 if test $debug -eq 1; then
260 CFLAGS="${CFLAGS} -DZLIB_DEBUG" 265 CFLAGS="${CFLAGS} -DZLIB_DEBUG"
261 SFLAGS="${SFLAGS} -DZLIB_DEBUG" 266 SFLAGS="${SFLAGS} -DZLIB_DEBUG"
@@ -740,7 +745,10 @@ EOF
740 echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" | tee -a configure.log 745 echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" | tee -a configure.log
741 echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log 746 echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log
742 echo " vulnerabilities." | tee -a configure.log 747 echo " vulnerabilities." | tee -a configure.log
743 748 if test $insecure -ne 1; then
749 echo " The --insecure option must be provided to ./configure in order to" | tee -a configure.log
750 echo " compile using the insecure vsprintf() function." | tee -a configure.log
751 fi
744 echo >> configure.log 752 echo >> configure.log
745 cat >$test.c <<EOF 753 cat >$test.c <<EOF
746#include <stdio.h> 754#include <stdio.h>
@@ -824,7 +832,10 @@ EOF
824 echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" | tee -a configure.log 832 echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" | tee -a configure.log
825 echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log 833 echo " can build but will be open to possible buffer-overflow security" | tee -a configure.log
826 echo " vulnerabilities." | tee -a configure.log 834 echo " vulnerabilities." | tee -a configure.log
827 835 if test $insecure -ne 1; then
836 echo " The --insecure option must be provided to ./configure in order to" | tee -a configure.log
837 echo " compile using the insecure sprintf() function." | tee -a configure.log
838 fi
828 echo >> configure.log 839 echo >> configure.log
829 cat >$test.c <<EOF 840 cat >$test.c <<EOF
830#include <stdio.h> 841#include <stdio.h>
diff --git a/gzwrite.c b/gzwrite.c
index 9348473..acea74f 100644
--- a/gzwrite.c
+++ b/gzwrite.c
@@ -371,6 +371,9 @@ int ZEXPORT gzputs(gzFile file, const char *s) {
371 return len && put == 0 ? -1 : (int)put; 371 return len && put == 0 ? -1 : (int)put;
372} 372}
373 373
374#if (((!defined(STDC) && !defined(Z_HAVE_STDARG_H)) || !defined(NO_vsnprintf)) && \
375 (defined(STDC) || defined(Z_HAVE_STDARG_H) || !defined(NO_snprintf))) || \
376 defined(ZLIB_INSECURE)
374/* If the second half of the input buffer is occupied, write out the contents. 377/* If the second half of the input buffer is occupied, write out the contents.
375 If there is any input remaining due to a non-blocking stall on write, move 378 If there is any input remaining due to a non-blocking stall on write, move
376 it to the start of the buffer. Return true if this did not open up the 379 it to the start of the buffer. Return true if this did not open up the
@@ -391,12 +394,20 @@ local int gz_vacate(gz_statep state) {
391 strm->next_in = state->in; 394 strm->next_in = state->in;
392 return strm->avail_in > state->size; 395 return strm->avail_in > state->size;
393} 396}
397#endif
394 398
395#if defined(STDC) || defined(Z_HAVE_STDARG_H) 399#if defined(STDC) || defined(Z_HAVE_STDARG_H)
396#include <stdarg.h> 400#include <stdarg.h>
397 401
398/* -- see zlib.h -- */ 402/* -- see zlib.h -- */
399int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { 403int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
404#if defined(NO_vsnprintf) && !defined(ZLIB_INSECURE)
405#warning "vsnprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
406#warning "you can recompile with ZLIB_INSECURE defined to use vsprintf()"
407 /* prevent use of insecure vsprintf(), unless purposefully requested */
408 (void)file, (void)format, (void)va;
409 return Z_STREAM_ERROR;
410#else
400 int len, ret; 411 int len, ret;
401 char *next; 412 char *next;
402 gz_statep state; 413 gz_statep state;
@@ -470,6 +481,7 @@ int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) {
470 if (state->err && !state->again) 481 if (state->err && !state->again)
471 return state->err; 482 return state->err;
472 return len; 483 return len;
484#endif
473} 485}
474 486
475int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { 487int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) {
@@ -489,6 +501,17 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
489 int a4, int a5, int a6, int a7, int a8, int a9, int a10, 501 int a4, int a5, int a6, int a7, int a8, int a9, int a10,
490 int a11, int a12, int a13, int a14, int a15, int a16, 502 int a11, int a12, int a13, int a14, int a15, int a16,
491 int a17, int a18, int a19, int a20) { 503 int a17, int a18, int a19, int a20) {
504#if defined(NO_snprintf) && !defined(ZLIB_INSECURE)
505#warning "snprintf() not available -- gzprintf() stub returns Z_STREAM_ERROR"
506#warning "you can recompile with ZLIB_INSECURE defined to use sprintf()"
507 /* prevent use of insecure sprintf(), unless purposefully requested */
508 (void)file, (void)format, (void)a1, (void)a2, (void)a3, (void)a4, (void)a5,
509 (void)a6, (void)a7, (void)a8, (void)a9, (void)a10, (void)a11, (void)a12,
510 (void)a13, (void)a14, (void)a15, (void)a16, (void)a17, (void)a18,
511 (void)a19, (void)a20;
512 return Z_STREAM_ERROR;
513#else
514 int ret;
492 unsigned len, left; 515 unsigned len, left;
493 char *next; 516 char *next;
494 gz_statep state; 517 gz_statep state;
@@ -511,11 +534,11 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
511 534
512 /* make sure we have some buffer space */ 535 /* make sure we have some buffer space */
513 if (state->size == 0 && gz_init(state) == -1) 536 if (state->size == 0 && gz_init(state) == -1)
514 return state->error; 537 return state->err;
515 538
516 /* check for seek request */ 539 /* check for seek request */
517 if (state->skip && gz_zero(state) == -1) 540 if (state->skip && gz_zero(state) == -1)
518 return state->error; 541 return state->err;
519 542
520 /* do the printf() into the input buffer, put length in len -- the input 543 /* do the printf() into the input buffer, put length in len -- the input
521 buffer is double-sized just for this function, so there is guaranteed to 544 buffer is double-sized just for this function, so there is guaranteed to
@@ -571,6 +594,7 @@ int ZEXPORTVA gzprintf(gzFile file, const char *format, int a1, int a2, int a3,
571 if (state->err && !state->again) 594 if (state->err && !state->again)
572 return state->err; 595 return state->err;
573 return (int)len; 596 return (int)len;
597#endif
574} 598}
575 599
576#endif 600#endif
diff --git a/zlib.h b/zlib.h
index 2881da7..595e3cf 100644
--- a/zlib.h
+++ b/zlib.h
@@ -1239,13 +1239,14 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags(void);
1239 21: FASTEST -- deflate algorithm with only one, lowest compression level 1239 21: FASTEST -- deflate algorithm with only one, lowest compression level
1240 22,23: 0 (reserved) 1240 22,23: 0 (reserved)
1241 1241
1242 The sprintf variant used by gzprintf (zero is best): 1242 The sprintf variant used by gzprintf (all zeros is best):
1243 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format 1243 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
1244 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! 1244 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() is not secure!
1245 26: 0 = returns value, 1 = void -- 1 means inferred string length returned 1245 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
1246 27: 0 = gzprintf() present, 1 = not -- 1 means gzprintf() returns an error
1246 1247
1247 Remainder: 1248 Remainder:
1248 27-31: 0 (reserved) 1249 28-31: 0 (reserved)
1249 */ 1250 */
1250 1251
1251#ifndef Z_SOLO 1252#ifndef Z_SOLO
@@ -1527,7 +1528,11 @@ ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size,
1527 gzwrite() instead. 1528 gzwrite() instead.
1528*/ 1529*/
1529 1530
1531#if defined(STDC) || defined(Z_HAVE_STDARG_H)
1530ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); 1532ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
1533#else
1534ZEXTERN int ZEXPORTVA gzprintf();
1535#endif
1531/* 1536/*
1532 Convert, format, compress, and write the arguments (...) to file under 1537 Convert, format, compress, and write the arguments (...) to file under
1533 control of the string format, as in fprintf. gzprintf returns the number of 1538 control of the string format, as in fprintf. gzprintf returns the number of
@@ -1535,11 +1540,16 @@ ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
1535 of error. The number of uncompressed bytes written is limited to 8191, or 1540 of error. The number of uncompressed bytes written is limited to 8191, or
1536 one less than the buffer size given to gzbuffer(). The caller should assure 1541 one less than the buffer size given to gzbuffer(). The caller should assure
1537 that this limit is not exceeded. If it is exceeded, then gzprintf() will 1542 that this limit is not exceeded. If it is exceeded, then gzprintf() will
1538 return an error (0) with nothing written. In this case, there may also be a 1543 return an error (0) with nothing written.
1539 buffer overflow with unpredictable consequences, which is possible only if 1544
1540 zlib was compiled with the insecure functions sprintf() or vsprintf(), 1545 In that last case, there may also be a buffer overflow with unpredictable
1541 because the secure snprintf() or vsnprintf() functions were not available. 1546 consequences, which is possible only if zlib was compiled with the insecure
1542 This can be determined using zlibCompileFlags(). 1547 functions sprintf() or vsprintf(), because the secure snprintf() and
1548 vsnprintf() functions were not available. That would only be the case for
1549 a non-ANSI C compiler. zlib may have been built without gzprintf() because
1550 secure functions were not available and having gzprintf() be insecure was
1551 not an option, in which case, gzprintf() returns Z_STREAM_ERROR. All of
1552 these possibilities can be determined using zlibCompileFlags().
1543 1553
1544 If a Z_BUF_ERROR is returned, then nothing was written due to a stall on 1554 If a Z_BUF_ERROR is returned, then nothing was written due to a stall on
1545 the non-blocking write destination. 1555 the non-blocking write destination.
diff --git a/zutil.c b/zutil.c
index b1c5d2d..6e8a369 100644
--- a/zutil.c
+++ b/zutil.c
@@ -86,28 +86,36 @@ uLong ZEXPORT zlibCompileFlags(void) {
86 flags += 1L << 21; 86 flags += 1L << 21;
87#endif 87#endif
88#if defined(STDC) || defined(Z_HAVE_STDARG_H) 88#if defined(STDC) || defined(Z_HAVE_STDARG_H)
89# ifdef NO_vsnprintf 89# ifdef NO_vsnprintf
90 flags += 1L << 25; 90# ifdef ZLIB_INSECURE
91# ifdef HAS_vsprintf_void 91 flags += 1L << 25;
92 flags += 1L << 26; 92# else
93# endif 93 flags += 1L << 27;
94# else 94# endif
95# ifdef HAS_vsnprintf_void 95# ifdef HAS_vsprintf_void
96 flags += 1L << 26; 96 flags += 1L << 26;
97# endif 97# endif
98# endif 98# else
99# ifdef HAS_vsnprintf_void
100 flags += 1L << 26;
101# endif
102# endif
99#else 103#else
100 flags += 1L << 24; 104 flags += 1L << 24;
101# ifdef NO_snprintf 105# ifdef NO_snprintf
102 flags += 1L << 25; 106# ifdef ZLIB_INSECURE
103# ifdef HAS_sprintf_void 107 flags += 1L << 25;
104 flags += 1L << 26; 108# else
105# endif 109 flags += 1L << 27;
106# else 110# endif
107# ifdef HAS_snprintf_void 111# ifdef HAS_sprintf_void
108 flags += 1L << 26; 112 flags += 1L << 26;
109# endif 113# endif
110# endif 114# else
115# ifdef HAS_snprintf_void
116 flags += 1L << 26;
117# endif
118# endif
111#endif 119#endif
112 return flags; 120 return flags;
113} 121}