bpo-32056: Improve exceptions in aifc, wave and sunau. (GH-5951)

This commit is contained in:
Serhiy Storchaka 2018-03-18 09:55:53 +02:00 committed by GitHub
parent bc300ce205
commit 134cb01cda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 150 additions and 7 deletions

View File

@ -467,6 +467,10 @@ class Aifc_read:
self._nframes = _read_long(chunk) self._nframes = _read_long(chunk)
self._sampwidth = (_read_short(chunk) + 7) // 8 self._sampwidth = (_read_short(chunk) + 7) // 8
self._framerate = int(_read_float(chunk)) self._framerate = int(_read_float(chunk))
if self._sampwidth <= 0:
raise Error('bad sample width')
if self._nchannels <= 0:
raise Error('bad # of channels')
self._framesize = self._nchannels * self._sampwidth self._framesize = self._nchannels * self._sampwidth
if self._aifc: if self._aifc:
#DEBUG: SGI's soundeditor produces a bad size :-( #DEBUG: SGI's soundeditor produces a bad size :-(

View File

@ -208,6 +208,8 @@ class Au_read:
raise Error('unknown encoding') raise Error('unknown encoding')
self._framerate = int(_read_u32(file)) self._framerate = int(_read_u32(file))
self._nchannels = int(_read_u32(file)) self._nchannels = int(_read_u32(file))
if not self._nchannels:
raise Error('bad # of channels')
self._framesize = self._framesize * self._nchannels self._framesize = self._framesize * self._nchannels
if self._hdr_size > 24: if self._hdr_size > 24:
self._info = file.read(self._hdr_size - 24) self._info = file.read(self._hdr_size - 24)

View File

@ -268,7 +268,8 @@ class AIFCLowLevelTest(unittest.TestCase):
def test_read_no_ssnd_chunk(self): def test_read_no_ssnd_chunk(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 38, 0, 0, 0, 0, 0, 0) b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00' b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
with self.assertRaisesRegex(aifc.Error, 'COMM chunk and/or SSND chunk' with self.assertRaisesRegex(aifc.Error, 'COMM chunk and/or SSND chunk'
' missing'): ' missing'):
@ -276,13 +277,35 @@ class AIFCLowLevelTest(unittest.TestCase):
def test_read_wrong_compression_type(self): def test_read_wrong_compression_type(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 23, 0, 0, 0, 0, 0, 0) b += b'COMM' + struct.pack('>LhlhhLL', 23, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'WRNG' + struct.pack('B', 0) b += b'WRNG' + struct.pack('B', 0)
self.assertRaises(aifc.Error, aifc.open, io.BytesIO(b)) self.assertRaises(aifc.Error, aifc.open, io.BytesIO(b))
def test_read_wrong_number_of_channels(self):
for nchannels in 0, -1:
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 38, nchannels, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertRaisesRegex(aifc.Error, 'bad # of channels'):
aifc.open(io.BytesIO(b))
def test_read_wrong_sample_width(self):
for sampwidth in 0, -1:
b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, sampwidth,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertRaisesRegex(aifc.Error, 'bad sample width'):
aifc.open(io.BytesIO(b))
def test_read_wrong_marks(self): def test_read_wrong_marks(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFF' b = b'FORM' + struct.pack('>L', 4) + b'AIFF'
b += b'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0) b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
b += b'MARK' + struct.pack('>LhB', 3, 1, 1) b += b'MARK' + struct.pack('>LhB', 3, 1, 1)
with self.assertWarns(UserWarning) as cm: with self.assertWarns(UserWarning) as cm:
@ -293,7 +316,8 @@ class AIFCLowLevelTest(unittest.TestCase):
def test_read_comm_kludge_compname_even(self): def test_read_comm_kludge_compname_even(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0) b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 4) + b'even' + b'\x00' b += b'NONE' + struct.pack('B', 4) + b'even' + b'\x00'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertWarns(UserWarning) as cm: with self.assertWarns(UserWarning) as cm:
@ -303,7 +327,8 @@ class AIFCLowLevelTest(unittest.TestCase):
def test_read_comm_kludge_compname_odd(self): def test_read_comm_kludge_compname_odd(self):
b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b = b'FORM' + struct.pack('>L', 4) + b'AIFC'
b += b'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0) b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8,
0x4000 | 12, 11025<<18, 0)
b += b'NONE' + struct.pack('B', 3) + b'odd' b += b'NONE' + struct.pack('B', 3) + b'odd'
b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8
with self.assertWarns(UserWarning) as cm: with self.assertWarns(UserWarning) as cm:

View File

@ -1,6 +1,8 @@
import unittest import unittest
from test import audiotests from test import audiotests
from audioop import byteswap from audioop import byteswap
import io
import struct
import sys import sys
import sunau import sunau
@ -121,5 +123,40 @@ class SunauMiscTests(audiotests.AudioMiscTests, unittest.TestCase):
module = sunau module = sunau
class SunauLowLevelTest(unittest.TestCase):
def test_read_bad_magic_number(self):
b = b'SPA'
with self.assertRaises(EOFError):
sunau.open(io.BytesIO(b))
b = b'SPAM'
with self.assertRaisesRegex(sunau.Error, 'bad magic number'):
sunau.open(io.BytesIO(b))
def test_read_too_small_header(self):
b = struct.pack('>LLLLL', sunau.AUDIO_FILE_MAGIC, 20, 0,
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025)
with self.assertRaisesRegex(sunau.Error, 'header size too small'):
sunau.open(io.BytesIO(b))
def test_read_too_large_header(self):
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 124, 0,
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025, 1)
b += b'\0' * 100
with self.assertRaisesRegex(sunau.Error, 'header size ridiculously large'):
sunau.open(io.BytesIO(b))
def test_read_wrong_encoding(self):
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 24, 0, 0, 11025, 1)
with self.assertRaisesRegex(sunau.Error, r'encoding not \(yet\) supported'):
sunau.open(io.BytesIO(b))
def test_read_wrong_number_of_channels(self):
b = struct.pack('>LLLLLL', sunau.AUDIO_FILE_MAGIC, 24, 0,
sunau.AUDIO_FILE_ENCODING_LINEAR_8, 11025, 0)
with self.assertRaisesRegex(sunau.Error, 'bad # of channels'):
sunau.open(io.BytesIO(b))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -2,6 +2,8 @@ import unittest
from test import audiotests from test import audiotests
from test import support from test import support
from audioop import byteswap from audioop import byteswap
import io
import struct
import sys import sys
import wave import wave
@ -111,5 +113,65 @@ class MiscTestCase(audiotests.AudioMiscTests, unittest.TestCase):
support.check__all__(self, wave, blacklist=blacklist) support.check__all__(self, wave, blacklist=blacklist)
class WaveLowLevelTest(unittest.TestCase):
def test_read_no_chunks(self):
b = b'SPAM'
with self.assertRaises(EOFError):
wave.open(io.BytesIO(b))
def test_read_no_riff_chunk(self):
b = b'SPAM' + struct.pack('<L', 0)
with self.assertRaisesRegex(wave.Error,
'file does not start with RIFF id'):
wave.open(io.BytesIO(b))
def test_read_not_wave(self):
b = b'RIFF' + struct.pack('<L', 4) + b'SPAM'
with self.assertRaisesRegex(wave.Error,
'not a WAVE file'):
wave.open(io.BytesIO(b))
def test_read_no_fmt_no_data_chunk(self):
b = b'RIFF' + struct.pack('<L', 4) + b'WAVE'
with self.assertRaisesRegex(wave.Error,
'fmt chunk and/or data chunk missing'):
wave.open(io.BytesIO(b))
def test_read_no_data_chunk(self):
b = b'RIFF' + struct.pack('<L', 28) + b'WAVE'
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 1, 11025, 11025, 1, 8)
with self.assertRaisesRegex(wave.Error,
'fmt chunk and/or data chunk missing'):
wave.open(io.BytesIO(b))
def test_read_no_fmt_chunk(self):
b = b'RIFF' + struct.pack('<L', 12) + b'WAVE'
b += b'data' + struct.pack('<L', 0)
with self.assertRaisesRegex(wave.Error, 'data chunk before fmt chunk'):
wave.open(io.BytesIO(b))
def test_read_wrong_form(self):
b = b'RIFF' + struct.pack('<L', 36) + b'WAVE'
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 2, 1, 11025, 11025, 1, 1)
b += b'data' + struct.pack('<L', 0)
with self.assertRaisesRegex(wave.Error, 'unknown format: 2'):
wave.open(io.BytesIO(b))
def test_read_wrong_number_of_channels(self):
b = b'RIFF' + struct.pack('<L', 36) + b'WAVE'
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 0, 11025, 11025, 1, 8)
b += b'data' + struct.pack('<L', 0)
with self.assertRaisesRegex(wave.Error, 'bad # of channels'):
wave.open(io.BytesIO(b))
def test_read_wrong_sample_width(self):
b = b'RIFF' + struct.pack('<L', 36) + b'WAVE'
b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 1, 11025, 11025, 1, 0)
b += b'data' + struct.pack('<L', 0)
with self.assertRaisesRegex(wave.Error, 'bad sample width'):
wave.open(io.BytesIO(b))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -253,12 +253,22 @@ class Wave_read:
# #
def _read_fmt_chunk(self, chunk): def _read_fmt_chunk(self, chunk):
try:
wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14)) wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
except struct.error:
raise EOFError from None
if wFormatTag == WAVE_FORMAT_PCM: if wFormatTag == WAVE_FORMAT_PCM:
try:
sampwidth = struct.unpack_from('<H', chunk.read(2))[0] sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
except struct.error:
raise EOFError from None
self._sampwidth = (sampwidth + 7) // 8 self._sampwidth = (sampwidth + 7) // 8
if not self._sampwidth:
raise Error('bad sample width')
else: else:
raise Error('unknown format: %r' % (wFormatTag,)) raise Error('unknown format: %r' % (wFormatTag,))
if not self._nchannels:
raise Error('bad # of channels')
self._framesize = self._nchannels * self._sampwidth self._framesize = self._nchannels * self._sampwidth
self._comptype = 'NONE' self._comptype = 'NONE'
self._compname = 'not compressed' self._compname = 'not compressed'

View File

@ -0,0 +1,3 @@
Improved exceptions raised for invalid number of channels and sample width
when read an audio file in modules :mod:`aifc`, :mod:`wave` and
:mod:`sunau`.