From: JinWang An Date: Mon, 12 Apr 2021 07:10:22 +0000 (+0900) Subject: [CVE-2017-18207]Improve exceptions in aifc, wave and sunau. X-Git-Tag: submit/tizen_6.0_base/20210412.113806 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Ftags%2Fsubmit%2Ftizen_6.0_base%2F20210412.113806;p=platform%2Fupstream%2Fpython.git [CVE-2017-18207]Improve exceptions in aifc, wave and sunau. ** DISPUTED ** The Wave_read._read_fmt_chunk function in Lib/wave.py in Python through 3.6.4 does not ensure a nonzero channel value, which allows attackers to cause a denial of service (divide-by-zero and exception) via a crafted wav format audio file. NOTE: the vendor disputes this issue because Python applications "need to be prepared to handle a wide variety of exceptions." Change-Id: Ia7b958c4d95596552802eda52f257fcc3fcc7469 Signed-off-by: JinWang An --- diff --git a/Lib/aifc.py b/Lib/aifc.py index 981f801..d0e5e02 100644 --- a/Lib/aifc.py +++ b/Lib/aifc.py @@ -465,6 +465,10 @@ class Aifc_read: self._nframes = _read_long(chunk) self._sampwidth = (_read_short(chunk) + 7) // 8 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 if self._aifc: #DEBUG: SGI's soundeditor produces a bad size :-( diff --git a/Lib/sunau.py b/Lib/sunau.py index b53044d..b5d83ea 100644 --- a/Lib/sunau.py +++ b/Lib/sunau.py @@ -194,6 +194,8 @@ class Au_read: raise Error, 'unknown encoding' self._framerate = 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 if self._hdr_size > 24: self._info = file.read(self._hdr_size - 24) diff --git a/Lib/test/test_aifc.py b/Lib/test/test_aifc.py index 92bbe7b..e8e2830 100644 --- a/Lib/test/test_aifc.py +++ b/Lib/test/test_aifc.py @@ -216,7 +216,8 @@ class AIFCLowLevelTest(unittest.TestCase): def test_read_no_ssnd_chunk(self): 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' with self.assertRaisesRegexp(aifc.Error, 'COMM chunk and/or SSND chunk' ' missing'): @@ -224,13 +225,35 @@ class AIFCLowLevelTest(unittest.TestCase): def test_read_wrong_compression_type(self): b = 'FORM' + struct.pack('>L', 4) + 'AIFC' - b += 'COMM' + struct.pack('>LhlhhLL', 23, 0, 0, 0, 0, 0, 0) + b += 'COMM' + struct.pack('>LhlhhLL', 23, 1, 0, 8, + 0x4000 | 12, 11025<<18, 0) b += 'WRNG' + struct.pack('B', 0) 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): b = 'FORM' + struct.pack('>L', 4) + 'AIFF' - b += 'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0) + b += 'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8, + 0x4000 | 12, 11025<<18, 0) b += 'SSND' + struct.pack('>L', 8) + '\x00' * 8 b += 'MARK' + struct.pack('>LhB', 3, 1, 1) with captured_stdout() as s: @@ -241,7 +264,8 @@ class AIFCLowLevelTest(unittest.TestCase): def test_read_comm_kludge_compname_even(self): b = 'FORM' + struct.pack('>L', 4) + 'AIFC' - b += 'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0) + b += 'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8, + 0x4000 | 12, 11025<<18, 0) b += 'NONE' + struct.pack('B', 4) + 'even' + '\x00' b += 'SSND' + struct.pack('>L', 8) + '\x00' * 8 with captured_stdout() as s: @@ -251,7 +275,8 @@ class AIFCLowLevelTest(unittest.TestCase): def test_read_comm_kludge_compname_odd(self): b = 'FORM' + struct.pack('>L', 4) + 'AIFC' - b += 'COMM' + struct.pack('>LhlhhLL', 18, 0, 0, 0, 0, 0, 0) + b += 'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8, + 0x4000 | 12, 11025<<18, 0) b += 'NONE' + struct.pack('B', 3) + 'odd' b += 'SSND' + struct.pack('>L', 8) + '\x00' * 8 with captured_stdout() as s: diff --git a/Lib/test/test_sunau.py b/Lib/test/test_sunau.py index f682868..146c8d0 100644 --- a/Lib/test/test_sunau.py +++ b/Lib/test/test_sunau.py @@ -1,6 +1,8 @@ from test.test_support import TESTFN, run_unittest import unittest from test import audiotests +import io +import struct import sys import sunau @@ -96,5 +98,64 @@ def test_main(): run_unittest(SunauPCM8Test, SunauPCM16Test, SunauPCM16Test, SunauPCM32Test, SunauULAWTest) +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('