Skip to content

Commit 9ced432

Browse files
AndreasKaratzashunterhogan
authored andcommitted
Fix sf_error(NULL) race condition under concurrent open failures
Signed-off-by: Andreas Karatzas <akaratza@amd.com>
1 parent acc90ad commit 9ced432

1 file changed

Lines changed: 27 additions & 10 deletions

File tree

soundfile.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
import os as _os
1414
import sys as _sys
15-
from collections.abc import Generator
15+
import threading as _threading
16+
import numpy.typing
17+
from os import SEEK_SET, SEEK_CUR, SEEK_END
1618
from ctypes.util import find_library as _find_library
1719
from os import SEEK_CUR, SEEK_END, SEEK_SET
1820
from typing import Any, BinaryIO, Final, Literal, TypeAlias, overload
@@ -1307,6 +1309,13 @@ def close(self) -> None:
13071309
self._file = None
13081310
_error_check(err)
13091311

1312+
# sf_error(NULL) returns a global (non-thread-safe) error code.
1313+
# When an sf_open* call fails (returns NULL), we must call
1314+
# sf_error(NULL) to retrieve the error code, but another thread
1315+
# may clear the global error between our open and our sf_error.
1316+
# This lock serialises the open+sf_error(NULL) pair to prevent that.
1317+
_open_lock = _threading.Lock()
1318+
13101319
def _open(self, file, mode_int, closefd):
13111320
"""Call the appropriate sf_open*() function from libsndfile."""
13121321
if isinstance(file, (str, bytes)):
@@ -1322,18 +1331,26 @@ def _open(self, file, mode_int, closefd):
13221331
openfunction = _snd.sf_wchar_open
13231332
else:
13241333
file = file.encode(_sys.getfilesystemencoding())
1325-
file_ptr = openfunction(file, mode_int, self._info)
1334+
with self._open_lock:
1335+
file_ptr = openfunction(file, mode_int, self._info)
1336+
if file_ptr == _ffi.NULL:
1337+
err = _snd.sf_error(file_ptr)
1338+
raise LibsndfileError(err, prefix="Error opening {0!r}: ".format(self.name))
13261339
elif isinstance(file, int):
1327-
file_ptr = _snd.sf_open_fd(file, mode_int, self._info, closefd)
1340+
with self._open_lock:
1341+
file_ptr = _snd.sf_open_fd(file, mode_int, self._info, closefd)
1342+
if file_ptr == _ffi.NULL:
1343+
err = _snd.sf_error(file_ptr)
1344+
raise LibsndfileError(err, prefix="Error opening {0!r}: ".format(self.name))
13281345
elif _has_virtual_io_attrs(file, mode_int):
1329-
file_ptr = _snd.sf_open_virtual(self._init_virtual_io(file),
1330-
mode_int, self._info, _ffi.NULL)
1346+
with self._open_lock:
1347+
file_ptr = _snd.sf_open_virtual(self._init_virtual_io(file),
1348+
mode_int, self._info, _ffi.NULL)
1349+
if file_ptr == _ffi.NULL:
1350+
err = _snd.sf_error(file_ptr)
1351+
raise LibsndfileError(err, prefix="Error opening {0!r}: ".format(self.name))
13311352
else:
1332-
raise TypeError(f"Invalid file: {self.name!r}")
1333-
if file_ptr == _ffi.NULL:
1334-
# get the actual error code
1335-
err = _snd.sf_error(file_ptr)
1336-
raise LibsndfileError(err, prefix=f"Error opening {self.name!r}: ")
1353+
raise TypeError("Invalid file: {0!r}".format(self.name))
13371354
if mode_int == _snd.SFM_WRITE:
13381355
# Due to a bug in libsndfile version <= 1.0.25, frames != 0
13391356
# when opening a named pipe in SFM_WRITE mode.

0 commit comments

Comments
 (0)