Live › vulnerability › CVE-2026-112938
Go to dashboardStrictSoftFileLock checks its `<path>.filelock` directory once and then follows that name by path, so a local user who can rename it can swap in a symbolic link during the gap. The lock user's process then creates claim directories and files with its own privileges in a directory the attacker chooses, which can make another strict lock time out until an operator clears the stranded claims.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-28 21:12 UTC. All times on this page are UTC.
Systems that run filelock's StrictSoftFileLock or AsyncStrictSoftFileLock as a user with a shared lock path, where another local account can write to a directory above the lock. Exposure depends on the lock directory's permissions and on whether the locked program takes the lock repeatedly. Systems that use only the default soft lock, or keep lock paths in directories only the lock user can write, can skip it.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| filelock | pypi | >= 3.30.0, <= 4.2.0 | 4.2.1 |
python -c "import importlib.metadata as m; print(m.version('filelock'))" and check whether the result is between 3.30.0 and 4.2.0.ls -ld on the directory that holds <path>.filelock.filelock package to 4.2.1.Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
StrictSoftFileLock and AsyncStrictSoftFileLock check <path>.filelock (the coordination directory that holds the lock's state) with one lstat call on its name. lstat reports what a name is without following a symbolic link (a file that redirects to another path). After that check, the code reaches <path>.filelock/claims by pathname for every later operation (mkdir, scan, publish, link, unlink). _open_directory passes O_NOFOLLOW (a flag that makes an open fail when the final name is a symbolic link), but the flag covers only the final claims component. The kernel follows a symbolic link in any earlier component.
Another local user (the other user below) who can rename <path>.filelock exchanges it for a symbolic link after the check. In a shared directory without the sticky bit (a directory flag that lets only a file's owner rename or remove it), any user with write access can do this. Where the shared directory is sticky, such as /tmp, only the user who created <path>.filelock before the user running the lock (the lock user below) first used it can. The lock user's process (root's, if root takes the lock) then creates a claims directory and claim files (the files whose presence says a process wants or holds the lock) inside a directory the other user chooses but only the lock user can write.
When the link points at another strict lock's .filelock directory, the stranded claims make that lock time out for every process until an operator finds and force-breaks each one. The claims property and force_break() resolve the same path with no check at all. Affected versions: filelock >= 3.30.0 and <= 4.2.0 (finding ANT-2026-SVFEYX89).
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:H, 5.8 (Medium).
AV:L (local): the other user needs an account on the same host. AC:H (high attack complexity): the swap must land between the lstat and the next mkdir. The other user does not aim at that window. It flips the name in a loop with renameat2(RENAME_EXCHANGE) (a system call that swaps two names in one step) across repeated acquires, and won after 1 to 31 runs of the lock user's program in the scanner's trials. A sticky directory also requires it to have created <path>.filelock first. PR:L (low privileges): an unprivileged account is enough. UI:N (no user interaction): the lock user's process acts on its own.
C:L (low confidentiality impact): through claims, one file name per planted link from a directory only the lock user can list. I:L (low integrity impact): a directory named claims and files with filelock's fixed claim names and contents appear, created with the lock user's privileges, in a directory the other user chooses but cannot write. The process overwrites or truncates nothing that exists. A:H (high availability impact): aimed at another strict lock of the lock user, the stranded claims deny that lock to every process until an operator finds and force-breaks each one. Classification from the review: security, conditional on the other user being able to rename the intermediate component. The scanner rated it high (CWE-363).
<path>.filelock. In a writable directory without the sticky bit (a 0777 or group-writable project or cache directory), any user with write permission on the directory can, even though the lock user created and owns <path>.filelock. In a sticky directory such as /tmp, only the user who created <path>.filelock before the lock user's first use can, because that user then owns both the directory and the symbolic link (the planting precondition of GHSA-9c4x-gm8v-wgc4).<path>.filelock can defeat that lock's exclusion anyway.fs.protected_symlinks=1 (a Linux setting that refuses to follow another user's symbolic link in a world-writable sticky directory) does not interfere. The link is an intermediate path component, not the last one. In the sticky-directory case it also sits in a directory the other user owns.SoftFileLockProtocolError or FileNotFoundError. The swap therefore works against a lock that a program takes more than once: a periodic job, a worker pool, or a caller that retries or waits with a timeout (each poll of a waiting acquisition is a new attempt).claims and force_break() on a resting symbolic link) depend on the lock user inspecting or recovering the lock. The planted link invites this by making fail with a protocol error. The variant works only when the lock user breaks claims that name a live process on its own host without checking them first, as the recovery loop in does. tells the operator to verify that the named owner is gone before breaking.filelock's checks against another local user hold only when no other user can replace the lock's directory or any of its ancestors. That is the case when each ancestor belongs to the lock user or root and is either not writable by others or sticky. A user who can rename entries in a non-sticky directory above the lock controls every name below it. No file-based lock can defend against that.
At the scanned commit b831496, the check and the first use are adjacent lines of StrictSoftFileLock._acquire. There, _ensure_protocol_directory runs lstat on <path>.filelock and then mkdir on <path>.filelock/claims. _open_directory passes O_DIRECTORY | O_NOFOLLOW but receives the full <path>.filelock/claims. The claim scan goes by pathname in _read_existing_claims (directory.exists()) and _list_claim_names (os.scandir(directory)). So do , , and the clock probe in . The and take the directory from by string concatenation, and [](
This page as .md · .json · in your agent: get_signal("CVE-2026-112938")
GitHub Advisory Database, CC-BY 4.0. Posts quoted belong to their authors.
acquire()force_break()docs/tutorials.rstdocs/how-to.rstdir_fd (file operations relative to an open directory handle), and on Windows the documented requirement is a lock directory that untrusted users cannot write._publish_record_require_exact_name