diff options
author | Erik de Castro Lopo <erikd@mega-nerd.com> | 2014-11-29 08:36:33 +1100 |
---|---|---|
committer | Erik de Castro Lopo <erikd@mega-nerd.com> | 2014-11-30 13:32:41 +1100 |
commit | e67d42d5585d4e14973b773293054545a377691b (patch) | |
tree | 7cf80b5da9f2616ec7fbc40e7b46effcaf016ec0 | |
parent | a0177b4076642fd92a3bc6409debcbd0ae7f32ac (diff) |
Fix a bunch of input validation issues.
Using the afl (http://lcamtuf.coredump.cx/afl/) fuzzer found a
number of issues where a malformed file could cause the various
file format parsers to go into an infinite loop:
* WAV : 7 cases, one leading to memory exhaustion
* AIFF : 1 case
* CAF : 2 cases
* MAT4 : 2 cases
-rw-r--r-- | src/aiff.c | 9 | ||||
-rw-r--r-- | src/caf.c | 7 | ||||
-rw-r--r-- | src/chanmap.c | 6 | ||||
-rw-r--r-- | src/mat4.c | 11 | ||||
-rw-r--r-- | src/wav.c | 42 |
5 files changed, 51 insertions, 24 deletions
@@ -1,5 +1,5 @@ /* -** Copyright (C) 1999-2012 Erik de Castro Lopo <erikd@mega-nerd.com> +** Copyright (C) 1999-2014 Erik de Castro Lopo <erikd@mega-nerd.com> ** Copyright (C) 2005 David Viens <davidv@plogue.com> ** ** This program is free software; you can redistribute it and/or modify @@ -631,7 +631,7 @@ aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) if (chunk_size == 0) break ; if (chunk_size >= SIGNED_SIZEOF (ubuf.scbuf) - 1) - { psf_log_printf (psf, " %M : %d (too big, skipping)\n", marker, chunk_size) ; + { psf_log_printf (psf, " %M : %u (too big, skipping)\n", marker, chunk_size) ; psf_binheader_readf (psf, "j", chunk_size + (chunk_size & 1)) ; break ; } ; @@ -858,10 +858,13 @@ aiff_read_header (SF_PRIVATE *psf, COMM_CHUNK *comm_fmt) break ; } ; psf_log_printf (psf, "*** Unknown chunk marker %X at position %D. Exiting parser.\n", marker, psf_ftell (psf)) ; - done = 1 ; + done = SF_TRUE ; break ; } ; /* switch (marker) */ + if (marker != SSND_MARKER && chunk_size >= 0xffffff00) + break ; + if ((! psf->sf.seekable) && (found_chunk & HAVE_SSND)) break ; @@ -1,5 +1,5 @@ /* -** Copyright (C) 2005-2013 Erik de Castro Lopo <erikd@mega-nerd.com> +** Copyright (C) 2005-2014 Erik de Castro Lopo <erikd@mega-nerd.com> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU Lesser General Public License as published by @@ -502,6 +502,9 @@ caf_read_header (SF_PRIVATE *psf) break ; } ; + if (marker != data_MARKER && chunk_size >= 0xffffff00) + break ; + if (! psf->sf.seekable && have_data) break ; @@ -741,7 +744,7 @@ caf_read_chanmap (SF_PRIVATE * psf, sf_count_t chunk_size) psf_binheader_readf (psf, "j", chunk_size - bytesread) ; if (map_info && map_info->channel_map != NULL) - { size_t chanmap_size = psf->sf.channels * sizeof (psf->channel_map [0]) ; + { size_t chanmap_size = SF_MIN (psf->sf.channels, layout_tag & 0xff) * sizeof (psf->channel_map [0]) ; free (psf->channel_map) ; diff --git a/src/chanmap.c b/src/chanmap.c index 9a9f7f0..c06702c 100644 --- a/src/chanmap.c +++ b/src/chanmap.c @@ -1,5 +1,5 @@ /* -** Copyright (C) 2009-2011 Erik de Castro Lopo <erikd@mega-nerd.com> +** Copyright (C) 2009-2014 Erik de Castro Lopo <erikd@mega-nerd.com> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU Lesser General Public License as published by @@ -228,7 +228,7 @@ aiff_caf_find_channel_layout_tag (const int *chan_map, int channels) { const AIFF_CAF_CHANNEL_MAP * curr_map ; unsigned k, len ; - if (channels < 1 || channels > ARRAY_LEN (map)) + if (channels < 1 || channels >= ARRAY_LEN (map)) return 0 ; curr_map = map [channels].map ; @@ -248,7 +248,7 @@ aiff_caf_of_channel_layout_tag (int tag) unsigned k, len ; int channels = tag & 0xffff ; - if (channels < 0 || channels > ARRAY_LEN (map)) + if (channels < 0 || channels >= ARRAY_LEN (map)) return NULL ; curr_map = map [channels].map ; @@ -1,5 +1,5 @@ /* -** Copyright (C) 2002-2013 Erik de Castro Lopo <erikd@mega-nerd.com> +** Copyright (C) 2002-2014 Erik de Castro Lopo <erikd@mega-nerd.com> ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU Lesser General Public License as published by @@ -123,9 +123,6 @@ mat4_open (SF_PRIVATE *psf) default : break ; } ; - if (error) - return error ; - return error ; } /* mat4_open */ @@ -275,9 +272,13 @@ mat4_read_header (SF_PRIVATE *psf) psf->dataoffset = psf_ftell (psf) ; - if (rows == 0 && cols == 0) + if (rows == 0) { psf_log_printf (psf, "*** Error : zero channel count.\n") ; return SFE_CHANNEL_COUNT_ZERO ; + } + else if (rows > SF_MAX_CHANNELS) + { psf_log_printf (psf, "*** Error : channel count %d > SF_MAX_CHANNELS.\n", rows) ; + return SFE_CHANNEL_COUNT ; } ; psf->sf.channels = rows ; @@ -535,23 +535,26 @@ wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) case cue_MARKER : parsestage |= HAVE_other ; - { uint32_t bytesread, cue_count ; + { uint32_t thisread, bytesread, cue_count ; int id, position, chunk_id, chunk_start, block_start, offset ; bytesread = psf_binheader_readf (psf, "4", &cue_count) ; psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ; if (cue_count > 10) - { psf_log_printf (psf, " Count : %d (skipping)\n", cue_count) ; - psf_binheader_readf (psf, "j", cue_count * 24) ; + { psf_log_printf (psf, " Count : %u (skipping)\n", cue_count) ; + psf_binheader_readf (psf, "j", (cue_count > 20 ? 20 : cue_count) * 24) ; break ; } ; psf_log_printf (psf, " Count : %d\n", cue_count) ; while (cue_count) - { bytesread += psf_binheader_readf (psf, "444444", &id, &position, - &chunk_id, &chunk_start, &block_start, &offset) ; + { + if ((thisread = psf_binheader_readf (psf, "444444", &id, &position, &chunk_id, &chunk_start, &block_start, &offset)) == 0) + break ; + bytesread += thisread ; + psf_log_printf (psf, " Cue ID : %2d" " Pos : %5u Chunk : %M" " Chk Start : %d Blk Start : %d" @@ -642,6 +645,9 @@ wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) break ; default : + if (chunk_size >= 0xffff0000) + done = SF_TRUE ; + if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF) && psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF)) { psf_log_printf (psf, "*** %M : %d (unknown marker)\n", marker, chunk_size) ; @@ -660,6 +666,9 @@ wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock) break ; } ; /* switch (marker) */ + if (marker != data_MARKER && chunk_size >= 0xffffff00) + break ; + if (! psf->sf.seekable && (parsestage & HAVE_data)) break ; @@ -1369,7 +1378,11 @@ wav_subchunk_parse (SF_PRIVATE *psf, int chunk, uint32_t chunk_length) psf_log_printf (psf, "%M : %d\n", chunk, chunk_length) ; while (bytesread < chunk_length) - { bytesread += psf_binheader_readf (psf, "m", &chunk) ; + { int thisread ; + + if ((thisread = psf_binheader_readf (psf, "m", &chunk)) == 0) + break ; + bytesread += thisread ; switch (chunk) { case adtl_MARKER : @@ -1422,7 +1435,7 @@ wav_subchunk_parse (SF_PRIVATE *psf, int chunk, uint32_t chunk_length) bytesread += psf_binheader_readf (psf, "4", &dword) ; dword += (dword & 1) ; if (dword >= SIGNED_SIZEOF (buffer)) - { psf_log_printf (psf, " *** %M : %d (too big)\n", chunk, dword) ; + { psf_log_printf (psf, " *** %M : %u (too big)\n", chunk, dword) ; psf_binheader_readf (psf, "j", dword) ; break ; } ; @@ -1518,7 +1531,7 @@ wav_subchunk_parse (SF_PRIVATE *psf, int chunk, uint32_t chunk_length) static int wav_read_smpl_chunk (SF_PRIVATE *psf, uint32_t chunklen) { char buffer [512] ; - uint32_t bytesread = 0, dword, sampler_data, loop_count ; + uint32_t thisread, bytesread = 0, dword, sampler_data, loop_count ; uint32_t note, start, end, type = -1, count ; int j, k ; @@ -1567,7 +1580,9 @@ wav_read_smpl_chunk (SF_PRIVATE *psf, uint32_t chunklen) psf->instrument->loop_count = loop_count ; for (j = 0 ; loop_count > 0 && chunklen - bytesread >= 24 ; j ++) - { bytesread += psf_binheader_readf (psf, "4", &dword) ; + { if ((thisread = psf_binheader_readf (psf, "4", &dword)) == 0) + break ; + bytesread += thisread ; psf_log_printf (psf, " Cue ID : %2u", dword) ; bytesread += psf_binheader_readf (psf, "4", &type) ; @@ -1630,7 +1645,9 @@ wav_read_smpl_chunk (SF_PRIVATE *psf, uint32_t chunklen) if (k > 0 && (k % 20) == 0) psf_log_printf (psf, "\n ") ; - bytesread += psf_binheader_readf (psf, "1", &ch) ; + if ((thisread = psf_binheader_readf (psf, "1", &ch)) == 0) + break ; + bytesread += thisread ; psf_log_printf (psf, "%02X ", ch & 0xFF) ; } ; @@ -1939,10 +1956,13 @@ static int exif_subchunk_parse (SF_PRIVATE *psf, uint32_t length) { uint32_t marker, dword, vmajor = -1, vminor = -1, bytesread = 0 ; char buf [4096] ; + int thisread ; while (bytesread < length) { - bytesread += psf_binheader_readf (psf, "m", &marker) ; + if ((thisread = psf_binheader_readf (psf, "m", &marker)) == 0) + break ; + bytesread += thisread ; switch (marker) { |