Skip to content

Commit c89801d

Browse files
authored
Merge pull request #43 from ActiveState/BE-3660-cve-2017-18207
Be 3660 CVE 2017 18207
2 parents 64790e7 + b9337f0 commit c89801d

File tree

7 files changed

+184
-37
lines changed

7 files changed

+184
-37
lines changed

Lib/aifc.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
"""
136136

137137
import struct
138+
import warnings
138139
import __builtin__
139140

140141
__all__ = ["Error","open","openfp"]
@@ -316,16 +317,16 @@ def initfp(self, file):
316317
except EOFError:
317318
break
318319
chunkname = chunk.getname()
319-
if chunkname == 'COMM':
320+
if chunkname == b'COMM':
320321
self._read_comm_chunk(chunk)
321322
self._comm_chunk_read = 1
322-
elif chunkname == 'SSND':
323+
elif chunkname == b'SSND':
323324
self._ssnd_chunk = chunk
324325
dummy = chunk.read(8)
325326
self._ssnd_seek_needed = 0
326-
elif chunkname == 'FVER':
327+
elif chunkname == b'FVER':
327328
self._version = _read_ulong(chunk)
328-
elif chunkname == 'MARK':
329+
elif chunkname == b'MARK':
329330
self._readmark(chunk)
330331
chunk.skip()
331332
if not self._comm_chunk_read or not self._ssnd_chunk:
@@ -465,13 +466,18 @@ def _read_comm_chunk(self, chunk):
465466
self._nframes = _read_long(chunk)
466467
self._sampwidth = (_read_short(chunk) + 7) // 8
467468
self._framerate = int(_read_float(chunk))
469+
if self._sampwidth <= 0:
470+
raise Error, 'bad sample width'
471+
if self._nchannels <= 0:
472+
raise Error, 'bad # of channels'
473+
468474
self._framesize = self._nchannels * self._sampwidth
469475
if self._aifc:
470476
#DEBUG: SGI's soundeditor produces a bad size :-(
471477
kludge = 0
472478
if chunk.chunksize == 18:
473479
kludge = 1
474-
print 'Warning: bad COMM chunk size'
480+
warnings.warn("bad COMM chunk size")
475481
chunk.chunksize = 23
476482
#DEBUG end
477483
self._comptype = chunk.read(4)
@@ -535,11 +541,13 @@ def _readmark(self, chunk):
535541
# a position 0 and name ''
536542
self._markers.append((id, pos, name))
537543
except EOFError:
538-
print 'Warning: MARK chunk contains only',
539-
print len(self._markers),
540-
if len(self._markers) == 1: print 'marker',
541-
else: print 'markers',
542-
print 'instead of', nmarkers
544+
warning_message = 'MARK chunk contains only ' + str(len(self._markers))
545+
if len(self._markers) == 1:
546+
warning_message += ' marker '
547+
else:
548+
warning_message += ' markers '
549+
warning_message += 'instead of ' + str(nmarkers)
550+
warnings.warn(warning_message)
543551

544552
class Aifc_write:
545553
# Variables used in this class:

Lib/sunau.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ def initfp(self, file):
194194
raise Error, 'unknown encoding'
195195
self._framerate = int(_read_u32(file))
196196
self._nchannels = int(_read_u32(file))
197+
if not self._nchannels:
198+
raise Error('bad # of channels')
197199
self._framesize = self._framesize * self._nchannels
198200
if self._hdr_size > 24:
199201
self._info = file.read(self._hdr_size - 24)

Lib/test/test_aifc.py

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys
88
import struct
99
import aifc
10+
from test.test_support import check_warnings
1011

1112

1213
class AifcTest(audiotests.AudioWriteTests,
@@ -216,47 +217,68 @@ def test_read_no_comm_chunk(self):
216217

217218
def test_read_no_ssnd_chunk(self):
218219
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
219-
b += b'COMM' + struct.pack('>LhlhhLL', 38, 0, 0, 0, 0, 0, 0)
220+
b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, 8,
221+
0x4000 | 12, 11025<<18, 0)
220222
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
221223
with self.assertRaisesRegexp(aifc.Error, 'COMM chunk and/or SSND chunk'
222224
' missing'):
223225
aifc.open(io.BytesIO(b))
224226

225227
def test_read_wrong_compression_type(self):
226-
b = 'FORM' + struct.pack('>L', 4) + 'AIFC'
227-
b += 'COMM' + struct.pack('>LhlhhLL', 23, 0, 0, 0, 0, 0, 0)
228-
b += 'WRNG' + struct.pack('B', 0)
228+
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
229+
b += b'COMM' + struct.pack('>LhlhhLL', 23, 1, 0, 8,
230+
0x4000 | 12, 11025<<18, 0)
231+
b += b'WRNG' + struct.pack('B', 0)
229232
self.assertRaises(aifc.Error, aifc.open, io.BytesIO(b))
230233

234+
def test_read_wrong_number_of_channels(self):
235+
for nchannels in 0, -1:
236+
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
237+
b += b'COMM' + struct.pack('>LhlhhLL', 38, nchannels, 0, 8,
238+
0x4000 | 12, 11025<<18, 0)
239+
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
240+
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
241+
with self.assertRaisesRegexp(aifc.Error, 'bad # of channels'):
242+
aifc.open(io.BytesIO(b))
243+
244+
def test_read_wrong_sample_width(self):
245+
for sampwidth in 0, -1:
246+
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
247+
b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, sampwidth,
248+
0x4000 | 12, 11025<<18, 0)
249+
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
250+
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
251+
with self.assertRaisesRegexp(aifc.Error, 'bad sample width'):
252+
aifc.open(io.BytesIO(b))
253+
231254
def test_read_wrong_marks(self):
232-
b = 'FORM' + struct.pack('>L', 4) + 'AIFF'
233-
b += 'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0)
234-
b += 'SSND' + struct.pack('>L', 8) + '\x00' * 8
235-
b += 'MARK' + struct.pack('>LhB', 3, 1, 1)
236-
with captured_stdout() as s:
255+
b = b'FORM' + struct.pack('>L', 4) + b'AIFF'
256+
b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
257+
0x4000 | 12, 11025<<18, 0)
258+
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
259+
b += b'MARK' + struct.pack('>LhB', 3, 1, 1)
260+
with check_warnings(('MARK chunk contains only 0 markers instead of 1', UserWarning)):
237261
f = aifc.open(io.BytesIO(b))
238-
self.assertEqual(s.getvalue(), 'Warning: MARK chunk contains '
239-
'only 0 markers instead of 1\n')
240-
self.assertEqual(f.getmarkers(), None)
262+
self.assertEqual(f.getmarkers(), None)
241263

242264
def test_read_comm_kludge_compname_even(self):
243-
b = 'FORM' + struct.pack('>L', 4) + 'AIFC'
244-
b += 'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0)
245-
b += 'NONE' + struct.pack('B', 4) + 'even' + '\x00'
246-
b += 'SSND' + struct.pack('>L', 8) + '\x00' * 8
247-
with captured_stdout() as s:
265+
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
266+
b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
267+
0x4000 | 12, 11025<<18, 0)
268+
b += b'NONE' + struct.pack('B', 4) + b'even' + b'\x00'
269+
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
270+
with check_warnings(('bad COMM chunk size', UserWarning)):
248271
f = aifc.open(io.BytesIO(b))
249-
self.assertEqual(s.getvalue(), 'Warning: bad COMM chunk size\n')
250272
self.assertEqual(f.getcompname(), 'even')
251273

252274
def test_read_comm_kludge_compname_odd(self):
253-
b = 'FORM' + struct.pack('>L', 4) + 'AIFC'
254-
b += 'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0)
255-
b += 'NONE' + struct.pack('B', 3) + 'odd'
256-
b += 'SSND' + struct.pack('>L', 8) + '\x00' * 8
257-
with captured_stdout() as s:
275+
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
276+
b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
277+
0x4000 | 12, 11025<<18, 0)
278+
b += b'NONE' + struct.pack('B', 3) + b'odd'
279+
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
280+
with check_warnings(('bad COMM chunk size', UserWarning)):
258281
f = aifc.open(io.BytesIO(b))
259-
self.assertEqual(s.getvalue(), 'Warning: bad COMM chunk size\n')
260282
self.assertEqual(f.getcompname(), 'odd')
261283

262284
def test_write_params_raises(self):

Lib/test/test_sunau.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from test.test_support import TESTFN, run_unittest
22
import unittest
33
from test import audiotests
4+
import io
5+
import struct
46
import sys
57
import sunau
68

@@ -96,5 +98,41 @@ def test_main():
9698
run_unittest(SunauPCM8Test, SunauPCM16Test, SunauPCM16Test,
9799
SunauPCM32Test, SunauULAWTest)
98100

101+
102+
class SunauLowLevelTest(unittest.TestCase):
103+
104+
def test_read_bad_magic_number(self):
105+
b = b'SPA'
106+
with self.assertRaises(EOFError):
107+
sunau.open(io.BytesIO(b))
108+
b = b'SPAM'
109+
with self.assertRaisesRegex(sunau.Error, 'bad magic number'):
110+
sunau.open(io.BytesIO(b))
111+
112+
def test_read_too_small_header(self):
113+
b = struct.pack('>LLLLL', sunau.AUDIO_FILE_MAGIC, 20, 0,
114+
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025)
115+
with self.assertRaisesRegex(sunau.Error, 'header size too small'):
116+
sunau.open(io.BytesIO(b))
117+
118+
def test_read_too_large_header(self):
119+
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 124, 0,
120+
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025, 1)
121+
b += b'\0' * 100
122+
with self.assertRaisesRegex(sunau.Error, 'header size ridiculously large'):
123+
sunau.open(io.BytesIO(b))
124+
125+
def test_read_wrong_encoding(self):
126+
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 24, 0, 0, 11025, 1)
127+
with self.assertRaisesRegex(sunau.Error, r'encoding not \(yet\) supported'):
128+
sunau.open(io.BytesIO(b))
129+
130+
def test_read_wrong_number_of_channels(self):
131+
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 24, 0,
132+
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025, 0)
133+
with self.assertRaisesRegex(sunau.Error, 'bad # of channels'):
134+
sunau.open(io.BytesIO(b))
135+
136+
99137
if __name__ == "__main__":
100138
test_main()

Lib/test/test_wave.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from test.test_support import TESTFN, run_unittest
22
import unittest
33
from test import audiotests
4+
from test import support
5+
import io
6+
import struct
47
import sys
58
import wave
69

@@ -119,5 +122,66 @@ def test_unseekable_incompleted_write(self):
119122
def test_main():
120123
run_unittest(WavePCM8Test, WavePCM16Test, WavePCM24Test, WavePCM32Test)
121124

125+
126+
class WaveLowLevelTest(unittest.TestCase):
127+
128+
def test_read_no_chunks(self):
129+
b = b'SPAM'
130+
with self.assertRaises(EOFError):
131+
wave.open(io.BytesIO(b))
132+
133+
def test_read_no_riff_chunk(self):
134+
b = b'SPAM' + struct.pack('<L', 0)
135+
with self.assertRaisesRegex(wave.Error,
136+
'file does not start with RIFF id'):
137+
wave.open(io.BytesIO(b))
138+
139+
def test_read_not_wave(self):
140+
b = b'RIFF' + struct.pack('<L', 4) + b'SPAM'
141+
with self.assertRaisesRegex(wave.Error,
142+
'not a WAVE file'):
143+
wave.open(io.BytesIO(b))
144+
145+
def test_read_no_fmt_no_data_chunk(self):
146+
b = b'RIFF' + struct.pack('<L', 4) + b'WAVE'
147+
with self.assertRaisesRegex(wave.Error,
148+
'fmt chunk and/or data chunk missing'):
149+
wave.open(io.BytesIO(b))
150+
151+
def test_read_no_data_chunk(self):
152+
b = b'RIFF' + struct.pack('<L', 28) + b'WAVE'
153+
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 1, 11025, 11025, 1, 8)
154+
with self.assertRaisesRegex(wave.Error,
155+
'fmt chunk and/or data chunk missing'):
156+
wave.open(io.BytesIO(b))
157+
158+
def test_read_no_fmt_chunk(self):
159+
b = b'RIFF' + struct.pack('<L', 12) + b'WAVE'
160+
b += b'data' + struct.pack('<L', 0)
161+
with self.assertRaisesRegex(wave.Error, 'data chunk before fmt chunk'):
162+
wave.open(io.BytesIO(b))
163+
164+
def test_read_wrong_form(self):
165+
b = b'RIFF' + struct.pack('<L', 36) + b'WAVE'
166+
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 2, 1, 11025, 11025, 1, 1)
167+
b += b'data' + struct.pack('<L', 0)
168+
with self.assertRaisesRegex(wave.Error, 'unknown format: 2'):
169+
wave.open(io.BytesIO(b))
170+
171+
def test_read_wrong_number_of_channels(self):
172+
b = b'RIFF' + struct.pack('<L', 36) + b'WAVE'
173+
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 0, 11025, 11025, 1, 8)
174+
b += b'data' + struct.pack('<L', 0)
175+
with self.assertRaisesRegex(wave.Error, 'bad # of channels'):
176+
wave.open(io.BytesIO(b))
177+
178+
def test_read_wrong_sample_width(self):
179+
b = b'RIFF' + struct.pack('<L', 36) + b'WAVE'
180+
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 1, 11025, 11025, 1, 0)
181+
b += b'data' + struct.pack('<L', 0)
182+
with self.assertRaisesRegex(wave.Error, 'bad sample width'):
183+
wave.open(io.BytesIO(b))
184+
185+
122186
if __name__ == '__main__':
123187
test_main()

Lib/wave.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,22 @@ def readframes(self, nframes):
266266
#
267267

268268
def _read_fmt_chunk(self, chunk):
269-
wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack('<HHLLH', chunk.read(14))
269+
try:
270+
wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
271+
except struct.error:
272+
raise EOFError
270273
if wFormatTag == WAVE_FORMAT_PCM:
271-
sampwidth = struct.unpack('<H', chunk.read(2))[0]
274+
try:
275+
sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
276+
except struct.error:
277+
raise EOFError
272278
self._sampwidth = (sampwidth + 7) // 8
279+
if not self._sampwidth:
280+
raise Error('bad sample width')
273281
else:
274-
raise Error, 'unknown format: %r' % (wFormatTag,)
282+
raise Error('unknown format: %r' % (wFormatTag,))
283+
if not self._nchannels:
284+
raise Error('bad # of channels')
275285
self._framesize = self._nchannels * self._sampwidth
276286
self._comptype = 'NONE'
277287
self._compname = 'not compressed'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Improved exceptions raised for invalid number of channels and sample width
2+
when read an audio file in modules :mod:`aifc`, :mod:`wave` and
3+
:mod:`sunau`.

0 commit comments

Comments
 (0)