From a0177b4076642fd92a3bc6409debcbd0ae7f32ac Mon Sep 17 00:00:00 2001
From: Erik de Castro Lopo <erikd@mega-nerd.com>
Date: Sat, 29 Nov 2014 08:12:19 +1100
Subject: [PATCH] WAV : Avoid divide by zero exception.

Found a couple of instances where a value retrieved from an input
file header was used as the denominator in a division. If the
retrieved value is zero it results on a divide by zero error.

Found using the afl (http://lcamtuf.coredump.cx/afl/) fuzzer.
---
 src/common.h  |    1 +
 src/sndfile.c |    1 +
 src/wav_w64.c |   31 ++++++++++++++++++++-----------
 3 files changed, 22 insertions(+), 11 deletions(-)

--- a/src/common.h
+++ b/src/common.h
@@ -482,6 +482,7 @@ enum
 	SFE_WAV_BAD_LIST,
 	SFE_WAV_ADPCM_NOT4BIT,
 	SFE_WAV_ADPCM_CHANNELS,
+	SFE_WAV_ADPCM_SAMPLES,
 	SFE_WAV_GSM610_FORMAT,
 	SFE_WAV_UNKNOWN_CHUNK,
 	SFE_WAV_WVPK_DATA,
--- a/src/sndfile.c
+++ b/src/sndfile.c
@@ -141,6 +141,7 @@ ErrorStruct SndfileErrors [] =
 
 	{	SFE_WAV_ADPCM_NOT4BIT	, "Error in ADPCM WAV file. Invalid bit width." },
 	{	SFE_WAV_ADPCM_CHANNELS	, "Error in ADPCM WAV file. Invalid number of channels." },
+	{	SFE_WAV_ADPCM_SAMPLES	, "Error in ADPCM WAV file. Invalid number of samples per block." },
 	{	SFE_WAV_GSM610_FORMAT	, "Error in GSM610 WAV file. Invalid format chunk." },
 
 	{	SFE_AIFF_NO_FORM		, "Error in AIFF file, bad 'FORM' marker." },
--- a/src/wav_w64.c
+++ b/src/wav_w64.c
@@ -168,7 +168,7 @@ wav_w64_read_fmt_chunk (SF_PRIVATE *psf,
 
 		case WAVE_FORMAT_ALAW :
 		case WAVE_FORMAT_MULAW :
-				if (wav_fmt->min.bytespersec / wav_fmt->min.blockalign != wav_fmt->min.samplerate)
+				if (wav_fmt->min.bytespersec != wav_fmt->min.samplerate * wav_fmt->min.blockalign)
 					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->min.bytespersec, wav_fmt->min.samplerate * wav_fmt->min.blockalign) ;
 				else
 					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->min.bytespersec) ;
@@ -186,8 +186,14 @@ wav_w64_read_fmt_chunk (SF_PRIVATE *psf,
 				if (wav_fmt->min.channels < 1 || wav_fmt->min.channels > 2)
 					return SFE_WAV_ADPCM_CHANNELS ;
 
-				bytesread +=
-				psf_binheader_readf (psf, "22", &(wav_fmt->ima.extrabytes), &(wav_fmt->ima.samplesperblock)) ;
+				bytesread += psf_binheader_readf (psf, "22", &(wav_fmt->ima.extrabytes), &(wav_fmt->ima.samplesperblock)) ;
+				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->ima.extrabytes) ;
+				if (wav_fmt->ima.samplesperblock < 1)
+				{	psf_log_printf (psf, "  Samples/Block : %d (should be > 0)\n", wav_fmt->ima.samplesperblock) ;
+					return SFE_WAV_ADPCM_SAMPLES ;
+					}
+				else
+					psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->ima.samplesperblock) ;
 
 				bytespersec = (wav_fmt->ima.samplerate * wav_fmt->ima.blockalign) / wav_fmt->ima.samplesperblock ;
 				if (wav_fmt->ima.bytespersec != (unsigned) bytespersec)
@@ -195,8 +201,6 @@ wav_w64_read_fmt_chunk (SF_PRIVATE *psf,
 				else
 					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->ima.bytespersec) ;
 
-				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->ima.extrabytes) ;
-				psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->ima.samplesperblock) ;
 				break ;
 
 		case WAVE_FORMAT_MS_ADPCM :
@@ -205,9 +209,16 @@ wav_w64_read_fmt_chunk (SF_PRIVATE *psf,
 				if (wav_fmt->msadpcm.channels < 1 || wav_fmt->msadpcm.channels > 2)
 					return SFE_WAV_ADPCM_CHANNELS ;
 
-				bytesread +=
-				psf_binheader_readf (psf, "222", &(wav_fmt->msadpcm.extrabytes),
-						&(wav_fmt->msadpcm.samplesperblock), &(wav_fmt->msadpcm.numcoeffs)) ;
+				bytesread += psf_binheader_readf (psf, "222", &(wav_fmt->msadpcm.extrabytes),
+								&(wav_fmt->msadpcm.samplesperblock), &(wav_fmt->msadpcm.numcoeffs)) ;
+
+				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->msadpcm.extrabytes) ;
+				if (wav_fmt->ima.samplesperblock < 1)
+				{	psf_log_printf (psf, "  Samples/Block : %d (should be > 0)\n", wav_fmt->ima.samplesperblock) ;
+					return SFE_WAV_ADPCM_SAMPLES ;
+					}
+				else
+					psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->ima.samplesperblock) ;
 
 				bytespersec = (wav_fmt->min.samplerate * wav_fmt->min.blockalign) / wav_fmt->msadpcm.samplesperblock ;
 				if (wav_fmt->min.bytespersec == (unsigned) bytespersec)
@@ -217,8 +228,6 @@ wav_w64_read_fmt_chunk (SF_PRIVATE *psf,
 				else
 					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->min.bytespersec, bytespersec) ;
 
-				psf_log_printf (psf, "  Extra Bytes   : %d\n", wav_fmt->msadpcm.extrabytes) ;
-				psf_log_printf (psf, "  Samples/Block : %d\n", wav_fmt->msadpcm.samplesperblock) ;
 				if (wav_fmt->msadpcm.numcoeffs > ARRAY_LEN (wav_fmt->msadpcm.coeffs))
 				{	psf_log_printf (psf, "  No. of Coeffs : %d (should be <= %d)\n", wav_fmt->msadpcm.numcoeffs, ARRAY_LEN (wav_fmt->msadpcm.coeffs)) ;
 					wav_fmt->msadpcm.numcoeffs = ARRAY_LEN (wav_fmt->msadpcm.coeffs) ;
@@ -256,7 +265,7 @@ wav_w64_read_fmt_chunk (SF_PRIVATE *psf,
 				break ;
 
 		case WAVE_FORMAT_EXTENSIBLE :
-				if (wav_fmt->ext.bytespersec / wav_fmt->ext.blockalign != wav_fmt->ext.samplerate)
+				if (wav_fmt->ext.bytespersec != wav_fmt->ext.samplerate * wav_fmt->ext.blockalign)
 					psf_log_printf (psf, "  Bytes/sec     : %d (should be %d)\n", wav_fmt->ext.bytespersec, wav_fmt->ext.samplerate * wav_fmt->ext.blockalign) ;
 				else
 					psf_log_printf (psf, "  Bytes/sec     : %d\n", wav_fmt->ext.bytespersec) ;
