VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2025-71309

CVE-2025-71309

Description

In the Linux kernel, the following vulnerability has been resolved:

fs/ntfs3: fix deadlock in ni_read_folio_cmpr

Syzbot reported a task hung in ni_readpage_cmpr (now ni_read_folio_cmpr). This is caused by a lock inversion deadlock involving the inode mutex (ni_lock) and page locks.

Scenario: 1. Task A enters ntfs_read_folio() for page X. It acquires ni_lock. 2. Task A calls ni_read_folio_cmpr(), which attempts to lock all pages in the compressed frame (including page Y). 3. Concurrently, Task B (e.g., via readahead) has locked page Y and calls ntfs_read_folio(). 4. Task B waits for ni_lock (held by A). 5. Task A waits for page Y lock (held by B). -> DEADLOCK.

The fix is to restructure locking: do not take ni_lock in ntfs_read_folio(). Instead, acquire ni_lock inside ni_read_folio_cmpr() ONLY AFTER all required page locks for the frame have been successfully acquired. This restores the correct lock ordering (Page Lock -> ni_lock) consistent with VFS.

[almaz.alexandrovich@paragon-software.com: ni_readpage_cmpr was renamed to ni_read_folio_cmpr]

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A lock inversion deadlock in the Linux kernel's ntfs3 driver allows local attackers to hang the system by triggering concurrent reads on a compressed file.

Vulnerability

In the Linux kernel's ntfs3 driver, a deadlock exists in the ni_read_folio_cmpr function (formerly ni_readpage_cmpr) when reading compressed files. The deadlock occurs due to lock inversion: the function acquires the inode mutex (ni_lock) while holding a page lock, and then attempts to lock additional pages in the compressed frame. If another thread (e.g., via readahead) holds a different page lock and tries to acquire ni_lock, a deadlock results. Affected versions include all Linux kernel versions with the ntfs3 driver prior to the commits e37a75bb866c and cfe246b318106 [1][2].

Exploitation

An attacker with local access to the system can trigger the deadlock by causing concurrent read operations on the same compressed file. For example, task A reads a compressed page, locks it, and acquires ni_lock, then waits to lock another page in the frame. Concurrently, task B (e.g., via readahead) locks that other page and waits for ni_lock. This creates a circular wait. No special privileges beyond local file read access are required; the attacker can orchestrate the race by issuing multiple reads.

Impact

A successful exploitation results in a task hung, leading to a denial of service (DoS) on the affected system. The system may become unresponsive or require a reboot. There is no evidence of information disclosure or privilege escalation; the impact is limited to availability.

Mitigation

The vulnerability is fixed in Linux kernel commits e37a75bb866c and cfe246b318106 [1][2]. Users should update their kernel to a version containing these fixes or apply the patches. There is no known workaround. The ntfs3 driver may be disabled if not needed. No CVE listing in KEV at this time.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

4
cfe246b31810

fs/ntfs3: fix deadlock in ni_read_folio_cmpr

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSzymon WilczekDec 22, 2025Fixed in 6.19.4via kernel-cna
2 files changed · +3 3
  • fs/ntfs3/frecord.c+2 0 modified
    diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
    index 7e3d61de2f8fa7..d5bbd47e1ee9db 100644
    --- a/fs/ntfs3/frecord.c
    +++ b/fs/ntfs3/frecord.c
    @@ -2107,7 +2107,9 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
     		pages[i] = pg;
     	}
     
    +	ni_lock(ni);
     	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
    +	ni_unlock(ni);
     
     out1:
     	for (i = 0; i < pages_per_frame; i++) {
    
  • fs/ntfs3/inode.c+1 3 modified
    diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
    index 1319b99dfeb417..ec8e954f4426c0 100644
    --- a/fs/ntfs3/inode.c
    +++ b/fs/ntfs3/inode.c
    @@ -735,9 +735,8 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
     	}
     
     	if (is_compressed(ni)) {
    -		ni_lock(ni);
    +		/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
     		err = ni_read_folio_cmpr(ni, folio);
    -		ni_unlock(ni);
     		return err;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
e37a75bb866c

fs/ntfs3: fix deadlock in ni_read_folio_cmpr

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitSzymon WilczekDec 22, 2025Fixed in 7.0via kernel-cna
2 files changed · +3 3
  • fs/ntfs3/frecord.c+2 0 modified
    diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
    index 03dcb66b5f6c31..3025a404e69586 100644
    --- a/fs/ntfs3/frecord.c
    +++ b/fs/ntfs3/frecord.c
    @@ -2107,7 +2107,9 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
     		pages[i] = pg;
     	}
     
    +	ni_lock(ni);
     	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
    +	ni_unlock(ni);
     
     out1:
     	for (i = 0; i < pages_per_frame; i++) {
    
  • fs/ntfs3/inode.c+1 3 modified
    diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
    index ace9873adaaef1..4b50fdb4ff470e 100644
    --- a/fs/ntfs3/inode.c
    +++ b/fs/ntfs3/inode.c
    @@ -748,9 +748,8 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
     	}
     
     	if (is_compressed(ni)) {
    -		ni_lock(ni);
    +		/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
     		err = ni_read_folio_cmpr(ni, folio);
    -		ni_unlock(ni);
     		return err;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
cfe246b31810

fs/ntfs3: fix deadlock in ni_read_folio_cmpr

2 files changed · +3 3
  • fs/ntfs3/frecord.c+2 0 modified
    diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
    index 7e3d61de2f8fa7..d5bbd47e1ee9db 100644
    --- a/fs/ntfs3/frecord.c
    +++ b/fs/ntfs3/frecord.c
    @@ -2107,7 +2107,9 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
     		pages[i] = pg;
     	}
     
    +	ni_lock(ni);
     	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
    +	ni_unlock(ni);
     
     out1:
     	for (i = 0; i < pages_per_frame; i++) {
    
  • fs/ntfs3/inode.c+1 3 modified
    diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
    index 1319b99dfeb417..ec8e954f4426c0 100644
    --- a/fs/ntfs3/inode.c
    +++ b/fs/ntfs3/inode.c
    @@ -735,9 +735,8 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
     	}
     
     	if (is_compressed(ni)) {
    -		ni_lock(ni);
    +		/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
     		err = ni_read_folio_cmpr(ni, folio);
    -		ni_unlock(ni);
     		return err;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    
e37a75bb866c

fs/ntfs3: fix deadlock in ni_read_folio_cmpr

2 files changed · +3 3
  • fs/ntfs3/frecord.c+2 0 modified
    diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
    index 03dcb66b5f6c31..3025a404e69586 100644
    --- a/fs/ntfs3/frecord.c
    +++ b/fs/ntfs3/frecord.c
    @@ -2107,7 +2107,9 @@ int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
     		pages[i] = pg;
     	}
     
    +	ni_lock(ni);
     	err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
    +	ni_unlock(ni);
     
     out1:
     	for (i = 0; i < pages_per_frame; i++) {
    
  • fs/ntfs3/inode.c+1 3 modified
    diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c
    index ace9873adaaef1..4b50fdb4ff470e 100644
    --- a/fs/ntfs3/inode.c
    +++ b/fs/ntfs3/inode.c
    @@ -748,9 +748,8 @@ static int ntfs_read_folio(struct file *file, struct folio *folio)
     	}
     
     	if (is_compressed(ni)) {
    -		ni_lock(ni);
    +		/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
     		err = ni_read_folio_cmpr(ni, folio);
    -		ni_unlock(ni);
     		return err;
     	}
     
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Lock inversion deadlock: `ntfs_read_folio()` acquires `ni_lock` before calling `ni_read_folio_cmpr()`, which then tries to lock pages, violating the required VFS lock ordering (page lock first, then inode lock)."

Attack vector

An attacker who can trigger concurrent read operations on a compressed NTFS file (e.g., via normal file reads and readahead) can cause a lock inversion deadlock. Task A enters `ntfs_read_folio()` for page X and acquires `ni_lock`. It then calls `ni_read_folio_cmpr()`, which tries to lock all pages in the compressed frame, including page Y. Meanwhile, Task B (e.g., triggered by readahead) has already locked page Y and calls `ntfs_read_folio()`, which blocks waiting for `ni_lock` held by Task A. Task A waits for page Y's lock held by Task B, resulting in a deadlock [patch_id=2662177]. No special privileges beyond the ability to read files on an NTFS3 filesystem are required.

Affected code

The deadlock involves two functions: `ntfs_read_folio()` in `fs/ntfs3/inode.c` and `ni_read_folio_cmpr()` in `fs/ntfs3/frecord.c`. In the vulnerable code, `ntfs_read_folio()` acquires `ni_lock` before calling `ni_read_folio_cmpr()`, which then attempts to lock pages for the compressed frame. The patch moves the `ni_lock`/`ni_unlock` calls from `ntfs_read_folio()` into `ni_read_folio_cmpr()`, placing them after all page locks have been acquired [patch_id=2662177].

What the fix does

The patch removes the `ni_lock(ni)` and `ni_unlock(ni)` calls from `ntfs_read_folio()` in `fs/ntfs3/inode.c` and instead adds `ni_lock(ni)` / `ni_unlock(ni)` inside `ni_read_folio_cmpr()` in `fs/ntfs3/frecord.c`, placed after the loop that acquires all page locks for the compressed frame [patch_id=2662177]. This restores the correct VFS lock ordering: page locks are acquired first, then `ni_lock`. By ensuring `ni_lock` is never held while waiting for a page lock, the classic lock inversion deadlock between two concurrent readers is eliminated.

Preconditions

  • configThe target filesystem must be NTFS3 with compressed files (is_compressed(ni) returns true)
  • inputAn attacker must be able to trigger concurrent read operations on the same compressed file (e.g., normal reads and readahead)
  • authNo special privileges beyond file read access on the NTFS3 filesystem are required

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

2

News mentions

0

No linked articles in our index yet.