| From 9341e9c6e70cd3ad76c901c3cf052d4cb52fd827 Mon Sep 17 00:00:00 2001 |
| From: Erik de Castro Lopo <erikd@mega-nerd.com> |
| Date: Thu, 27 Jun 2013 18:04:03 +1000 |
| Subject: [PATCH] src/sd2.c : Fix segfault in SD2 RSRC parser. |
| |
| (Upstream commit 9341e9c6e70cd3ad76c901c3cf052d4cb52fd827) |
| |
| A specially crafted resource fork for an SD2 file can cause |
| the SD2 RSRC parser to read data from outside a dynamically |
| defined buffer. The data that is read is converted into a |
| short or int and used during further processing. |
| |
| Since no write occurs, this is unlikely to be exploitable. |
| |
| Bug reported by The Mayhem Team from Cylab, Carnegie Mellon |
| Univeristy. Paper is: |
| http://users.ece.cmu.edu/~arebert/papers/mayhem-oakland-12.pdf |
| |
| Upstream-Status: Backport |
| |
| Signed-off-by: Yue Tao <yue.tao@windriver.com> |
| --- |
| src/sd2.c | 93 ++++++++++++++++++++++++++++++++++++------------------------- |
| 1 file changed, 55 insertions(+), 38 deletions(-) |
| |
| diff --git a/src/sd2.c b/src/sd2.c |
| index 35ce36b..6be150c 100644 |
| --- a/src/sd2.c |
| +++ b/src/sd2.c |
| @@ -1,5 +1,5 @@ |
| /* |
| -** Copyright (C) 2001-2011 Erik de Castro Lopo <erikd@mega-nerd.com> |
| +** Copyright (C) 2001-2013 Erik de Castro Lopo <erikd@mega-nerd.com> |
| ** Copyright (C) 2004 Paavo Jumppanen |
| ** |
| ** This program is free software; you can redistribute it and/or modify |
| @@ -371,44 +371,61 @@ sd2_write_rsrc_fork (SF_PRIVATE *psf, int UNUSED (calc_length)) |
| */ |
| |
| static inline int |
| -read_char (const unsigned char * data, int offset) |
| -{ return data [offset] ; |
| -} /* read_char */ |
| +read_rsrc_char (const SD2_RSRC *prsrc, int offset) |
| +{ const unsigned char * data = prsrc->rsrc_data ; |
| + if (offset < 0 || offset >= prsrc->rsrc_len) |
| + return 0 ; |
| + return data [offset] ; |
| +} /* read_rsrc_char */ |
| |
| static inline int |
| -read_short (const unsigned char * data, int offset) |
| -{ return (data [offset] << 8) + data [offset + 1] ; |
| -} /* read_short */ |
| +read_rsrc_short (const SD2_RSRC *prsrc, int offset) |
| +{ const unsigned char * data = prsrc->rsrc_data ; |
| + if (offset < 0 || offset + 1 >= prsrc->rsrc_len) |
| + return 0 ; |
| + return (data [offset] << 8) + data [offset + 1] ; |
| +} /* read_rsrc_short */ |
| |
| static inline int |
| -read_int (const unsigned char * data, int offset) |
| -{ return (data [offset] << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ; |
| -} /* read_int */ |
| +read_rsrc_int (const SD2_RSRC *prsrc, int offset) |
| +{ const unsigned char * data = prsrc->rsrc_data ; |
| + if (offset < 0 || offset + 3 >= prsrc->rsrc_len) |
| + return 0 ; |
| + return (data [offset] << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ; |
| +} /* read_rsrc_int */ |
| |
| static inline int |
| -read_marker (const unsigned char * data, int offset) |
| -{ |
| +read_rsrc_marker (const SD2_RSRC *prsrc, int offset) |
| +{ const unsigned char * data = prsrc->rsrc_data ; |
| + |
| + if (offset < 0 || offset + 3 >= prsrc->rsrc_len) |
| + return 0 ; |
| + |
| if (CPU_IS_BIG_ENDIAN) |
| return (data [offset] << 24) + (data [offset + 1] << 16) + (data [offset + 2] << 8) + data [offset + 3] ; |
| - else if (CPU_IS_LITTLE_ENDIAN) |
| + if (CPU_IS_LITTLE_ENDIAN) |
| return data [offset] + (data [offset + 1] << 8) + (data [offset + 2] << 16) + (data [offset + 3] << 24) ; |
| - else |
| - return 0x666 ; |
| -} /* read_marker */ |
| + |
| + return 0 ; |
| +} /* read_rsrc_marker */ |
| |
| static void |
| -read_str (const unsigned char * data, int offset, char * buffer, int buffer_len) |
| -{ int k ; |
| +read_rsrc_str (const SD2_RSRC *prsrc, int offset, char * buffer, int buffer_len) |
| +{ const unsigned char * data = prsrc->rsrc_data ; |
| + int k ; |
| |
| memset (buffer, 0, buffer_len) ; |
| |
| + if (offset < 0 || offset + buffer_len >= prsrc->rsrc_len) |
| + return ; |
| + |
| for (k = 0 ; k < buffer_len - 1 ; k++) |
| { if (psf_isprint (data [offset + k]) == 0) |
| return ; |
| buffer [k] = data [offset + k] ; |
| } ; |
| return ; |
| -} /* read_str */ |
| +} /* read_rsrc_str */ |
| |
| static int |
| sd2_parse_rsrc_fork (SF_PRIVATE *psf) |
| @@ -435,17 +452,17 @@ sd2_parse_rsrc_fork (SF_PRIVATE *psf) |
| /* Reset the header storage because we have changed to the rsrcdes. */ |
| psf->headindex = psf->headend = rsrc.rsrc_len ; |
| |
| - rsrc.data_offset = read_int (rsrc.rsrc_data, 0) ; |
| - rsrc.map_offset = read_int (rsrc.rsrc_data, 4) ; |
| - rsrc.data_length = read_int (rsrc.rsrc_data, 8) ; |
| - rsrc.map_length = read_int (rsrc.rsrc_data, 12) ; |
| + rsrc.data_offset = read_rsrc_int (&rsrc, 0) ; |
| + rsrc.map_offset = read_rsrc_int (&rsrc, 4) ; |
| + rsrc.data_length = read_rsrc_int (&rsrc, 8) ; |
| + rsrc.map_length = read_rsrc_int (&rsrc, 12) ; |
| |
| if (rsrc.data_offset == 0x51607 && rsrc.map_offset == 0x20000) |
| { psf_log_printf (psf, "Trying offset of 0x52 bytes.\n") ; |
| - rsrc.data_offset = read_int (rsrc.rsrc_data, 0x52 + 0) + 0x52 ; |
| - rsrc.map_offset = read_int (rsrc.rsrc_data, 0x52 + 4) + 0x52 ; |
| - rsrc.data_length = read_int (rsrc.rsrc_data, 0x52 + 8) ; |
| - rsrc.map_length = read_int (rsrc.rsrc_data, 0x52 + 12) ; |
| + rsrc.data_offset = read_rsrc_int (&rsrc, 0x52 + 0) + 0x52 ; |
| + rsrc.map_offset = read_rsrc_int (&rsrc, 0x52 + 4) + 0x52 ; |
| + rsrc.data_length = read_rsrc_int (&rsrc, 0x52 + 8) ; |
| + rsrc.map_length = read_rsrc_int (&rsrc, 0x52 + 12) ; |
| } ; |
| |
| psf_log_printf (psf, " data offset : 0x%04X\n map offset : 0x%04X\n" |
| @@ -488,7 +505,7 @@ sd2_parse_rsrc_fork (SF_PRIVATE *psf) |
| goto parse_rsrc_fork_cleanup ; |
| } ; |
| |
| - rsrc.string_offset = rsrc.map_offset + read_short (rsrc.rsrc_data, rsrc.map_offset + 26) ; |
| + rsrc.string_offset = rsrc.map_offset + read_rsrc_short (&rsrc, rsrc.map_offset + 26) ; |
| if (rsrc.string_offset > rsrc.rsrc_len) |
| { psf_log_printf (psf, "Bad string offset (%d).\n", rsrc.string_offset) ; |
| error = SFE_SD2_BAD_RSRC ; |
| @@ -497,7 +514,7 @@ sd2_parse_rsrc_fork (SF_PRIVATE *psf) |
| |
| rsrc.type_offset = rsrc.map_offset + 30 ; |
| |
| - rsrc.type_count = read_short (rsrc.rsrc_data, rsrc.map_offset + 28) + 1 ; |
| + rsrc.type_count = read_rsrc_short (&rsrc, rsrc.map_offset + 28) + 1 ; |
| if (rsrc.type_count < 1) |
| { psf_log_printf (psf, "Bad type count.\n") ; |
| error = SFE_SD2_BAD_RSRC ; |
| @@ -513,11 +530,11 @@ sd2_parse_rsrc_fork (SF_PRIVATE *psf) |
| |
| rsrc.str_index = -1 ; |
| for (k = 0 ; k < rsrc.type_count ; k ++) |
| - { marker = read_marker (rsrc.rsrc_data, rsrc.type_offset + k * 8) ; |
| + { marker = read_rsrc_marker (&rsrc, rsrc.type_offset + k * 8) ; |
| |
| if (marker == STR_MARKER) |
| { rsrc.str_index = k ; |
| - rsrc.str_count = read_short (rsrc.rsrc_data, rsrc.type_offset + k * 8 + 4) + 1 ; |
| + rsrc.str_count = read_rsrc_short (&rsrc, rsrc.type_offset + k * 8 + 4) + 1 ; |
| error = parse_str_rsrc (psf, &rsrc) ; |
| goto parse_rsrc_fork_cleanup ; |
| } ; |
| @@ -549,26 +566,26 @@ parse_str_rsrc (SF_PRIVATE *psf, SD2_RSRC * rsrc) |
| for (k = 0 ; data_offset + data_len < rsrc->rsrc_len ; k++) |
| { int slen ; |
| |
| - slen = read_char (rsrc->rsrc_data, str_offset) ; |
| - read_str (rsrc->rsrc_data, str_offset + 1, name, SF_MIN (SIGNED_SIZEOF (name), slen + 1)) ; |
| + slen = read_rsrc_char (rsrc, str_offset) ; |
| + read_rsrc_str (rsrc, str_offset + 1, name, SF_MIN (SIGNED_SIZEOF (name), slen + 1)) ; |
| str_offset += slen + 1 ; |
| |
| - rsrc_id = read_short (rsrc->rsrc_data, rsrc->item_offset + k * 12) ; |
| + rsrc_id = read_rsrc_short (rsrc, rsrc->item_offset + k * 12) ; |
| |
| - data_offset = rsrc->data_offset + read_int (rsrc->rsrc_data, rsrc->item_offset + k * 12 + 4) ; |
| + data_offset = rsrc->data_offset + read_rsrc_int (rsrc, rsrc->item_offset + k * 12 + 4) ; |
| if (data_offset < 0 || data_offset > rsrc->rsrc_len) |
| { psf_log_printf (psf, "Exiting parser on data offset of %d.\n", data_offset) ; |
| break ; |
| } ; |
| |
| - data_len = read_int (rsrc->rsrc_data, data_offset) ; |
| + data_len = read_rsrc_int (rsrc, data_offset) ; |
| if (data_len < 0 || data_len > rsrc->rsrc_len) |
| { psf_log_printf (psf, "Exiting parser on data length of %d.\n", data_len) ; |
| break ; |
| } ; |
| |
| - slen = read_char (rsrc->rsrc_data, data_offset + 4) ; |
| - read_str (rsrc->rsrc_data, data_offset + 5, value, SF_MIN (SIGNED_SIZEOF (value), slen + 1)) ; |
| + slen = read_rsrc_char (rsrc, data_offset + 4) ; |
| + read_rsrc_str (rsrc, data_offset + 5, value, SF_MIN (SIGNED_SIZEOF (value), slen + 1)) ; |
| |
| psf_log_printf (psf, " 0x%04x %4d %4d %3d '%s'\n", data_offset, rsrc_id, data_len, slen, value) ; |
| |
| -- |
| 1.7.9.5 |
| |