From fba29fabb3c3892ccd8afc3a9c3988aaeb036dc8 Mon Sep 17 00:00:00 2001 From: Werner Lemberg Date: Tue, 27 Oct 2015 21:04:48 +0100 Subject: [PATCH] [pfr] Add some safety guards (#46302). * src/pfr/pfrload.h (PFR_CHECK): Rename to... (PFR_CHECK_SIZE): ... this. (PFR_SIZE): [!PFR_CONFIG_NO_CHECKS]: Define to PFR_CHECK_SIZE. * src/pfr/pfrload.c (pfr_log_font_count): Check `count'. (pfr_extra_item_load_kerning_pairs): Remove tracing message. (pfr_phy_font_load): Use PFR_CHECK_SIZE where appropriate. Allocate `chars' after doing a size checks. * src/pfr/pfrsbit.c (pfr_load_bitmap_bits): Move test for invalid bitmap format to... (pfr_slot_load_bitmap): ... this function. Check bitmap size. --- ChangeLog | 18 ++++++++ src/pfr/pfrload.c | 109 +++++++++++++++++++++++++++++++++++++++++++--- src/pfr/pfrload.h | 15 ++++--- src/pfr/pfrsbit.c | 50 ++++++++++++++++++++- 4 files changed, 178 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index a19acf07b..8c618ccba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2015-10-27 Werner Lemberg + + [pfr] Add some safety guards (#46302). + + * src/pfr/pfrload.h (PFR_CHECK): Rename to... + (PFR_CHECK_SIZE): ... this. + (PFR_SIZE): [!PFR_CONFIG_NO_CHECKS]: Define to PFR_CHECK_SIZE. + + * src/pfr/pfrload.c (pfr_log_font_count): Check `count'. + (pfr_extra_item_load_kerning_pairs): Remove tracing message. + (pfr_phy_font_load): Use PFR_CHECK_SIZE where appropriate. + Allocate `chars' after doing a size checks. + + * src/pfr/pfrsbit.c (pfr_load_bitmap_bits): Move test for invalid + bitmap format to... + (pfr_slot_load_bitmap): ... this function. + Check bitmap size. + 2015-10-26 Werner Lemberg [truetype] Fix sanitizing logic for `loca' (#46223). diff --git a/src/pfr/pfrload.c b/src/pfr/pfrload.c index a1cae2cc2..ed8327af8 100644 --- a/src/pfr/pfrload.c +++ b/src/pfr/pfrload.c @@ -26,6 +26,93 @@ #define FT_COMPONENT trace_pfr + /* + * The overall structure of a PFR file is as follows. + * + * PFR header + * 58 bytes (contains nPhysFonts) + * + * Logical font directory (size at most 2^16 bytes) + * 2 bytes (nLogFonts) + * + nLogFonts * 5 bytes + * + * ==> nLogFonts <= 13106 + * + * Logical font section (size at most 2^24 bytes) + * nLogFonts * logFontRecord + * + * logFontRecord (size at most 2^16 bytes) + * 12 bytes (fontMatrix) + * + 1 byte (flags) + * + 0-5 bytes (depending on `flags') + * + 0-(1+255*(2+255)) = 0-65536 (depending on `flags') + * + 5 bytes (physical font info) + * + 0-1 bytes (depending on PFR header) + * + * ==> minimum size 18 bytes + * + * Physical font section (size at most 2^24 bytes) + * nPhysFonts * (physFontRecord + * + nBitmapSizes * nBmapChars * bmapCharRecord) + * + * physFontRecord (size at most 2^24 bytes) + * 14 bytes (font info) + * + 1 byte (flags) + * + 0-2 (depending on `flags') + * + 0-? (structure too complicated to be shown here; depending on + * `flags'; contains `nBitmapSizes' and `nBmapChars') + * + 3 bytes (nAuxBytes) + * + nAuxBytes + * + 1 byte (nBlueValues) + * + 2 * nBlueValues + * + 6 bytes (hinting data) + * + 2 bytes (nCharacters) + * + nCharacters * (4-10 bytes) (depending on `flags') + * + * ==> minimum size 27 bytes + * + * bmapCharRecord + * 4-7 bytes + * + * Glyph program strings (three possible types: simpleGps, compoundGps, + * and bitmapGps; size at most 2^24 bytes) + * simpleGps (size at most 2^16 bytes) + * 1 byte (flags) + * 1-2 bytes (n[XY]orus, depending on `flags') + * 0-(64+512*2) = 0-1088 bytes (depending on `n[XY]orus') + * 0-? (structure too complicated to be shown here; depending on + * `flags') + * 1-? glyph data (faintly resembling PS Type 1 charstrings) + * + * ==> minimum size 3 bytes + * + * compoundGps (size at most 2^16 bytes) + * 1 byte (nElements <= 63, flags) + * + 0-(1+255*(2+255)) = 0-65536 (depending on `flags') + * + nElements * (6-14 bytes) + * + * bitmapGps (size at most 2^16 bytes) + * 1 byte (flags) + * 3-13 bytes (position info, depending on `flags') + * 0-? bitmap data + * + * ==> minimum size 4 bytes + * + * PFR trailer + * 8 bytes + * + * + * ==> minimum size of a valid PFR: + * 58 (header) + * + 2 (nLogFonts) + * + 27 (1 physFontRecord) + * + 8 (trailer) + * ----- + * 95 bytes + * + */ + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -212,6 +299,16 @@ FT_READ_USHORT( count ) ) goto Exit; + /* check maximum value and a rough minimum size */ + if ( count > ( ( 1 << 16 ) - 2 ) / 5 || + 2 + count * 5 >= stream->size - section_offset ) + { + FT_ERROR(( "pfr_log_font_count:" + " invalid number of logical fonts\n" )); + error = FT_THROW( Invalid_Table ); + goto Exit; + } + result = count; Exit: @@ -530,8 +627,6 @@ FT_Memory memory = phy_font->memory; - FT_TRACE2(( "pfr_extra_item_load_kerning_pairs()\n" )); - if ( FT_NEW( item ) ) goto Exit; @@ -782,7 +877,7 @@ FT_Byte* q2; - PFR_CHECK( num_aux ); + PFR_CHECK_SIZE( num_aux ); p += num_aux; while ( num_aux > 0 ) @@ -871,9 +966,6 @@ phy_font->num_chars = count = PFR_NEXT_USHORT( p ); phy_font->chars_offset = offset + (FT_Offset)( p - stream->cursor ); - if ( FT_NEW_ARRAY( phy_font->chars, count ) ) - goto Fail; - Size = 1 + 1 + 2; if ( flags & PFR_PHY_2BYTE_CHARCODE ) Size += 1; @@ -890,7 +982,10 @@ if ( flags & PFR_PHY_3BYTE_GPS_OFFSET ) Size += 1; - PFR_CHECK( count * Size ); + PFR_CHECK_SIZE( count * Size ); + + if ( FT_NEW_ARRAY( phy_font->chars, count ) ) + goto Fail; for ( n = 0; n < count; n++ ) { diff --git a/src/pfr/pfrload.h b/src/pfr/pfrload.h index 0a512346b..867e63240 100644 --- a/src/pfr/pfrload.h +++ b/src/pfr/pfrload.h @@ -25,14 +25,19 @@ FT_BEGIN_HEADER + /* some size checks should be always done (mainly to prevent */ + /* excessive allocation for malformed data), ... */ +#define PFR_CHECK_SIZE( x ) do \ + { \ + if ( p + (x) > limit ) \ + goto Too_Short; \ + } while ( 0 ) + + /* ... and some only if intensive checking is explicitly requested */ #ifdef PFR_CONFIG_NO_CHECKS #define PFR_CHECK( x ) do { } while ( 0 ) #else -#define PFR_CHECK( x ) do \ - { \ - if ( p + (x) > limit ) \ - goto Too_Short; \ - } while ( 0 ) +#define PFR_CHECK PFR_CHECK_SIZE #endif #define PFR_NEXT_BYTE( p ) FT_NEXT_BYTE( p ) diff --git a/src/pfr/pfrsbit.c b/src/pfr/pfrsbit.c index bed082e8b..a21963646 100644 --- a/src/pfr/pfrsbit.c +++ b/src/pfr/pfrsbit.c @@ -510,8 +510,7 @@ break; default: - FT_ERROR(( "pfr_read_bitmap_data: invalid image type\n" )); - error = FT_THROW( Invalid_File_Format ); + ; } } @@ -632,6 +631,53 @@ &xsize, &ysize, &advance, &format ); + /* + * Before allocating the target bitmap, we check whether the given + * bitmap dimensions are valid, depending on the image format. + * + * Format 0: We have a stream of pixels (with 8 pixels per byte). + * + * (xsize * ysize + 7) / 8 <= gps_size + * + * Format 1: Run-length encoding; the high nibble holds the number of + * white bits, the low nibble the number of black bits. In + * other words, a single byte can represent at most 15 + * pixels. + * + * xsize * ysize <= 15 * gps_size + * + * Format 2: Run-length encoding; the high byte holds the number of + * white bits, the low byte the number of black bits. In + * other words, two bytes can represent at most 255 pixels. + * + * xsize * ysize <= 255 * (gps_size + 1) / 2 + */ + switch ( format ) + { + case 0: + if ( ( (FT_ULong)xsize * ysize + 7 ) / 8 > gps_size ) + error = FT_THROW( Invalid_Table ); + break; + case 1: + if ( (FT_ULong)xsize * ysize > 15 * gps_size ) + error = FT_THROW( Invalid_Table ); + break; + case 2: + if ( (FT_ULong)xsize * ysize > 255 * ( ( gps_size + 1 ) / 2 ) ) + error = FT_THROW( Invalid_Table ); + break; + default: + FT_ERROR(( "pfr_slot_load_bitmap: invalid image type\n" )); + error = FT_THROW( Invalid_Table ); + } + + if ( error ) + { + if ( FT_ERR_EQ( error, Invalid_Table ) ) + FT_ERROR(( "pfr_slot_load_bitmap: invalid bitmap dimensions\n" )); + goto Exit; + } + /* * XXX: on 16bit systems we return an error for huge bitmaps * that cause size truncation, because truncated