summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik de Castro Lopo <erikd@mega-nerd.com>2014-11-29 08:36:33 +1100
committerErik de Castro Lopo <erikd@mega-nerd.com>2014-11-30 13:32:41 +1100
commite67d42d5585d4e14973b773293054545a377691b (patch)
tree7cf80b5da9f2616ec7fbc40e7b46effcaf016ec0
parenta0177b4076642fd92a3bc6409debcbd0ae7f32ac (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.c9
-rw-r--r--src/caf.c7
-rw-r--r--src/chanmap.c6
-rw-r--r--src/mat4.c11
-rw-r--r--src/wav.c42
5 files changed, 51 insertions, 24 deletions
diff --git a/src/aiff.c b/src/aiff.c
index 1171c91..445fb32 100644
--- a/src/aiff.c
+++ b/src/aiff.c
@@ -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 ;
diff --git a/src/caf.c b/src/caf.c
index 133498d..3f80c6f 100644
--- a/src/caf.c
+++ b/src/caf.c
@@ -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 ;
diff --git a/src/mat4.c b/src/mat4.c
index 77ece26..0f6604d 100644
--- a/src/mat4.c
+++ b/src/mat4.c
@@ -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 ;
diff --git a/src/wav.c b/src/wav.c
index b4988a9..68b7619 100644
--- a/src/wav.c
+++ b/src/wav.c
@@ -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)
{