Skip to content

Commit e9aef63

Browse files
Fix segfaults caused by modifying existing shared library (#295)
* Fix segfaults caused by modifying existing shared library When the built shared library is copied to the correct location, it actually *modifies* any existing shared library, which causes any running process using it to segfault. To fix this, we first delete any existing shared library (which is safe to do) and then copy the newly built shared library to the correct location as a new file. Related to #257 * Use os.rename * Use os.replace * Update setuptools_rust/build.py Co-authored-by: Adam Reichold <[email protected]> * Update changelog Co-authored-by: Adam Reichold <[email protected]>
1 parent 96684af commit e9aef63

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
### Fixed
5+
- Fix a bug where rebuilding the library would cause any running processes using it to segfault. [#295](https://github.com/PyO3/setuptools-rust/pull/295)
6+
37
## 1.5.2 (2022-09-19)
48
### Fixed
59
- Fix regression in `dylib` build artifacts not being found since 1.5.0. [#290](https://github.com/PyO3/setuptools-rust/pull/290)

setuptools_rust/build.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,18 @@ def install_extension(
345345
os.makedirs(os.path.dirname(ext_path), exist_ok=True)
346346

347347
log.info("Copying rust artifact from %s to %s", dylib_path, ext_path)
348-
shutil.copyfile(dylib_path, ext_path)
348+
349+
# We want to atomically replace any existing library file. We can't
350+
# just copy the new library directly on top of the old one as that
351+
# causes the existing library to be modified (rather the replaced).
352+
# This means that any process that currently uses the shared library
353+
# will see it modified and likely segfault.
354+
#
355+
# We first copy the file to the same directory, as `os.rename`
356+
# doesn't work across file system boundaries.
357+
temp_ext_path = ext_path + "~"
358+
shutil.copyfile(dylib_path, temp_ext_path)
359+
os.replace(temp_ext_path, ext_path)
349360

350361
if sys.platform != "win32" and not debug_build:
351362
args = []

0 commit comments

Comments
 (0)