From d32704891e70ed3812503b5b6b10954dac102e1c Mon Sep 17 00:00:00 2001 From: "Steffen \"Daode\" Nurpmeso" Date: Wed, 26 Sep 2012 14:10:45 +0200 Subject: [PATCH] SECURITY FIX: lzw.c: fix handling of corrupt compress(1)ed data.. Update lzw.c to the current FreeBSD version, which includes at least the following security fix from git://git.freebsd.org/freebsd:master:usr.bin/compress/zopen.c: commit 2902cb5e28a1e38bce859ef1ae14e9d22fe50214 Author: bz Date: 2011-09-28 08:47:17 +0000 Fix handling of corrupt compress(1)ed data. [11:04] Add missing length checks on unix socket addresses. [11:05] Approved by: so (cperciva) Approved by: re (kensmith) Security: FreeBSD-SA-11:04.compress Security: CVE-2011-2895 [11:04] Security: FreeBSD-SA-11:05.unix For easier future diff(1)ing with FreeBSDs zopen.c the file was also updated in respect to typenames and style. Lead into correct direction by John Dodson (johnd AT physiol DOT usyd DOT edu DOT au). --- lzw.c | 112 +++++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/lzw.c b/lzw.c index 4a9f032c..74098902 100644 --- a/lzw.c +++ b/lzw.c @@ -2,6 +2,7 @@ * Heirloom mailx - a mail user agent derived from Berkeley Mail. * * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany. + * Copyright (c) 2012 Steffen "Daode" Nurpmeso. */ /*- * Copyright (c) 1985, 1986, 1992, 1993 @@ -19,10 +20,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. @@ -43,6 +40,10 @@ /* from zopen.c 8.1 (Berkeley) 6/27/93 */ /* from FreeBSD: /repoman/r/ncvs/src/usr.bin/compress/zopen.c,v * 1.5.6.1 2002/07/16 00:52:08 tjr Exp */ +/* from FreeBSD: git://git.freebsd.org/freebsd, + * master:usr.bin/compress/zopen.c, + * (Fix handling of corrupt compress(1)ed data. [11:04], 2011-09-28), + * 2902cb5e28a1e38bce859ef1ae14e9d22fe50214. */ /*- * lzw.c - File compression ala IEEE Computer, June 1984. @@ -67,18 +68,27 @@ #include "rcv.h" #include "extern.h" + #include +/* Minimize differences to FreeBSDs usr.bin/compress/zopen.c */ +#undef u_int +#define u_int unsigned int +#undef u_short +#define u_short unsigned short +#undef u_char +#define u_char unsigned char + #define BITS 16 /* Default bits. */ #define HSIZE 69001 /* 95% occupancy */ /* A code_int must be able to hold 2**BITS values of type int, and also -1. */ -typedef long code_int; -typedef long count_int; +typedef long code_int; +typedef long count_int; -typedef unsigned char char_type; -static char_type magic_header[] = - {037, 0235}; /* 1F 9D */ +typedef u_char char_type; +static char_type magic_header[] = + {'\037', '\235'}; /* 1F 9D */ #define BIT_MASK 0x1f /* Defines for third byte of header. */ #define BLOCK_MASK 0x80 @@ -95,14 +105,14 @@ struct s_zstate { FILE *zs_fp; /* File stream for I/O */ char zs_mode; /* r or w */ enum { - ST_START, ST_MIDDLE, ST_EOF + S_START, S_MIDDLE, S_EOF } zs_state; /* State of computation */ - unsigned zs_n_bits; /* Number of bits/code. */ - unsigned zs_maxbits; /* User settable max # bits/code. */ + u_int zs_n_bits; /* Number of bits/code. */ + u_int zs_maxbits; /* User settable max # bits/code. */ code_int zs_maxcode; /* Maximum code, given n_bits. */ code_int zs_maxmaxcode; /* Should NEVER generate this code. */ - count_int zs_htab[HSIZE]; - unsigned short zs_codetab[HSIZE]; + count_int zs_htab [HSIZE]; + u_short zs_codetab [HSIZE]; code_int zs_hsize; /* For dynamic table sizing. */ code_int zs_free_ent; /* First unused entry. */ /* @@ -113,24 +123,24 @@ struct s_zstate { int zs_clear_flg; long zs_ratio; count_int zs_checkpoint; - unsigned zs_offset; + u_int zs_offset; long zs_in_count; /* Length of input. */ long zs_bytes_out; /* Length of compressed output. */ long zs_out_count; /* # of codes output (for debugging). */ - char_type zs_buf[BITS+1]; + char_type zs_buf[BITS]; union { struct { long zs_fcode; code_int zs_ent; code_int zs_hsize_reg; int zs_hshift; - } w; /* Write paramenters */ + } w; /* Write parameters */ struct { char_type *zs_stackp; int zs_finchar; code_int zs_code, zs_oldcode, zs_incode; int zs_roffset, zs_size; - char_type zs_gbuf[BITS+1]; + char_type zs_gbuf[BITS]; } r; /* Read parameters */ } u; }; @@ -231,8 +241,8 @@ zwrite(void *cookie, const char *wbp, int num) code_int i; int c, disp; struct s_zstate *zs; - const unsigned char *bp; - unsigned char tmp; + const u_char *bp; + u_char tmp; int count; if (num == 0) @@ -241,16 +251,16 @@ zwrite(void *cookie, const char *wbp, int num) zs = cookie; zmode = 'w'; count = num; - bp = (const unsigned char *)wbp; - if (state == ST_MIDDLE) + bp = (const u_char *)wbp; + if (state == S_MIDDLE) goto middle; - state = ST_MIDDLE; + state = S_MIDDLE; maxmaxcode = 1L << maxbits; if (fwrite(magic_header, sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header)) return (-1); - tmp = (unsigned char)((maxbits) | block_compress); + tmp = (u_char)((maxbits) | block_compress); if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp)) return (-1); @@ -359,7 +369,7 @@ static int output(struct s_zstate *zs, code_int ocode) { int r_off; - unsigned bits; + u_int bits; char_type *bp; r_off = offset; @@ -446,23 +456,23 @@ output(struct s_zstate *zs, code_int ocode) int zread(void *cookie, char *rbp, int num) { - unsigned count; + u_int count; struct s_zstate *zs; - unsigned char *bp, header[3]; + u_char *bp, header[3]; if (num == 0) return (0); zs = cookie; count = num; - bp = (unsigned char *)rbp; + bp = (u_char *)rbp; switch (state) { - case ST_START: - state = ST_MIDDLE; + case S_START: + state = S_MIDDLE; break; - case ST_MIDDLE: + case S_MIDDLE: goto middle; - case ST_EOF: + case S_EOF: goto eof; } @@ -476,7 +486,7 @@ zread(void *cookie, char *rbp, int num) block_compress = maxbits & BLOCK_MASK; maxbits &= BIT_MASK; maxmaxcode = 1L << maxbits; - if (maxbits > BITS) { + if (maxbits > BITS || maxbits < 12) { return (-1); } /* As above, initialize the first 256 entries in the table. */ @@ -492,7 +502,7 @@ zread(void *cookie, char *rbp, int num) return (0); /* Get out of here */ /* First code must be 8 bits = char. */ - *bp++ = (unsigned char)finchar; + *bp++ = (u_char)finchar; count--; stackp = de_stack; @@ -502,17 +512,26 @@ zread(void *cookie, char *rbp, int num) for (code = 255; code >= 0; code--) tab_prefixof(code) = 0; clear_flg = 1; - free_ent = FIRST - 1; - if ((code = getcode(zs)) == -1) /* O, untimely death! */ - break; + free_ent = FIRST; + oldcode = -1; + continue; } incode = code; - /* Special case for KwKwK string. */ + /* Special case for kWkWk string. */ if (code >= free_ent) { + if (code > free_ent || oldcode == -1) { + return (-1); + } *stackp++ = finchar; code = oldcode; } + /* + * The above condition ensures that code < free_ent. + * The construction of tab_prefixof in turn guarantees that + * each iteration decreases code and therefore stack usage is + * bound by 1 << BITS - 256. + */ /* Generate output characters in reverse order. */ while (code >= 256) { @@ -529,8 +548,8 @@ middle: do { } while (stackp > de_stack); /* Generate the new entry. */ - if ((code = free_ent) < maxmaxcode) { - tab_prefixof(code) = (unsigned short) oldcode; + if ((code = free_ent) < maxmaxcode && oldcode != -1) { + tab_prefixof(code) = (u_short) oldcode; tab_suffixof(code) = finchar; free_ent = code + 1; } @@ -538,7 +557,7 @@ middle: do { /* Remember previous code. */ oldcode = incode; } - state = ST_EOF; + state = S_EOF; eof: return (num - count); } @@ -608,9 +627,7 @@ getcode(struct s_zstate *zs) } static int -cl_block ( /* Table clear for block compress. */ - struct s_zstate *zs -) +cl_block(struct s_zstate *zs) /* Table clear for block compress. */ { long rat; @@ -638,10 +655,7 @@ cl_block ( /* Table clear for block compress. */ } static void -cl_hash ( /* Reset code table. */ - struct s_zstate *zs, - count_int cl_hsize -) +cl_hash(struct s_zstate *zs, count_int cl_hsize) /* Reset code table. */ { count_int *htab_p; long i, m1; @@ -690,7 +704,7 @@ zalloc(FILE *fp) checkpoint = CHECK_GAP; in_count = 1; /* Length of input. */ out_count = 0; /* # of codes output (for debugging). */ - state = ST_START; + state = S_START; roffset = 0; size = 0; zs->zs_fp = fp; -- 2.11.4.GIT