Oct 23, 2026·6 min read·6 visits
filelock versions 4.1.0-4.2.0 track stale locks using a tuple of inode number and modification time. An attacker can manipulate these properties on shared filesystems to trick waiters into deleting live locks, causing concurrent critical section access.
A local race condition and mutual exclusion bypass vulnerability exists in filelock versions 4.1.0 through 4.2.0. On Linux filesystems with rapid inode reuse, a local attacker can manipulate modification timestamps and lock files to cause waiting processes to falsely evict active soft lock markers.
The Python filelock library provides platform-independent file locking mechanisms utilized extensively across the Python package ecosystem. On POSIX systems where platform native file locking mechanisms like flock or fcntl are unavailable or configured to fall back to soft file locking primitives, filelock utilizes disk-based lock markers represented by SoftFileLock, MarkerSoftFileLock, and SoftFileLease objects.
Soft file locking relies on the presence of a specific marker file on disk to represent an active hold on a critical resource. To prevent deadlocks resulting from ungracefully terminated owner processes, the library implements a self-healing mechanism designed to detect stale lock markers. If a lock marker remains unchanged beyond a specified time threshold, contender processes assume the lock owner is inactive and automatically evict the marker file.
In versions 4.1.0 through 4.2.0, the heuristic used to identify unchanged lock files relied exclusively on a composite key of modification time (mtime) and inode number (ino). When deployed in shared environments such as /tmp on POSIX systems using filesystems with aggressive inode recycling (such as Linux ext4 or overlayfs), this identification mechanism fails. A local adversary can force new, legitimate lock files to match previously observed (mtime, ino) tuples, causing waiting processes to evict live lock markers and breaking mutual exclusion guarantees.
The primary flaw stems from relying on (mtime, ino) as a globally unique file identity key across time intervals without accounting for file descriptor lifetime or kernel metadata modification semantics. On POSIX filesystems, unprivileged local users possessing write permission to the lock directory can arbitrarily modify a file's mtime timestamp using system calls like utime() or utimensat().
When a file is unlinked on systems using ext4 or overlayfs, its allocated inode number $I$ is immediately returned to the allocator pool. Subsequent file creation operations within the same directory frequently reuse the newly freed inode number $I$. If the new file creation occurs within the same clock tick or timestamp interval $T$ manipulated by the adversary, the newly generated lock marker inherits the identical (mtime, ino) key combination $(T, I)$.
Because filelock tracked state across polling loops solely via this tuple, a contender process monitoring the lock path compares the new lock file against its cached observation state. Finding identical values for mtime and ino, the contender incorrectly calculates that the file has been unreferenced for longer than the stale lock threshold. Consequently, the contender process issues an eviction request against a newly created, valid lock file currently held by another active process.
Prior to version 4.2.1, filelock tracked soft lock files using a simple state comparison method in src/filelock/_soft.py. The marker comparison logic only inspected st_mtime and st_ino from os.stat_result structures during state checks.
# Vulnerable tracking implementation in filelock <= 4.2.0
def _marker_unchanged_for(self, marker: os.stat_result) -> float:
now: Final = time.monotonic()
key: Final = marker.st_mtime, marker.st_ino # Vulnerable key tuple
if (seen := self._marker_seen) is None or seen[0] != key:
self._marker_seen = key, now
return 0.0
return now - seen[1]In patch commit 740ff666b62a88496c8d7394e4e6230605e4d62b, the core tracking function was modified to incorporate the kernel-managed change time (st_ctime_ns). While users can freely manipulate mtime, ctime is strictly controlled by the operating system kernel and reflects metadata changes, ensuring newly allocated files receive a distinct timestamp regardless of inode recycling.
# Patched implementation in filelock >= 4.2.1
def _marker_unchanged_for(self, marker: os.stat_result) -> float:
now: Final = time.monotonic()
# Fix: Added kernel-managed st_ctime_ns to key definition
key: Final = marker.st_mtime, marker.st_ino, marker.st_ctime_ns
if (seen := self._marker_seen) is None or seen[0] != key:
self._marker_seen = key, now
return 0.0
return now - seen[1]In addition to adding st_ctime_ns, the patch introduced inode pinning via open file descriptors (O_PATH / O_RDONLY with O_NOFOLLOW) to prevent inode reuse during state evaluation, alongside atomic filesystem exchange operations (renameat2 with RENAME_EXCHANGE on Linux) when evicting stale lock markers.
An exploitation scenario requires local access to the system and a shared directory where filelock soft lock files are generated (e.g., /tmp). The attacker does not require administrative privileges or special access beyond standard user file creation and modification rights in the shared directory.
To trigger the condition, the attacker creates a temporary file in the target lock directory and uses utime() to pre-date its modification time to a timestamp $T$. The attacker then unlinks the temporary file to return inode $I$ to the filesystem allocator pool. Contender processes actively polling the directory observe state $(T, I)$ during their loop iterations.
When a legitimate process attempts to acquire the lock, it creates a new marker file. On ext4 or overlayfs, the kernel assigns the newly freed inode $I$ to the file. If creation completes during timestamp interval $T$, the new file assumes key $(T, I)$. A waiting process checking the file matches the key against its stale cache record, concludes the lock is expired, and deletes the live lock marker. A secondary waiting process then creates a lock file in the vacant path, allowing two processes to enter the critical section simultaneously.
Exploitation of CVE-2026-112906 results in a complete loss of mutual exclusion integrity for applications relying on SoftFileLock, MarkerSoftFileLock, or SoftFileLease. When two processes enter protected critical sections simultaneously, data corruption, state inconsistency, and transactional race conditions occur.
The CVSS v3.1 vector is rated at 4.7 Medium (CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N). High attack complexity (AC:H) reflects the dependency on timing windows and filesystem-specific inode allocation behaviors. Local low-privileged access (AV:L/PR:L) is required.
This vulnerability does not directly expose read access to confidential memory or grant elevated privilege escalation on the host system. However, applications relying on file locking to synchronize file writes, database updates, or cache generation risk severe data corruption if compromised.
The primary remediation for CVE-2026-112906 is upgrading the filelock package to version 4.2.1 or higher. The fixed release includes kernel ctime tracking, open file descriptor pinning, and atomic lock swaps.
System administrators and application developers can apply dependency updates using standard Python package management tools:
pip install --upgrade "filelock>=4.2.1"In environments where upgrading dependencies is delayed, application architectures should avoid utilizing global shared paths (such as /tmp or /var/tmp) for lock files. Configuring application lock directories within restricted, user-private paths (such as XDG_RUNTIME_DIR with 0700 permissions) prevents unprivileged local users from injecting pre-dated files or observing lock operations.
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
filelock tox-dev | >= 4.1.0, < 4.2.1 | 4.2.1 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-112906 / GHSA-2g95-wg33-c5x2 |
| Weakness Enumeration | CWE-694 (Duplicate Identifier / Inode Race) |
| CVSS v3.1 Score | 4.7 (Medium) |
| EPSS Score | 0.00205 (10.01th percentile) |
| CISA KEV Status | Not Listed |
| Exploit Maturity | Proof of Concept / Test Suite |
| Impact | Mutual Exclusion Bypass / Critical Section Race Condition |
The software reuses or relies on identifiers that are not guaranteed to be unique across file allocations, leading to resource misidentification and race conditions.
A Time-of-Check Time-of-Use (TOCTOU) race condition in the Python `filelock` library (versions 4.1.0 to 4.2.0) allows local unprivileged attackers to cause arbitrary directory deletion by swapping file system targets during lock cleanup or dead owner sweeps.
vLLM prior to version 0.31.0 contains an uncontrolled resource consumption vulnerability (CWE-400) in its HTTP API request handling logic. Unauthenticated remote attackers can transmit per-request keyword arguments (`mm_processor_kwargs` and `media_io_kwargs`) that are passed directly into internal multimodal preprocessing pipelines, causing CPU/GPU memory exhaustion and crashing the engine process.
An insufficient verification of data authenticity vulnerability in SiYuan prior to version 3.7.4 allows an attacker to overwrite arbitrary installed marketplace packages (plugins, widgets, themes, and templates). By providing a mismatched package name and package archive repository URL during a Bazaar installation request, arbitrary file write into trusted plugin directories can be achieved, enabling persistence and execution across application restarts.
In obsidian-tc versions 1.0.1 through 1.31.3, memory lifecycle tools fail to validate note ownership prior to rematerializing Markdown files and execute non-atomic SQLite database mutations. An authorized caller can overwrite arbitrary note bodies on disk and cause persistent desynchronization between the database memory graph and physical vault files.
A high-severity authorization bypass vulnerability in obsidian-tc allows clients to bypass Human-In-The-Loop (HITL) confirmation prompts over Streamable HTTP deployments. Due to incomplete validation of returned prompt responses, an attacker echoing a signed requestState token can force execution of destructive vault operations even if a human user declines or cancels the prompt.
A Denial of Service (DoS) vulnerability in pypdf versions prior to 6.21.0 allows remote attackers to cause high CPU and memory resource exhaustion by supplying PDF files containing oversized or deeply nested arrays, dictionaries, or content streams.