Skip to content
Permalink
Browse files

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
  • Loading branch information
erikd committed Nov 30, 2014
1 parent a0177b4 commit e67d42d5585d4e14973b773293054545a377691b
Showing with 51 additions and 24 deletions.
  1. +6 −3 src/aiff.c
  2. +5 −2 src/caf.c
  3. +3 −3 src/chanmap.c
  4. +6 −5 src/mat4.c
  5. +31 −11 src/wav.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 ;

@@ -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) ;

@@ -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)
{

0 comments on commit e67d42d

Please sign in to comment.
You can’t perform that action at this time.