Module Name:    src
Committed By:   mrg
Date:           Fri Mar  8 06:57:59 UTC 2024

Modified Files:
        src/usr.bin/audio/common: libaudio.h wav.c

Log Message:
audio_wav_parse_hdr(): avoid buffer overreads and clean up

reimplement most of this function using a new method of buffer
management to ensure that we never read beyond the provided size.

properly handle RIFF chunk lengths, instead of assuming various
offsets from most files are right.

update list of consumed documentation and fill the list of WAVE
formats from RFC 2361 (most remain not supported.)

should fix PR#57973.

tested against a large number of .wav files i have handy and with
a testsuite generator that should be incoming soon.


To generate a diff of this commit:
cvs rdiff -u -r1.21 -r1.22 src/usr.bin/audio/common/libaudio.h
cvs rdiff -u -r1.18 -r1.19 src/usr.bin/audio/common/wav.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/audio/common/libaudio.h
diff -u src/usr.bin/audio/common/libaudio.h:1.21 src/usr.bin/audio/common/libaudio.h:1.22
--- src/usr.bin/audio/common/libaudio.h:1.21	Sat Nov  9 12:46:44 2019
+++ src/usr.bin/audio/common/libaudio.h	Fri Mar  8 06:57:59 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: libaudio.h,v 1.21 2019/11/09 12:46:44 mrg Exp $	*/
+/*	$NetBSD: libaudio.h,v 1.22 2024/03/08 06:57:59 mrg Exp $	*/
 
 /*
- * Copyright (c) 1999, 2009, 2013, 2015, 2019 Matthew R. Green
+ * Copyright (c) 1999, 2009, 2013, 2015, 2019, 2024 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -106,7 +106,10 @@ int	audio_sun_to_encoding (int, u_int *,
 int	audio_encoding_to_sun (int, int, int *);
 
 /*
- * M$ WAV files, info gleamed from sox sources
+ * RIFF WAVE files.  Sources: RFC 2361, and various Microsoft docs
+ * https://learn.microsoft.com/en-us/windows/win32/xaudio2/resource-interchange-file-format--riff-
+ * https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn653308(v=vs.85)
+ * "Multimedia Programming Interface and Data Specifications 1.0" chapter 4
  */
 
 /*
@@ -118,30 +121,112 @@ int	audio_encoding_to_sun (int, int, int
 #define	WAVAUDIO_FILE_MAGIC_FMT		((u_int32_t)0x666d7420)
 #define	WAVAUDIO_FILE_MAGIC_DATA	((u_int32_t)0x64617461)
 
-/* purloined from public Microsoft RIFF docs via sox, mplayer, or directly */
+/* From RFC 2361 */
 #define WAVE_FORMAT_UNKNOWN		(0x0000)
 #define WAVE_FORMAT_PCM			(0x0001)
 #define WAVE_FORMAT_ADPCM		(0x0002)
 #define WAVE_FORMAT_IEEE_FLOAT		(0x0003)
+#define WAVE_FORMAT_VSELP		(0x0004)
+#define WAVE_FORMAT_IBM_CVSD		(0x0005)
 #define WAVE_FORMAT_ALAW		(0x0006)
 #define WAVE_FORMAT_MULAW		(0x0007)
 #define WAVE_FORMAT_OKI_ADPCM		(0x0010)
 #define WAVE_FORMAT_IMA_ADPCM		(0x0011)
+#define WAVE_FORMAT_MEDIASPACE_ADPCM	(0x0012)
+#define WAVE_FORMAT_SIERRA_ADPCM	(0x0013)
+#define WAVE_FORMAT_G723_ADPCM		(0x0014)
 #define WAVE_FORMAT_DIGISTD		(0x0015)
 #define WAVE_FORMAT_DIGIFIX		(0x0016)
+#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM	(0x0017)
+#define WAVE_FORMAT_MEDIAVISION_ADPCM	(0x0018)
+#define WAVE_FORMAT_CU_CODEC		(0x0019)
+#define WAVE_FORMAT_YAMAHA_ADPCM	(0x0020)
+#define WAVE_FORMAT_SONARC		(0x0021)
+#define WAVE_FORMAT_DSPGROUP_TRUESPEECH	(0x0022)
+#define WAVE_FORMAT_ECHOSC1		(0x0023)
+#define WAVE_FORMAT_AUDIOFILE_AF36	(0x0024)
+#define WAVE_FORMAT_APTX		(0x0025)
+#define WAVE_FORMAT_AUDIOFILE_AF10	(0x0026)
+#define WAVE_FORMAT_PROSODY_1612	(0x0027)
+#define WAVE_FORMAT_LRC			(0x0028)
 #define WAVE_FORMAT_DOLBY_AC2		(0x0030)
 #define WAVE_FORMAT_GSM610		(0x0031)
+#define WAVE_FORMAT_MSNAUDIO		(0x0032)
+#define WAVE_FORMAT_ANTEX_ADPCME	(0x0033)
+#define WAVE_FORMAT_CONTROL_RES_VQLPC	(0x0034)
+#define WAVE_FORMAT_DIGIREAL		(0x0035)
+#define WAVE_FORMAT_DIGIADPCM		(0x0036)
+#define WAVE_FORMAT_CONTROL_RES_CR10	(0x0037)
+#define WAVE_FORMAT_NMS_VBXADPCM	(0x0038)
+#define WAVE_FORMAT_ROLAND_RDAC		(0x0039)
+#define WAVE_FORMAT_ECHOSC3		(0x003a)
 #define WAVE_FORMAT_ROCKWELL_ADPCM	(0x003b)
 #define WAVE_FORMAT_ROCKWELL_DIGITALK	(0x003c)
+#define WAVE_FORMAT_XEBEC		(0x003d)
 #define WAVE_FORMAT_G721_ADPCM		(0x0040)
 #define WAVE_FORMAT_G728_CELP		(0x0041)
+#define WAVE_FORMAT_MSG723		(0x0042)
 #define WAVE_FORMAT_MPEG		(0x0050)
+#define WAVE_FORMAT_RT24		(0x0052)
+#define WAVE_FORMAT_PAC			(0x0053)
 #define WAVE_FORMAT_MPEGLAYER3		(0x0055)
+#define WAVE_FORMAT_LUCENT_G723		(0x0059)
+#define WAVE_FORMAT_CIRRUS		(0x0060)
+#define WAVE_FORMAT_ESPCM		(0x0061)
+#define WAVE_FORMAT_VOXWARE		(0x0062)
+#define WAVE_FORMAT_CANOPUS_ATRAC	(0x0063)
 #define WAVE_FORMAT_G726_ADPCM		(0x0064)
 #define WAVE_FORMAT_G722_ADPCM		(0x0065)
-#define IBM_FORMAT_MULAW		(0x0101)
-#define IBM_FORMAT_ALAW			(0x0102)
-#define IBM_FORMAT_ADPCM		(0x0103)
+#define WAVE_FORMAT_DSAT		(0x0066)
+#define WAVE_FORMAT_DSAT_DISPLAY	(0x0067)
+#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED (0x0069)
+#define WAVE_FORMAT_VOXWARE_AC8		(0x0070)
+#define WAVE_FORMAT_VOXWARE_AC10	(0x0071)
+#define WAVE_FORMAT_VOXWARE_AC16	(0x0072)
+#define WAVE_FORMAT_VOXWARE_AC20	(0x0073)
+#define WAVE_FORMAT_VOXWARE_RT24	(0x0074)
+#define WAVE_FORMAT_VOXWARE_RT29	(0x0075)
+#define WAVE_FORMAT_VOXWARE_RT29HW	(0x0076)
+#define WAVE_FORMAT_VOXWARE_VR12	(0x0077)
+#define WAVE_FORMAT_VOXWARE_VR18	(0x0078)
+#define WAVE_FORMAT_VOXWARE_TQ40	(0x0079)
+#define WAVE_FORMAT_SOFTSOUND		(0x0080)
+#define WAVE_FORMAT_VOXWARE_TQ60	(0x0081)
+#define WAVE_FORMAT_MSRT24		(0x0082)
+#define WAVE_FORMAT_G729A		(0x0083)
+#define WAVE_FORMAT_MVI_MV12		(0x0084)
+#define WAVE_FORMAT_DF_G726		(0x0085)
+#define WAVE_FORMAT_DF_GSM610		(0x0086)
+#define WAVE_FORMAT_ISIAUDIO		(0x0088)
+#define WAVE_FORMAT_ONLIVE		(0x0089)
+#define WAVE_FORMAT_SBC24		(0x0091)
+#define WAVE_FORMAT_DOLBY_AC3_SPDIF	(0x0092)
+#define WAVE_FORMAT_ZYXEL_ADPCM		(0x0097)
+#define WAVE_FORMAT_PHILIPS_LPCBB	(0x0098)
+#define WAVE_FORMAT_PACKED		(0x0099)
+#define WAVE_FORMAT_RHETOREX_ADPCM	(0x0100)
+#define WAVE_FORMAT_IRAT		(0x0101)
+#define WAVE_FORMAT_VIVO_G723		(0x0111)
+#define WAVE_FORMAT_VIVO_SIREN		(0x0112)
+#define WAVE_FORMAT_DIGITAL_G723	(0x0123)
+#define WAVE_FORMAT_CREATIVE_ADPCM	(0x0200)
+#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 (0x0202)
+#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 (0x0203)
+#define WAVE_FORMAT_QUARTERDECK		(0x0220)
+#define WAVE_FORMAT_FM_TOWNS_SND	(0x0300)
+#define WAVE_FORMAT_BTV_DIGITAL		(0x0400)
+#define WAVE_FORMAT_VME_VMPCM		(0x0680)
+#define WAVE_FORMAT_OLIGSM		(0x1000)
+#define WAVE_FORMAT_OLIADPCM		(0x1001)
+#define WAVE_FORMAT_OLICELP		(0x1002)
+#define WAVE_FORMAT_OLISBC		(0x1003)
+#define WAVE_FORMAT_OLIOPR		(0x1004)
+#define WAVE_FORMAT_LH_CODEC		(0x1100)
+#define WAVE_FORMAT_NORRIS		(0x1400)
+#define WAVE_FORMAT_ISIAUDIO2		(0x1401)
+#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS (0x1500)
+#define WAVE_FORMAT_DVM			(0x2000)
+
 #define WAVE_FORMAT_EXTENSIBLE		(0xfffe)
 
 const char *wav_enc_from_val (int);
@@ -155,7 +240,7 @@ typedef struct {
 	u_int16_t	tag;
 	u_int16_t	channels;
 	u_int32_t	sample_rate;
-	u_int32_t	avg_bps;
+	u_int32_t	avg_bytes_per_sec;
 	u_int16_t	alignment;
 	u_int16_t	bits_per_sample;
 } __packed wav_audioheaderfmt;
@@ -165,7 +250,7 @@ typedef struct {
 	u_int16_t	valid_bits;
 	u_int32_t	speaker_pos_mask;
 	u_int16_t	sub_tag;
-	u_int8_t	dummy[14];
+	u_int8_t	guid[14];
 } __packed wav_audiohdrextensible;
 
 /* returns size of header, or -ve for failure */

Index: src/usr.bin/audio/common/wav.c
diff -u src/usr.bin/audio/common/wav.c:1.18 src/usr.bin/audio/common/wav.c:1.19
--- src/usr.bin/audio/common/wav.c:1.18	Tue Feb 27 21:05:34 2024
+++ src/usr.bin/audio/common/wav.c	Fri Mar  8 06:57:59 2024
@@ -1,7 +1,7 @@
-/*	$NetBSD: wav.c,v 1.18 2024/02/27 21:05:34 gson Exp $	*/
+/*	$NetBSD: wav.c,v 1.19 2024/03/08 06:57:59 mrg Exp $	*/
 
 /*
- * Copyright (c) 2002, 2009, 2013, 2015, 2019 Matthew R. Green
+ * Copyright (c) 2002, 2009, 2013, 2015, 2019, 2024 Matthew R. Green
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -33,7 +33,7 @@
 #include <sys/cdefs.h>
 
 #ifndef lint
-__RCSID("$NetBSD: wav.c,v 1.18 2024/02/27 21:05:34 gson Exp $");
+__RCSID("$NetBSD: wav.c,v 1.19 2024/03/08 06:57:59 mrg Exp $");
 #endif
 
 
@@ -49,6 +49,7 @@ __RCSID("$NetBSD: wav.c,v 1.18 2024/02/2
 #include <string.h>
 #include <stdint.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #include "libaudio.h"
 #include "auconv.h"
@@ -96,61 +97,94 @@ ssize_t
 audio_wav_parse_hdr(void *hdr, size_t sz, u_int *enc, u_int *prec,
     u_int *sample, u_int *channels, off_t *datasize)
 {
-	char	*where = hdr, *owhere;
+	char	*where = hdr;
 	wav_audioheaderpart part;
 	wav_audioheaderfmt fmt;
 	wav_audiohdrextensible ext;
-	char	*end = (((char *)hdr) + sz);
+	size_t remain = sz;
 	u_int	newenc, newprec;
+	uint32_t len = 0;
 	u_int16_t fmttag;
 	static const char
 	    strfmt[4] = "fmt ",
 	    strRIFF[4] = "RIFF",
 	    strWAVE[4] = "WAVE",
 	    strdata[4] = "data";
+	bool found;
 
 	if (sz < 32)
 		return (AUDIO_ENOENT);
 
-	if (strncmp(where, strRIFF, sizeof strRIFF))
+#define ADJUST(l) do {				\
+	if (l >= remain)			\
+		return (AUDIO_ESHORTHDR);	\
+	where += (l);				\
+	remain -= (l);				\
+} while (0)
+
+	if (strncmp(where, strRIFF, sizeof strRIFF) != 0)
 		return (AUDIO_ENOENT);
-	where += 8;
-	if (strncmp(where, strWAVE, sizeof strWAVE))
+	ADJUST(8);
+	if (strncmp(where, strWAVE, sizeof strWAVE) != 0)
 		return (AUDIO_ENOENT);
-	where += 4;
+	ADJUST(4);
 
-	do {
+	found = false;
+	while (remain >= sizeof part) {
 		memcpy(&part, where, sizeof part);
-		owhere = where;
-		where += getle32(part.len) + 8;
-	} while (where < end && strncmp(part.name, strfmt, sizeof strfmt));
+		ADJUST(sizeof part);
+		len = getle32(part.len);
+		if (strncmp(part.name, strfmt, sizeof strfmt) == 0) {
+			found = true;
+			break;
+		}
+		ADJUST(len);
+	}
 
 	/* too short ? */
-	if (where + sizeof fmt > end)
+	if (!found || remain <= sizeof fmt)
 		return (AUDIO_ESHORTHDR);
 
-	memcpy(&fmt, (owhere + 8), sizeof fmt);
-
+	memcpy(&fmt, where, sizeof fmt);
 	fmttag = getle16(fmt.tag);
 	if (verbose)
-		printf("WAVE format tag: %x\n", fmttag);
+		printf("WAVE format tag/len: %04x/%u\n", fmttag, len);
 
 	if (fmttag == WAVE_FORMAT_EXTENSIBLE) {
-		if ((uintptr_t)(where - owhere) < sizeof(fmt) + sizeof(ext))
+		if (len < sizeof(fmt) + sizeof(ext)) {
+			if (verbose)
+				fprintf(stderr, "short WAVE ext fmt\n");
 			return (AUDIO_ESHORTHDR);
-		memcpy(&ext, owhere + sizeof fmt, sizeof ext);
-		if (getle16(ext.len) < sizeof(ext) - sizeof(ext.len))
+		}
+		if (remain <= sizeof ext + sizeof fmt) {
+			if (verbose)
+				fprintf(stderr, "WAVE ext truncated\n");
 			return (AUDIO_ESHORTHDR);
+		}
+		memcpy(&ext, where + sizeof fmt, sizeof ext);
 		fmttag = getle16(ext.sub_tag);
+		uint16_t sublen = getle16(ext.len);
 		if (verbose)
-			printf("WAVE extensible sub tag: %x\n", fmttag);
+			printf("WAVE extensible tag/len: %04x/%u\n", fmttag, sublen);
+
+		/*
+		 * XXXMRG: it may be that part.len (aka sizeof fmt + sizeof ext)
+		 * should equal sizeof fmt + sizeof ext.len + sublen?  this block
+		 * is only entered for part.len == 40, where ext.len is expected
+		 * to be 22 (sizeof ext.len = 2, sizeof fmt = 16).
+		 *
+		 * warn about this, but don't consider it an error.
+		 */
+		if (ext.len != 22 && verbose)
+			fprintf(stderr, "warning: WAVE ext.len %u not 22\n", ext.len);
+	} else if (len < sizeof(fmt)) {
+		if (verbose)
+			fprintf(stderr, "WAVE fmt unsupported size %u\n", len);
+		return (AUDIO_EWAVUNSUPP);
 	}
+	ADJUST(len);
 
 	switch (fmttag) {
-	case WAVE_FORMAT_UNKNOWN:
-	case IBM_FORMAT_MULAW:
-	case IBM_FORMAT_ALAW:
-	case IBM_FORMAT_ADPCM:
 	default:
 		return (AUDIO_EWAVUNSUPP);
 
@@ -205,13 +239,22 @@ audio_wav_parse_hdr(void *hdr, size_t sz
 		break;
 	}
 
-	do {
+	found = false;
+	while (remain >= sizeof part) {
 		memcpy(&part, where, sizeof part);
-		owhere = where;
-		where += (getle32(part.len) + 8);
-	} while (where < end && strncmp(part.name, strdata, sizeof strdata));
+		ADJUST(sizeof part);
+		if (strncmp(part.name, strdata, sizeof strdata) == 0) {
+			found = true;
+			break;
+		}
+		/* Adjust len here only for non-data parts. */
+		len = getle32(part.len);
+		ADJUST(len);
+	}
+	if (!found)
+		return (AUDIO_ENOENT);
 
-	if ((where - getle32(part.len)) <= end) {
+	if (getle32(part.len)) {
 		if (channels)
 			*channels = (u_int)getle16(fmt.channels);
 		if (sample)
@@ -222,9 +265,11 @@ audio_wav_parse_hdr(void *hdr, size_t sz
 			*prec = newprec;
 		if (datasize)
 			*datasize = (off_t)getle32(part.len);
-		return (owhere - (char *)hdr + 8);
+		return (where - (char *)hdr);
 	}
 	return (AUDIO_EWAVNODATA);
+
+#undef ADJUST
 }
 
 
@@ -240,7 +285,7 @@ wav_prepare_header(struct track_info *ti
 	 *
 	 *      bytes   purpose
 	 *      0-3     "RIFF"
-	 *      4-7     file length (minus 8)
+	 *      4-7     RIFF chunk length (file length minus 8)
 	 *      8-15    "WAVEfmt "
 	 *      16-19   format size
 	 *      20-21   format tag

Reply via email to