Subject: bin/12328: audioplay overruns with RIFF WAVE file
To: None <gnats-bugs@gnats.netbsd.org>
From: None <takashi.yamamoto@bigfoot.com>
List: netbsd-bugs
Date: 03/04/2001 19:59:48
>Number:         12328
>Category:       bin
>Synopsis:       audioplay overruns with RIFF WAVE file
>Confidential:   yes
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Mar 04 03:03:00 PST 2001
>Closed-Date:
>Last-Modified:
>Originator:     YAMAMOTO Takashi
>Release:        current
>Organization:
>Environment:
	
System: NetBSD capybara 1.5S NetBSD 1.5S (stg) #136: Sun Mar 4 17:23:02 JST 2001 takashi@capybara:/usr/src/sys/arch/i386/compile/stg i386
Architecture: i386
Machine: i386
>Description:
	audioplay simply reads wav file until EOF.
	so chunks after data chunk causes noises.

>How-To-Repeat:
	play RIFF WAVE file which has any chunks after data chunk.
	(ex. windows98's "%WINDIR%\media\The Microsoft Sount.wav")
>Fix:
	apply following patches.
	I'm not sure that it works with sun audio files because I don't know 
	their format.


Index: common/audio.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/audio/common/audio.c,v
retrieving revision 1.10
diff -u -r1.10 audio.c
--- audio.c	2000/12/13 08:19:54	1.10
+++ audio.c	2001/03/04 10:58:27
@@ -178,13 +178,14 @@
  * find a .wav header, etc. returns header length on success
  */
 size_t
-audio_parse_wav_hdr(hdr, sz, enc, prec, sample, channels)
+audio_parse_wav_hdr(hdr, sz, enc, prec, sample, channels, datasize)
 	void	*hdr;
 	size_t	sz;
 	int	*enc;
 	int	*prec;
 	int	*sample;
 	int	*channels;
+	size_t *datasize;
 {
 	char	*where = hdr;
 	wav_audioheaderpart *part;
@@ -274,6 +275,8 @@
 		*sample = getle32(fmt->sample_rate);
 		*enc = newenc;
 		*prec = newprec;
+		if (datasize)
+			*datasize = (size_t)getle32(part->len);
 		part++;
 		return ((char *)part - (char *)hdr);
 	}
Index: common/libaudio.h
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/audio/common/libaudio.h,v
retrieving revision 1.6
diff -u -r1.6 libaudio.h
--- libaudio.h	2000/12/22 11:38:42	1.6
+++ libaudio.h	2001/03/04 10:58:29
@@ -127,7 +127,7 @@
 } wav_audioheaderfmt __attribute__((__packed__));
 
 /* returns size of header, or -1 */
-size_t audio_parse_wav_hdr (void *, size_t, int *, int *, int *, int *);
+size_t audio_parse_wav_hdr (void *, size_t, int *, int *, int *, int *, size_t *);
 
 /*
  * audio routine error codes
Index: play/play.c
===================================================================
RCS file: /cvsroot/basesrc/usr.bin/audio/play/play.c,v
retrieving revision 1.22
diff -u -r1.22 play.c
--- play.c	2001/02/19 23:03:44	1.22
+++ play.c	2001/03/04 10:56:53
@@ -49,7 +49,7 @@
 void usage (void);
 void play (char *);
 void play_fd (char *, int);
-ssize_t audioctl_write_fromhdr (void *, size_t, int);
+ssize_t audioctl_write_fromhdr (void *, size_t, int, size_t *);
 
 audio_info_t	info;
 int	volume;
@@ -201,6 +201,7 @@
 	struct stat sb;
 	void *addr, *oaddr;
 	off_t	filesize;
+	size_t datasize;
 	ssize_t	hdrlen;
 	int fd;
 
@@ -242,7 +243,7 @@
 	 * get the header length and set up the audio device
 	 */
 	if ((hdrlen = audioctl_write_fromhdr(addr,
-	    (size_t)filesize, ctlfd)) < 0) {
+	    (size_t)filesize, ctlfd, &datasize)) < 0) {
 		if (play_errstring)
 			errx(1, "%s: %s", play_errstring, file);
 		else
@@ -251,14 +252,18 @@
 
 	filesize -= hdrlen;
 	addr = (char *)addr + hdrlen;
+	if (filesize < datasize || datasize == 0) {
+		warn("bogus datasize:%u", datasize);
+		datasize = filesize;
+	}
 
-	while (filesize > bufsize) {
+	while (datasize > bufsize) {
 		if (write(audiofd, addr, bufsize) != bufsize)
 			err(1, "write failed");
 		addr = (char *)addr + bufsize;
-		filesize -= bufsize;
+		datasize -= bufsize;
 	}
-	if (write(audiofd, addr, (size_t)filesize) != (ssize_t)filesize)
+	if (write(audiofd, addr, (size_t)datasize) != (ssize_t)datasize)
 		err(1, "final write failed");
 
 	if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
@@ -279,7 +284,9 @@
 {
 	char    *buffer = malloc(bufsize);
 	ssize_t hdrlen;
-	int     n, m;
+	int     n;
+	size_t	datasize;
+	size_t	datainbuf;
 
 	if (buffer == NULL)
 		err(1, "malloc of read buffer failed");
@@ -291,7 +298,7 @@
 	if (n == 0)
 		errx(1, "EOF on standard input");
 
-	hdrlen = audioctl_write_fromhdr(buffer, n, ctlfd);
+	hdrlen = audioctl_write_fromhdr(buffer, n, ctlfd, &datasize);
 	if (hdrlen < 0) {
 		if (play_errstring)
 			errx(1, "%s: %s", play_errstring, file);
@@ -306,17 +313,26 @@
 			err(1, "bogus hdrlen %d > length %d?", (int)hdrlen, n);
 
 		memmove(buffer, buffer + hdrlen, n - hdrlen);
-
-		m = read(fd, buffer + n, hdrlen);
-		n += m;
 	}
-	/* read until EOF or error */
+
+	datainbuf = n;
 	do {
-		if (n == -1)
-			err(1, "read of standard input failed");
-		if (write(audiofd, buffer, n) != n)
+		if (datasize < datainbuf) {
+			datainbuf = datasize;
+		}
+		else {
+			n = read(fd, buffer + datainbuf, MIN(bufsize - datainbuf, datasize));
+			if (n == -1)
+				err(1, "read of %s failed", file);
+			datainbuf += n;
+		}
+		if (write(audiofd, buffer, datainbuf) != datainbuf)
 			err(1, "write failed");
-	} while ((n = read(fd, buffer, bufsize)));
+		
+		datasize -= datainbuf;
+		datainbuf = 0;
+	}
+	while (datasize);
 
 	if (ioctl(audiofd, AUDIO_DRAIN) < 0 && !qflag)
 		warn("audio drain ioctl failed");
@@ -329,10 +345,11 @@
  * uses the local "info" variable. blah... fix me!
  */
 ssize_t
-audioctl_write_fromhdr(hdr, fsz, fd)
+audioctl_write_fromhdr(hdr, fsz, fd, datasize)
 	void	*hdr;
 	size_t	fsz;
 	int	fd;
+	size_t	*datasize;
 {
 	sun_audioheader	*sunhdr;
 	ssize_t	hdr_len;
@@ -354,11 +371,12 @@
 		info.play.channels = ntohl(sunhdr->channels);
 		hdr_len = ntohl(sunhdr->hdr_size); 
 
+		*datasize = ntohl(sunhdr->data_size); 
 		goto set_audio_mode;
 	}
 
 	hdr_len = audio_parse_wav_hdr(hdr, fsz, &info.play.encoding,
-	    &info.play.precision, &info.play.sample_rate, &info.play.channels);
+	    &info.play.precision, &info.play.sample_rate, &info.play.channels, datasize);
 
 	switch (hdr_len) {
 	case AUDIO_ESHORTHDR:
>Release-Note:
>Audit-Trail:
>Unformatted: