Skip to content

Commit 15e5b1b

Browse files
author
Victor Stinner
committed
Issue python#7673: Fix security vulnerability (CVE-2010-2089) in the audioop module,
ensure that the input string length is a multiple of the frame size
1 parent 7b18c93 commit 15e5b1b

File tree

3 files changed

+108
-74
lines changed

3 files changed

+108
-74
lines changed

Lib/test/test_audioop.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ def gendata4():
2020

2121
data = [gendata1(), gendata2(), gendata4()]
2222

23+
INVALID_DATA = [
24+
('abc', 0),
25+
('abc', 2),
26+
('abc', 4),
27+
]
28+
2329

2430
class TestAudioop(unittest.TestCase):
2531

@@ -166,6 +172,33 @@ def test_negativelen(self):
166172
self.assertRaises(audioop.error,
167173
audioop.findmax, ''.join( chr(x) for x in xrange(256)), -2392392)
168174

175+
def test_issue7673(self):
176+
state = None
177+
for data, size in INVALID_DATA:
178+
size2 = size
179+
self.assertRaises(audioop.error, audioop.getsample, data, size, 0)
180+
self.assertRaises(audioop.error, audioop.max, data, size)
181+
self.assertRaises(audioop.error, audioop.minmax, data, size)
182+
self.assertRaises(audioop.error, audioop.avg, data, size)
183+
self.assertRaises(audioop.error, audioop.rms, data, size)
184+
self.assertRaises(audioop.error, audioop.avgpp, data, size)
185+
self.assertRaises(audioop.error, audioop.maxpp, data, size)
186+
self.assertRaises(audioop.error, audioop.cross, data, size)
187+
self.assertRaises(audioop.error, audioop.mul, data, size, 1.0)
188+
self.assertRaises(audioop.error, audioop.tomono, data, size, 0.5, 0.5)
189+
self.assertRaises(audioop.error, audioop.tostereo, data, size, 0.5, 0.5)
190+
self.assertRaises(audioop.error, audioop.add, data, data, size)
191+
self.assertRaises(audioop.error, audioop.bias, data, size, 0)
192+
self.assertRaises(audioop.error, audioop.reverse, data, size)
193+
self.assertRaises(audioop.error, audioop.lin2lin, data, size, size2)
194+
self.assertRaises(audioop.error, audioop.ratecv, data, size, 1, 1, 1, state)
195+
self.assertRaises(audioop.error, audioop.lin2ulaw, data, size)
196+
self.assertRaises(audioop.error, audioop.ulaw2lin, data, size)
197+
self.assertRaises(audioop.error, audioop.lin2alaw, data, size)
198+
self.assertRaises(audioop.error, audioop.alaw2lin, data, size)
199+
self.assertRaises(audioop.error, audioop.lin2adpcm, data, size, state)
200+
self.assertRaises(audioop.error, audioop.adpcm2lin, data, size, state)
201+
169202
def test_main():
170203
run_unittest(TestAudioop)
171204

Misc/NEWS

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ Core and Builtins
1717
Library
1818
-------
1919

20+
- Issue #7673: Fix security vulnerability (CVE-2010-2089) in the audioop
21+
module, ensure that the input string length is a multiple of the frame size
22+
2023
- Issue #9075: In the ssl module, remove the setting of a ``debug`` flag
2124
on an OpenSSL structure.
2225

@@ -51,7 +54,7 @@ Build
5154
Library
5255
-------
5356

54-
- Issue #6589: cleanup asyncore.socket_map in case smtpd.SMTPServer constructor
57+
- Issue #6589: cleanup asyncore.socket_map in case smtpd.SMTPServer constructor
5558
raises an exception.
5659

5760
- Issue #8959: fix regression caused by using unmodified libffi

Modules/audioop.c

Lines changed: 71 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,29 @@ static int stepsizeTable[89] = {
295295

296296
static PyObject *AudioopError;
297297

298+
static int
299+
audioop_check_size(int size)
300+
{
301+
if (size != 1 && size != 2 && size != 4) {
302+
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
303+
return 0;
304+
}
305+
else
306+
return 1;
307+
}
308+
309+
static int
310+
audioop_check_parameters(int len, int size)
311+
{
312+
if (!audioop_check_size(size))
313+
return 0;
314+
if (len % size != 0) {
315+
PyErr_SetString(AudioopError, "not a whole number of frames");
316+
return 0;
317+
}
318+
return 1;
319+
}
320+
298321
static PyObject *
299322
audioop_getsample(PyObject *self, PyObject *args)
300323
{
@@ -304,10 +327,8 @@ audioop_getsample(PyObject *self, PyObject *args)
304327

305328
if ( !PyArg_ParseTuple(args, "s#ii:getsample", &cp, &len, &size, &i) )
306329
return 0;
307-
if ( size != 1 && size != 2 && size != 4 ) {
308-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
309-
return 0;
310-
}
330+
if (!audioop_check_parameters(len, size))
331+
return NULL;
311332
if ( i < 0 || i >= len/size ) {
312333
PyErr_SetString(AudioopError, "Index out of range");
313334
return 0;
@@ -328,10 +349,8 @@ audioop_max(PyObject *self, PyObject *args)
328349

329350
if ( !PyArg_ParseTuple(args, "s#i:max", &cp, &len, &size) )
330351
return 0;
331-
if ( size != 1 && size != 2 && size != 4 ) {
332-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
333-
return 0;
334-
}
352+
if (!audioop_check_parameters(len, size))
353+
return NULL;
335354
for ( i=0; i<len; i+= size) {
336355
if ( size == 1 ) val = (int)*CHARP(cp, i);
337356
else if ( size == 2 ) val = (int)*SHORTP(cp, i);
@@ -352,10 +371,8 @@ audioop_minmax(PyObject *self, PyObject *args)
352371

353372
if (!PyArg_ParseTuple(args, "s#i:minmax", &cp, &len, &size))
354373
return NULL;
355-
if (size != 1 && size != 2 && size != 4) {
356-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
374+
if (!audioop_check_parameters(len, size))
357375
return NULL;
358-
}
359376
for (i = 0; i < len; i += size) {
360377
if (size == 1) val = (int) *CHARP(cp, i);
361378
else if (size == 2) val = (int) *SHORTP(cp, i);
@@ -376,10 +393,8 @@ audioop_avg(PyObject *self, PyObject *args)
376393

377394
if ( !PyArg_ParseTuple(args, "s#i:avg", &cp, &len, &size) )
378395
return 0;
379-
if ( size != 1 && size != 2 && size != 4 ) {
380-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
381-
return 0;
382-
}
396+
if (!audioop_check_parameters(len, size))
397+
return NULL;
383398
for ( i=0; i<len; i+= size) {
384399
if ( size == 1 ) val = (int)*CHARP(cp, i);
385400
else if ( size == 2 ) val = (int)*SHORTP(cp, i);
@@ -403,10 +418,8 @@ audioop_rms(PyObject *self, PyObject *args)
403418

404419
if ( !PyArg_ParseTuple(args, "s#i:rms", &cp, &len, &size) )
405420
return 0;
406-
if ( size != 1 && size != 2 && size != 4 ) {
407-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
408-
return 0;
409-
}
421+
if (!audioop_check_parameters(len, size))
422+
return NULL;
410423
for ( i=0; i<len; i+= size) {
411424
if ( size == 1 ) val = (int)*CHARP(cp, i);
412425
else if ( size == 2 ) val = (int)*SHORTP(cp, i);
@@ -612,10 +625,8 @@ audioop_avgpp(PyObject *self, PyObject *args)
612625

613626
if ( !PyArg_ParseTuple(args, "s#i:avgpp", &cp, &len, &size) )
614627
return 0;
615-
if ( size != 1 && size != 2 && size != 4 ) {
616-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
617-
return 0;
618-
}
628+
if (!audioop_check_parameters(len, size))
629+
return NULL;
619630
/* Compute first delta value ahead. Also automatically makes us
620631
** skip the first extreme value
621632
*/
@@ -669,10 +680,8 @@ audioop_maxpp(PyObject *self, PyObject *args)
669680

670681
if ( !PyArg_ParseTuple(args, "s#i:maxpp", &cp, &len, &size) )
671682
return 0;
672-
if ( size != 1 && size != 2 && size != 4 ) {
673-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
674-
return 0;
675-
}
683+
if (!audioop_check_parameters(len, size))
684+
return NULL;
676685
/* Compute first delta value ahead. Also automatically makes us
677686
** skip the first extreme value
678687
*/
@@ -720,10 +729,8 @@ audioop_cross(PyObject *self, PyObject *args)
720729

721730
if ( !PyArg_ParseTuple(args, "s#i:cross", &cp, &len, &size) )
722731
return 0;
723-
if ( size != 1 && size != 2 && size != 4 ) {
724-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
725-
return 0;
726-
}
732+
if (!audioop_check_parameters(len, size))
733+
return NULL;
727734
ncross = -1;
728735
prevval = 17; /* Anything <> 0,1 */
729736
for ( i=0; i<len; i+= size) {
@@ -748,6 +755,8 @@ audioop_mul(PyObject *self, PyObject *args)
748755

749756
if ( !PyArg_ParseTuple(args, "s#id:mul", &cp, &len, &size, &factor ) )
750757
return 0;
758+
if (!audioop_check_parameters(len, size))
759+
return NULL;
751760

752761
if ( size == 1 ) maxval = (double) 0x7f;
753762
else if ( size == 2 ) maxval = (double) 0x7fff;
@@ -790,6 +799,12 @@ audioop_tomono(PyObject *self, PyObject *args)
790799
if ( !PyArg_ParseTuple(args, "s#idd:tomono",
791800
&cp, &len, &size, &fac1, &fac2 ) )
792801
return 0;
802+
if (!audioop_check_parameters(len, size))
803+
return NULL;
804+
if (((len / size) & 1) != 0) {
805+
PyErr_SetString(AudioopError, "not a whole number of frames");
806+
return NULL;
807+
}
793808

794809
if ( size == 1 ) maxval = (double) 0x7f;
795810
else if ( size == 2 ) maxval = (double) 0x7fff;
@@ -835,6 +850,8 @@ audioop_tostereo(PyObject *self, PyObject *args)
835850
if ( !PyArg_ParseTuple(args, "s#idd:tostereo",
836851
&cp, &len, &size, &fac1, &fac2 ) )
837852
return 0;
853+
if (!audioop_check_parameters(len, size))
854+
return NULL;
838855

839856
if ( size == 1 ) maxval = (double) 0x7f;
840857
else if ( size == 2 ) maxval = (double) 0x7fff;
@@ -893,7 +910,8 @@ audioop_add(PyObject *self, PyObject *args)
893910
if ( !PyArg_ParseTuple(args, "s#s#i:add",
894911
&cp1, &len1, &cp2, &len2, &size ) )
895912
return 0;
896-
913+
if (!audioop_check_parameters(len1, size))
914+
return NULL;
897915
if ( len1 != len2 ) {
898916
PyErr_SetString(AudioopError, "Lengths should be the same");
899917
return 0;
@@ -948,10 +966,8 @@ audioop_bias(PyObject *self, PyObject *args)
948966
&cp, &len, &size , &bias) )
949967
return 0;
950968

951-
if ( size != 1 && size != 2 && size != 4) {
952-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
953-
return 0;
954-
}
969+
if (!audioop_check_parameters(len, size))
970+
return NULL;
955971

956972
rv = PyString_FromStringAndSize(NULL, len);
957973
if ( rv == 0 )
@@ -984,10 +1000,8 @@ audioop_reverse(PyObject *self, PyObject *args)
9841000
&cp, &len, &size) )
9851001
return 0;
9861002

987-
if ( size != 1 && size != 2 && size != 4 ) {
988-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
989-
return 0;
990-
}
1003+
if (!audioop_check_parameters(len, size))
1004+
return NULL;
9911005

9921006
rv = PyString_FromStringAndSize(NULL, len);
9931007
if ( rv == 0 )
@@ -1021,11 +1035,10 @@ audioop_lin2lin(PyObject *self, PyObject *args)
10211035
&cp, &len, &size, &size2) )
10221036
return 0;
10231037

1024-
if ( (size != 1 && size != 2 && size != 4) ||
1025-
(size2 != 1 && size2 != 2 && size2 != 4)) {
1026-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1027-
return 0;
1028-
}
1038+
if (!audioop_check_parameters(len, size))
1039+
return NULL;
1040+
if (!audioop_check_size(size2))
1041+
return NULL;
10291042

10301043
if (len/size > INT_MAX/size2) {
10311044
PyErr_SetString(PyExc_MemoryError,
@@ -1075,10 +1088,8 @@ audioop_ratecv(PyObject *self, PyObject *args)
10751088
&nchannels, &inrate, &outrate, &state,
10761089
&weightA, &weightB))
10771090
return NULL;
1078-
if (size != 1 && size != 2 && size != 4) {
1079-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1091+
if (!audioop_check_size(size))
10801092
return NULL;
1081-
}
10821093
if (nchannels < 1) {
10831094
PyErr_SetString(AudioopError, "# of channels should be >= 1");
10841095
return NULL;
@@ -1255,10 +1266,8 @@ audioop_lin2ulaw(PyObject *self, PyObject *args)
12551266
&cp, &len, &size) )
12561267
return 0 ;
12571268

1258-
if ( size != 1 && size != 2 && size != 4) {
1259-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1260-
return 0;
1261-
}
1269+
if (!audioop_check_parameters(len, size))
1270+
return NULL;
12621271

12631272
rv = PyString_FromStringAndSize(NULL, len/size);
12641273
if ( rv == 0 )
@@ -1289,10 +1298,8 @@ audioop_ulaw2lin(PyObject *self, PyObject *args)
12891298
&cp, &len, &size) )
12901299
return 0;
12911300

1292-
if ( size != 1 && size != 2 && size != 4) {
1293-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1294-
return 0;
1295-
}
1301+
if (!audioop_check_parameters(len, size))
1302+
return NULL;
12961303

12971304
if (len > INT_MAX/size) {
12981305
PyErr_SetString(PyExc_MemoryError,
@@ -1328,10 +1335,8 @@ audioop_lin2alaw(PyObject *self, PyObject *args)
13281335
&cp, &len, &size) )
13291336
return 0;
13301337

1331-
if ( size != 1 && size != 2 && size != 4) {
1332-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1333-
return 0;
1334-
}
1338+
if (!audioop_check_parameters(len, size))
1339+
return NULL;
13351340

13361341
rv = PyString_FromStringAndSize(NULL, len/size);
13371342
if ( rv == 0 )
@@ -1362,10 +1367,8 @@ audioop_alaw2lin(PyObject *self, PyObject *args)
13621367
&cp, &len, &size) )
13631368
return 0;
13641369

1365-
if ( size != 1 && size != 2 && size != 4) {
1366-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1367-
return 0;
1368-
}
1370+
if (!audioop_check_parameters(len, size))
1371+
return NULL;
13691372

13701373
if (len > INT_MAX/size) {
13711374
PyErr_SetString(PyExc_MemoryError,
@@ -1402,11 +1405,8 @@ audioop_lin2adpcm(PyObject *self, PyObject *args)
14021405
&cp, &len, &size, &state) )
14031406
return 0;
14041407

1405-
1406-
if ( size != 1 && size != 2 && size != 4) {
1407-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1408-
return 0;
1409-
}
1408+
if (!audioop_check_parameters(len, size))
1409+
return NULL;
14101410

14111411
str = PyString_FromStringAndSize(NULL, len/(size*2));
14121412
if ( str == 0 )
@@ -1509,10 +1509,8 @@ audioop_adpcm2lin(PyObject *self, PyObject *args)
15091509
&cp, &len, &size, &state) )
15101510
return 0;
15111511

1512-
if ( size != 1 && size != 2 && size != 4) {
1513-
PyErr_SetString(AudioopError, "Size should be 1, 2 or 4");
1514-
return 0;
1515-
}
1512+
if (!audioop_check_parameters(len, size))
1513+
return NULL;
15161514

15171515
/* Decode state, should have (value, step) */
15181516
if ( state == Py_None ) {

0 commit comments

Comments
 (0)