diff options
| author | Mark Adler <git@madler.net> | 2026-01-03 01:07:40 -0600 |
|---|---|---|
| committer | Mark Adler <git@madler.net> | 2026-01-05 15:03:04 -0600 |
| commit | fd366384cf324d750596feb03be44ddf4d1e6acd (patch) | |
| tree | 0c1d8eaa0538f8681ae0ce7a4e02ba71ff07d4fd | |
| parent | cab7352dc71048f130a7d4e0b7fd773909761133 (diff) | |
| download | zlib-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-- | FAQ | 18 | ||||
| -rwxr-xr-x | configure | 17 | ||||
| -rw-r--r-- | gzwrite.c | 28 | ||||
| -rw-r--r-- | zlib.h | 26 | ||||
| -rw-r--r-- | zutil.c | 48 |
5 files changed, 95 insertions, 42 deletions
| @@ -258,15 +258,15 @@ The latest zlib FAQ is at http://zlib.net/zlib_faq.html | |||
| 258 | 33. Does zlib have any security vulnerabilities? | 258 | 33. 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: |
| @@ -92,6 +92,7 @@ warn=0 | |||
| 92 | debug=0 | 92 | debug=0 |
| 93 | address=0 | 93 | address=0 |
| 94 | memory=0 | 94 | memory=0 |
| 95 | insecure=0 | ||
| 95 | unknown=0 | 96 | unknown=0 |
| 96 | old_cc="$CC" | 97 | old_cc="$CC" |
| 97 | old_cflags="$CFLAGS" | 98 | old_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 |
| 151 | done | 153 | done |
| @@ -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> |
| @@ -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 -- */ |
| 399 | int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) { | 403 | int 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 | ||
| 475 | int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) { | 487 | int 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 |
| @@ -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) | ||
| 1530 | ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); | 1532 | ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); |
| 1533 | #else | ||
| 1534 | ZEXTERN 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. |
| @@ -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 | } |
