From 3e2f953a10cafa1b037d0172f81103c9f1a5f433 Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 22 May 2007 13:10:59 +0000 Subject: [PATCH] real fix for bug #19910. the .Z format is really badly designed :-( --- ChangeLog | 6 +- src/lzw/ftzopen.c | 185 +++++++++++++++++++++++----------------------- src/lzw/ftzopen.h | 14 ++-- 3 files changed, 100 insertions(+), 105 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3f044a891..dd8c99ef4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,8 @@ 2007-05-22 David Turner - * src/lzw/ftzopen.h, src/lzw/ftzopen.c: apply some "band-aid" - to avoid blowing up the heap in the case of malformed fonts. - related to bug #19910; *not* a real fix though... + * src/lzw/ftzopen.h, src/lzw/ftzopen.c: fix for bug #19910 + (heap blowup with very large .Z font file). The .Z format is + *really* crappy :-( 2007-05-20 Ismail Dönmez diff --git a/src/lzw/ftzopen.c b/src/lzw/ftzopen.c index f486d1bd2..85e322362 100644 --- a/src/lzw/ftzopen.c +++ b/src/lzw/ftzopen.c @@ -23,59 +23,86 @@ #include FT_INTERNAL_STREAM_H #include FT_INTERNAL_DEBUG_H - /* refill input buffer, return 0 on success, or -1 if eof */ static int ft_lzwstate_refill( FT_LzwState state ) { - int result = -1; + FT_ULong count; + if (state->in_eof) + return -1; - if ( !state->in_eof ) - { - FT_ULong count = FT_Stream_TryRead( state->source, - state->in_buff, - sizeof ( state->in_buff ) ); + count = FT_Stream_TryRead( state->source, + state->buf_tab, + state->num_bits ); /* WHY ?? */ - state->in_cursor = state->in_buff; - state->in_limit = state->in_buff + count; - state->in_eof = FT_BOOL( count < sizeof ( state->in_buff ) ); + state->buf_size = (FT_UInt) count; + state->buf_total += count; + state->in_eof = FT_BOOL( count < state->num_bits ); + state->buf_offset = 0; + state->buf_size = (state->buf_size << 3) - (state->num_bits-1); - if ( count > 0 ) - result = 0; - } - return result; + if (count == 0) /* end of file */ + return -1; + + return 0; } - /* return new code of 'num_bits', or -1 if eof */ static FT_Int32 - ft_lzwstate_get_code( FT_LzwState state, - FT_UInt num_bits ) + ft_lzwstate_get_code( FT_LzwState state ) { - FT_Int32 result = -1; - FT_UInt32 pad = state->pad; - FT_UInt pad_bits = state->pad_bits; + FT_UInt num_bits = state->num_bits; + FT_Int offset = state->buf_offset; + FT_Byte* p; + FT_Int result; - while ( num_bits > pad_bits ) + if ( state->buf_clear || + offset >= state->buf_size || + state->free_ent >= state->free_bits ) { - if ( state->in_cursor >= state->in_limit && - ft_lzwstate_refill( state ) < 0 ) - goto Exit; + if ( state->free_ent >= state->free_bits ) + { + state->num_bits = ++num_bits; + state->free_bits = state->num_bits < state->max_bits + ? (FT_UInt)( ( 1UL << num_bits ) - 256 ) + : state->max_free + 1; + } - pad |= (FT_UInt32)(*state->in_cursor++) << pad_bits; - pad_bits += 8; + if ( state->buf_clear ) + { + state->num_bits = num_bits = LZW_INIT_BITS; + state->free_bits = (FT_UInt)( ( 1UL << num_bits ) - 256 ); + state->buf_clear = 0; + } + + if ( ft_lzwstate_refill( state ) < 0 ) + return -1; + + offset = 0; } - result = (FT_Int32)( pad & LZW_MASK( num_bits ) ); - state->pad_bits = pad_bits - num_bits; - state->pad = pad >> num_bits; + state->buf_offset = offset + num_bits; + + p = &state->buf_tab[offset >> 3]; + offset &= 7; + result = *p++ >> offset; + offset = 8-offset; + num_bits -= offset; + if (num_bits >= 8) + { + result |= *p++ << offset; + offset += 8; + num_bits -= 8; + } + if (num_bits > 0) + result |= (*p & LZW_MASK(num_bits)) << offset; - Exit: return result; } + /* grow the character stack */ static int ft_lzwstate_stack_grow( FT_LzwState state ) @@ -87,11 +114,6 @@ FT_UInt old_size = state->stack_size; FT_UInt new_size = old_size; - /* limit stack size to detect invalid files */ - /* the magic number comes from the origin NetBSD zopen implementation */ - if (state->stack_size >= 69001 ) - return -1; - new_size = new_size + ( new_size >> 1 ) + 4; if ( state->stack == state->stack_0 ) @@ -115,7 +137,6 @@ { FT_UInt old_size = state->prefix_size; FT_UInt new_size = old_size; - FT_UInt max_size = INT_MAX/(sizeof(FT_UShort)+sizeof(FT_Byte)); FT_Memory memory = state->memory; FT_Error error; @@ -125,13 +146,6 @@ else new_size += new_size >> 2; /* don't grow too fast */ - if (new_size < old_size || new_size > max_size) - { - new_size = max_size; - if (new_size == old_size) - return -1; - } - /* * Note that the `suffix' array is located in the same memory block * pointed to by `prefix'. @@ -159,12 +173,11 @@ FT_LOCAL_DEF( void ) ft_lzwstate_reset( FT_LzwState state ) { - state->in_cursor = state->in_buff; - state->in_limit = state->in_buff; state->in_eof = 0; - state->pad_bits = 0; - state->pad = 0; - + state->buf_offset = 0; + state->buf_size = 0; + state->buf_clear = 0; + state->buf_total = 0; state->stack_top = 0; state->num_bits = LZW_INIT_BITS; state->phase = FT_LZW_PHASE_START; @@ -226,11 +239,9 @@ { FT_ULong result = 0; - FT_UInt num_bits = state->num_bits; - FT_UInt free_ent = state->free_ent; - FT_UInt last_char = state->last_char; - FT_UInt old_code = state->old_code; - FT_UInt in_code = state->in_code; + FT_UInt old_char = state->old_char; + FT_UInt old_code = state->old_code; + FT_UInt in_code = state->in_code; if ( out_size == 0 ) @@ -256,27 +267,27 @@ if ( state->max_bits > LZW_MAX_BITS ) goto Eof; - num_bits = LZW_INIT_BITS; - free_ent = ( state->block_mode ? LZW_FIRST : LZW_CLEAR ) - 256; + state->num_bits = LZW_INIT_BITS; + state->free_ent = ( state->block_mode ? LZW_FIRST : LZW_CLEAR ) - 256; in_code = 0; - state->free_bits = num_bits < state->max_bits - ? (FT_UInt)( ( 1UL << num_bits ) - 256 ) + state->free_bits = state->num_bits < state->max_bits + ? (FT_UInt)( ( 1UL << state->num_bits ) - 256 ) : state->max_free + 1; - c = ft_lzwstate_get_code( state, num_bits ); + c = ft_lzwstate_get_code( state ); if ( c < 0 ) goto Eof; - old_code = last_char = (FT_UInt)c; + old_code = old_char = (FT_UInt)c; if ( buffer ) - buffer[result] = (FT_Byte)last_char; - - state->phase = FT_LZW_PHASE_CODE; + buffer[result] = (FT_Byte)old_char; if ( ++result >= out_size ) goto Exit; + + state->phase = FT_LZW_PHASE_CODE; } /* fall-through */ @@ -287,7 +298,7 @@ NextCode: - c = ft_lzwstate_get_code( state, num_bits ); + c = ft_lzwstate_get_code( state ); if ( c < 0 ) goto Eof; @@ -295,14 +306,9 @@ if ( code == LZW_CLEAR && state->block_mode ) { - free_ent = ( LZW_FIRST - 1 ) - 256; /* why not LZW_FIRST-256 ? */ - num_bits = LZW_INIT_BITS; - - state->free_bits = num_bits < state->max_bits - ? (FT_UInt)( ( 1UL << num_bits ) - 256 ) - : state->max_free + 1; - - c = ft_lzwstate_get_code( state, num_bits ); + state->free_ent = ( LZW_FIRST - 1 ) - 256; /* why not LZW_FIRST-256 ? */ + state->buf_clear = 1; + c = ft_lzwstate_get_code( state ); if ( c < 0 ) goto Eof; @@ -314,9 +320,9 @@ if ( code >= 256U ) { /* special case for KwKwKwK */ - if ( code - 256U >= free_ent ) + if ( code - 256U >= state->free_ent ) { - FTLZW_STACK_PUSH( last_char ); + FTLZW_STACK_PUSH( old_char ); code = old_code; } @@ -327,8 +333,8 @@ } } - last_char = code; - FTLZW_STACK_PUSH( last_char ); + old_char = code; + FTLZW_STACK_PUSH( old_char ); state->phase = FT_LZW_PHASE_STACK; } @@ -348,25 +354,18 @@ } /* now create new entry */ - if ( free_ent < state->max_free ) + if ( state->free_ent < state->max_free ) { - if ( free_ent >= state->prefix_size && - ft_lzwstate_prefix_grow( state ) < 0 ) + if ( state->free_ent >= state->prefix_size && + ft_lzwstate_prefix_grow( state ) < 0 ) goto Eof; - FT_ASSERT( free_ent < state->prefix_size ); + FT_ASSERT( state->free_ent < state->prefix_size ); - state->prefix[free_ent] = (FT_UShort)old_code; - state->suffix[free_ent] = (FT_Byte) last_char; + state->prefix[state->free_ent] = (FT_UShort)old_code; + state->suffix[state->free_ent] = (FT_Byte) old_char; - if ( ++free_ent == state->free_bits ) - { - num_bits++; - - state->free_bits = num_bits < state->max_bits - ? (FT_UInt)( ( 1UL << num_bits ) - 256 ) - : state->max_free + 1; - } + state->free_ent += 1; } old_code = in_code; @@ -380,11 +379,9 @@ } Exit: - state->num_bits = num_bits; - state->free_ent = free_ent; - state->old_code = old_code; - state->last_char = last_char; - state->in_code = in_code; + state->old_code = old_code; + state->old_char = old_char; + state->in_code = in_code; return result; diff --git a/src/lzw/ftzopen.h b/src/lzw/ftzopen.h index 220c1cc9c..16a53eee7 100644 --- a/src/lzw/ftzopen.h +++ b/src/lzw/ftzopen.h @@ -112,13 +112,13 @@ typedef struct _FT_LzwStateRec { FT_LzwPhase phase; - FT_Int in_eof; - FT_Byte* in_cursor; /* current buffer pos */ - FT_Byte* in_limit; /* current buffer limit */ - FT_UInt32 pad; /* a pad value where incoming bits were read */ - FT_Int pad_bits; /* number of meaningful bits in pad value */ + FT_Byte buf_tab[16]; + FT_Int buf_offset; + FT_Int buf_size; + FT_Bool buf_clear; + FT_Int buf_total; FT_UInt max_bits; /* max code bits, from file header */ FT_Int block_mode; /* block mode flag, from file header */ @@ -128,7 +128,7 @@ FT_UInt free_ent; /* index of next free entry */ FT_UInt free_bits; /* if reached by free_ent, increment num_bits */ FT_UInt old_code; - FT_UInt last_char; + FT_UInt old_char; FT_UInt in_code; FT_UShort* prefix; /* always dynamically allocated / reallocated */ @@ -138,8 +138,6 @@ FT_Byte* stack; /* character stack */ FT_UInt stack_top; FT_UInt stack_size; - - FT_Byte in_buff[FT_LZW_IN_BUFF_SIZE]; /* small read-buffer */ FT_Byte stack_0[FT_LZW_DEFAULT_STACK_SIZE]; /* minimize heap alloc */ FT_Stream source; /* source stream */