Skip to content

Commit 0d7aced

Browse files
authored
Fix NULL pointer access error in SynthDriverAudioStream (#17607)
This is a fix for the NULL pointer access error introduced by #17592 and reported in this comment. According to Microsoft's documentation, the pcbWritten parameter in ISequentialStream::Write and the plibNewPosition parameter in IStream::Seek can be NULL, in which case the function should ignore the output parameter and succeed. Description of user facing changes None Description of development approach ISequentialStream_RemoteWrite and IStream_RemoteSeek are changed to use the low level implementation. This makes checking the output parameter easier. Then, check if the output pointer is NULL before assigning the output value.
1 parent 8cc9123 commit 0d7aced

File tree

1 file changed

+38
-13
lines changed

1 file changed

+38
-13
lines changed

source/synthDrivers/sapi5.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
# This file is covered by the GNU General Public License.
55
# See the file COPYING for more details.
66

7-
from ctypes import POINTER, c_ubyte, c_wchar_p, cast, windll, _Pointer
7+
from ctypes import POINTER, c_ubyte, c_ulong, c_wchar_p, cast, windll, _Pointer
88
from enum import IntEnum
99
import locale
1010
from collections import OrderedDict, deque
1111
from typing import TYPE_CHECKING
1212
from comInterfaces.SpeechLib import ISpEventSource, ISpNotifySource, ISpNotifySink
1313
import comtypes.client
14-
from comtypes import COMError, COMObject, IUnknown, hresult, ReturnHRESULT
14+
from comtypes import COMError, COMObject, IUnknown, hresult
1515
import winreg
1616
import nvwave
1717
from objidl import _LARGE_INTEGER, _ULARGE_INTEGER, IStream
@@ -50,8 +50,12 @@ class SpeechVoiceEvents(IntEnum):
5050

5151
if TYPE_CHECKING:
5252
LP_c_ubyte = _Pointer[c_ubyte]
53+
LP_c_ulong = _Pointer[c_ulong]
54+
LP__ULARGE_INTEGER = _Pointer[_ULARGE_INTEGER]
5355
else:
5456
LP_c_ubyte = POINTER(c_ubyte)
57+
LP_c_ulong = POINTER(c_ulong)
58+
LP__ULARGE_INTEGER = POINTER(_ULARGE_INTEGER)
5559

5660

5761
class SynthDriverAudioStream(COMObject):
@@ -68,36 +72,57 @@ def __init__(self, synthRef: weakref.ReferenceType):
6872
self.synthRef = synthRef
6973
self._writtenBytes = 0
7074

71-
def ISequentialStream_RemoteWrite(self, pv: LP_c_ubyte, cb: int) -> int:
75+
def ISequentialStream_RemoteWrite(
76+
self,
77+
this: int,
78+
pv: LP_c_ubyte,
79+
cb: int,
80+
pcbWritten: LP_c_ulong,
81+
) -> int:
7282
"""This is called when SAPI wants to write (output) a wave data chunk.
7383
:param pv: A pointer to the first wave data byte.
7484
:param cb: The number of bytes to write.
75-
:returns: The number of bytes written.
85+
:param pcbWritten: A pointer to a variable where the actual number of bytes written will be stored.
86+
Can be null.
87+
:returns: HRESULT code.
7688
"""
7789
synth = self.synthRef()
90+
if pcbWritten:
91+
pcbWritten[0] = 0
7892
if synth is None:
7993
log.debugWarning("Called Write method on AudioStream while driver is dead")
80-
return 0
94+
return hresult.E_UNEXPECTED
8195
if not synth.isSpeaking:
82-
return 0
96+
return hresult.E_FAIL
8397
synth.player.feed(pv, cb)
98+
if pcbWritten:
99+
pcbWritten[0] = cb
84100
self._writtenBytes += cb
85-
return cb
86-
87-
def IStream_RemoteSeek(self, dlibMove: _LARGE_INTEGER, dwOrigin: int) -> _ULARGE_INTEGER:
101+
return hresult.S_OK
102+
103+
def IStream_RemoteSeek(
104+
self,
105+
this: int,
106+
dlibMove: _LARGE_INTEGER,
107+
dwOrigin: int,
108+
plibNewPosition: LP__ULARGE_INTEGER,
109+
) -> int:
88110
"""This is called when SAPI wants to get the current stream position.
89111
Seeking to another position is not supported.
90112
:param dlibMove: The displacement to be added to the location indicated by the dwOrigin parameter.
91113
Only 0 is supported.
92114
:param dwOrigin: The origin for the displacement specified in dlibMove.
93115
Only 1 (STREAM_SEEK_CUR) is supported.
94-
:returns: The current stream position.
116+
:param plibNewPosition: A pointer to a ULARGE_INTEGER where the current stream position will be stored.
117+
Can be null.
118+
:returns: HRESULT code.
95119
"""
96120
if dwOrigin == 1 and dlibMove.QuadPart == 0:
97121
# SAPI is querying the current position.
98-
return _ULARGE_INTEGER(self._writtenBytes)
99-
# Return E_NOTIMPL without logging an error.
100-
raise ReturnHRESULT(hresult.E_NOTIMPL, None)
122+
if plibNewPosition:
123+
plibNewPosition.QuadPart = self._writtenBytes
124+
return hresult.S_OK
125+
return hresult.E_NOTIMPL
101126

102127
def IStream_Commit(self, grfCommitFlags: int):
103128
"""This is called when MSSP wants to flush the written data.

0 commit comments

Comments
 (0)