this patch reduces the ~2500+ worst-case stack footprint of zlib to ~500 bytes, by making the largest arrays static and by introducing a spinlock to protect access to them. Signed-off-by: Ingo Molnar arch/arm/boot/compressed/misc.c | 1 arch/arm26/boot/compressed/misc.c | 1 arch/i386/boot/compressed/misc.c | 1 arch/x86_64/boot/compressed/misc.c | 1 lib/inflate.c | 44 +++++++++++++++++++++++++++--------- lib/zlib_inflate/inftrees.c | 45 ++++++++++++++++++++++++++++--------- 6 files changed, 72 insertions(+), 21 deletions(-) Index: linux/arch/arm/boot/compressed/misc.c =================================================================== --- linux.orig/arch/arm/boot/compressed/misc.c +++ linux/arch/arm/boot/compressed/misc.c @@ -199,6 +199,7 @@ static ulg free_mem_ptr_end; #define HEAP_SIZE 0x2000 +#define ZLIB_INFLATE_NO_INFLATE_LOCK #include "../../../../lib/inflate.c" #ifndef STANDALONE_DEBUG Index: linux/arch/arm26/boot/compressed/misc.c =================================================================== --- linux.orig/arch/arm26/boot/compressed/misc.c +++ linux/arch/arm26/boot/compressed/misc.c @@ -184,6 +184,7 @@ static ulg free_mem_ptr_end; #define HEAP_SIZE 0x2000 +#define ZLIB_INFLATE_NO_INFLATE_LOCK #include "../../../../lib/inflate.c" #ifndef STANDALONE_DEBUG Index: linux/arch/i386/boot/compressed/misc.c =================================================================== --- linux.orig/arch/i386/boot/compressed/misc.c +++ linux/arch/i386/boot/compressed/misc.c @@ -125,6 +125,7 @@ static int lines, cols; static void * xquad_portio = NULL; #endif +#define ZLIB_INFLATE_NO_INFLATE_LOCK #include "../../../../lib/inflate.c" static void *malloc(int size) Index: linux/arch/x86_64/boot/compressed/misc.c =================================================================== --- linux.orig/arch/x86_64/boot/compressed/misc.c +++ linux/arch/x86_64/boot/compressed/misc.c @@ -114,6 +114,7 @@ static char *vidmem = (char *)0xb8000; static int vidport; static int lines, cols; +#define ZLIB_INFLATE_NO_INFLATE_LOCK #include "../../../../lib/inflate.c" static void *malloc(int size) Index: linux/lib/inflate.c =================================================================== --- linux.orig/lib/inflate.c +++ linux/lib/inflate.c @@ -141,6 +141,25 @@ struct huft { } v; }; +/* + * turn off the inflate_lock for the bootloader code, it is + * single-threaded and has no need for (nor access to) the + * kernel's locking primitives: + */ +#ifdef ZLIB_INFLATE_NO_INFLATE_LOCK +# undef DEFINE_SPINLOCK +# undef spin_lock +# undef spin_unlock +# define DEFINE_SPINLOCK(x) int x +# define spin_lock(x) (void)(x) +# define spin_unlock(x) (void)(x) +#endif + +/* + * lock protecting static variables of huft_build() and other inflate + * functions, to reduce their insane stack footprint. + */ +static DEFINE_SPINLOCK(inflate_lock); /* Function prototypes */ STATIC int INIT huft_build OF((unsigned *, unsigned, unsigned, @@ -304,7 +323,7 @@ STATIC int INIT huft_build( register struct huft *q; /* points to current table */ struct huft r; /* table entry for structure assignment */ struct huft *u[BMAX]; /* table stack */ - unsigned v[N_MAX]; /* values in order of bit length */ + static unsigned v[N_MAX]; /* values in order of bit length */ register int w; /* bits before this table == (l * h) */ unsigned x[BMAX+1]; /* bit offsets, then code stack */ unsigned *xp; /* pointer into x */ @@ -705,7 +724,7 @@ STATIC int noinline INIT inflate_fixed(v struct huft *td; /* distance code table */ int bl; /* lookup bits for tl */ int bd; /* lookup bits for td */ - unsigned l[288]; /* length list for huft_build */ + static unsigned l[288]; /* length list for huft_build */ DEBG(""); /* bad block type */ - return 2; + return ret; underrun: return 4; /* Input underrun */ Index: linux/lib/zlib_inflate/inftrees.c =================================================================== --- linux.orig/lib/zlib_inflate/inftrees.c +++ linux/lib/zlib_inflate/inftrees.c @@ -4,11 +4,19 @@ */ #include +#include #include "inftrees.h" #include "infutil.h" static const char inflate_copyright[] __attribute_used__ = " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; + +/* + * lock protecting static variables of huft_build() and other inflate + * functions, to reduce their insane stack footprint. + */ +static DEFINE_SPINLOCK(inflate_lock); + /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -107,7 +115,7 @@ static int huft_build( { uInt a; /* counter for codes of length k */ - uInt c[BMAX+1]; /* bit length count table */ + static uInt c[BMAX+1]; /* bit length count table */ uInt f; /* i repeats in table every f entries */ int g; /* maximum code length */ int h; /* table level */ @@ -118,10 +126,10 @@ static int huft_build( uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ register uInt *p; /* pointer into c[], b[], or v[] */ inflate_huft *q; /* points to current table */ - struct inflate_huft_s r; /* table entry for structure assignment */ - inflate_huft *u[BMAX]; /* table stack */ + static struct inflate_huft_s r; /* table entry for structure assignment */ + static inflate_huft *u[BMAX]; /* table stack */ register int w; /* bits before this table == (l * h) */ - uInt x[BMAX+1]; /* bit offsets, then code stack */ + static uInt x[BMAX+1]; /* bit offsets, then code stack */ uInt *xp; /* pointer into x */ int y; /* number of dummy codes added */ uInt z; /* number of entries in current table */ @@ -300,9 +308,13 @@ int zlib_inflate_trees_bits( int r; uInt hn = 0; /* hufts used in space */ uInt *v; /* work area for huft_build */ - + v = WS(z)->tree_work_area_1; + + spin_lock(&inflate_lock); r = huft_build(c, 19, 19, NULL, NULL, tb, bb, hp, &hn, v); + spin_unlock(&inflate_lock); + if (r == Z_DATA_ERROR) z->msg = (char*)"oversubscribed dynamic bit lengths tree"; else if (r == Z_BUF_ERROR || *bb == 0) @@ -333,7 +345,10 @@ int zlib_inflate_trees_dynamic( v = WS(z)->tree_work_area_2; /* build literal/length tree */ + spin_lock(&inflate_lock); r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + spin_unlock(&inflate_lock); + if (r != Z_OK || *bl == 0) { if (r == Z_DATA_ERROR) @@ -347,7 +362,10 @@ int zlib_inflate_trees_dynamic( } /* build distance tree */ + spin_lock(&inflate_lock); r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + spin_unlock(&inflate_lock); + if (r != Z_OK || (*bd == 0 && nl > 257)) { if (r == Z_DATA_ERROR) @@ -383,9 +401,11 @@ int zlib_inflate_trees_fixed( z_streamp z /* for memory allocation */ ) { - int i; /* temporary variable */ - unsigned l[288]; /* length list for huft_build */ - uInt *v; /* work area for huft_build */ + int i; /* temporary variable */ + static unsigned l[288]; /* length list for huft_build */ + uInt *v; /* work area for huft_build */ + + spin_lock(&inflate_lock); /* set up literal table */ for (i = 0; i < 144; i++) @@ -398,15 +418,20 @@ int zlib_inflate_trees_fixed( l[i] = 8; *bl = 9; v = WS(z)->tree_work_area_1; - if ((i = huft_build(l, 288, 257, cplens, cplext, tl, bl, hp, &i, v)) != 0) + if ((i = huft_build(l, 288, 257, cplens, cplext, tl, bl, hp, &i, v)) != 0) { + spin_unlock(&inflate_lock); return i; + } /* set up distance table */ for (i = 0; i < 30; i++) /* make an incomplete code set */ l[i] = 5; *bd = 5; - if ((i = huft_build(l, 30, 0, cpdist, cpdext, td, bd, hp, &i, v)) > 1) + if ((i = huft_build(l, 30, 0, cpdist, cpdext, td, bd, hp, &i, v)) > 1) { + spin_unlock(&inflate_lock); return i; + } + spin_unlock(&inflate_lock); return Z_OK; }