[Python-checkins] CVS: python/dist/src/Modules audioop.c,1.44,1.45

Tim Peters tim_one@users.sourceforge.net
Tue, 04 Dec 2001 22:05:09 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory usw-pr-cvs1:/tmp/cvs-serv23575/python/Modules

Modified Files:
	audioop.c 
Log Message:
SF bug 482574:  audioop.ratecv crashes.
Bugfix candidate.
A numerically naive computation of output buffer size caused crashes
and spurious MemoryErrors for reasonable arguments.
audioop_ratecv():  Avoid spurious overflow by careful reworking of the
buffer size computations, triggering MemoryError if and only if the
final buffer size can't be represented in a C int (although
PyString_FromStringAndSize may legitimately raise MemoryError even if
it does fit in a C int).  All reasonable arguments should work as
intended now, and all unreasonable arguments should be cuaght.


Index: audioop.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/audioop.c,v
retrieving revision 1.44
retrieving revision 1.45
diff -C2 -d -r1.44 -r1.45
*** audioop.c	2000/09/01 23:29:26	1.44
--- audioop.c	2001/12/05 06:05:07	1.45
***************
*** 3,6 ****
--- 3,7 ----
  
  #include "Python.h"
+ #include <math.h>
  
  #if SIZEOF_INT == 4
***************
*** 903,906 ****
--- 904,908 ----
  	int chan, d, *prev_i, *cur_i, cur_o;
  	PyObject *state, *samps, *str, *rv = NULL;
+ 	int size_times_nchannels;
  
  	weightA = 1;
***************
*** 937,941 ****
  	prev_i = (int *) malloc(nchannels * sizeof(int));
  	cur_i = (int *) malloc(nchannels * sizeof(int));
- 	len /= size * nchannels;	/* # of frames */
  	if (prev_i == NULL || cur_i == NULL) {
  		(void) PyErr_NoMemory();
--- 939,942 ----
***************
*** 943,951 ****
  	}
  
  	if (state == Py_None) {
  		d = -outrate;
  		for (chan = 0; chan < nchannels; chan++)
  			prev_i[chan] = cur_i[chan] = 0;
! 	} else {
  		if (!PyArg_ParseTuple(state,
  				"iO!;audioop.ratecv: illegal state argument",
--- 944,964 ----
  	}
  
+ 	size_times_nchannels = size * nchannels;
+ 	if (size_times_nchannels / nchannels != size) {
+ 		/* This overflow test is rigorously correct because
+ 		   both multiplicands are >= 1.  Use the argument names
+ 		   from the docs for the error msg. */
+ 		PyErr_SetString(PyExc_OverflowError,
+ 		                "width * nchannels too big for a C int");
+ 		goto exit;
+ 	}
+ 	len /= size_times_nchannels;	/* # of frames */
+ 
  	if (state == Py_None) {
  		d = -outrate;
  		for (chan = 0; chan < nchannels; chan++)
  			prev_i[chan] = cur_i[chan] = 0;
! 	}
! 	else {
  		if (!PyArg_ParseTuple(state,
  				"iO!;audioop.ratecv: illegal state argument",
***************
*** 963,970 ****
  		}
  	}
! 	str = PyString_FromStringAndSize(
! 	      NULL, size * nchannels * (len * outrate + inrate - 1) / inrate);
! 	if (str == NULL)
! 		goto exit;
  	ncp = PyString_AsString(str);
  
--- 976,1026 ----
  		}
  	}
! 
! 	/* str <- Space for the output buffer. */
! 	{
! 		/* There are len input frames, so we need (mathematically)
! 		   ceiling(len*outrate/inrate) output frames, and each frame
! 		   requires size_times_nchannels bytes.  Computing this
! 		   without spurious overflow is the challenge. */
! 		int ceiling;   /* the number of output frames, eventually */
! 		int nbytes;    /* the number of output bytes needed */
! 		int q = len / inrate;
! 		int r = len - q * inrate;
! 		/* Now len = q * inrate + r exactly, so
! 		   len*outrate/inrate =
! 		   (q*inrate+r)*outrate/inrate =
! 		   (q*inrate*outrate + r*outrate)/inrate =
! 		   q*outrate + r*outrate/inrate exactly.
! 		   q*outrate is an exact integer, so the ceiling we're after is
! 		   q*outrate + ceiling(r*outrate/inrate). */
! 		ceiling = q * outrate;
! 		if (ceiling / outrate != q) {
! 			PyErr_SetString(PyExc_MemoryError,
! 				"not enough memory for output buffer");
! 			goto exit;
! 		}
! 		/* Since r = len % inrate, in particular r < inrate.  So
! 		   r * outrate / inrate = (r / inrate) * outrate < outrate,
! 		   so ceiling(r * outrate / inrate) <= outrate:  the final
! 		   result fits in an int -- it can't overflow. */
! 		assert(r < inrate);
! 		q = (int)ceil((double)r * (double)outrate / (double)inrate);
! 		assert(q <= outrate);
! 		ceiling += q;
! 		if (ceiling < 0) {
! 			PyErr_SetString(PyExc_MemoryError,
! 				"not enough memory for output buffer");
! 			goto exit;
! 		}
! 		nbytes = ceiling * size_times_nchannels;
! 		if (nbytes / size_times_nchannels != ceiling) {
! 			PyErr_SetString(PyExc_MemoryError,
! 				"not enough memory for output buffer");
! 			goto exit;
! 		}
! 		str = PyString_FromStringAndSize(NULL, nbytes);
! 		if (str == NULL)
! 			goto exit;
! 	}
  	ncp = PyString_AsString(str);