CVE-2026-45984
Description
In the Linux kernel, the following vulnerability has been resolved:
gfs2: Fix use-after-free in iomap inline data write path
The inline data buffer head (dibh) is being released prematurely in gfs2_iomap_begin() via release_metapath() while iomap->inline_data still points to dibh->b_data. This causes a use-after-free when iomap_write_end_inline() later attempts to write to the inline data area.
The bug sequence: 1. gfs2_iomap_begin() calls gfs2_meta_inode_buffer() to read inode metadata into dibh 2. Sets iomap->inline_data = dibh->b_data + sizeof(struct gfs2_dinode) 3. Calls release_metapath() which calls brelse(dibh), dropping refcount to 0 4. kswapd reclaims the page (~39ms later in the syzbot report) 5. iomap_write_end_inline() tries to memcpy() to iomap->inline_data 6. KASAN detects use-after-free write to freed memory
Fix by storing dibh in iomap->private and incrementing its refcount with get_bh() in gfs2_iomap_begin(). The buffer is then properly released in gfs2_iomap_end() after the inline write completes, ensuring the page stays alive for the entire iomap operation.
Note: A C reproducer is not available for this issue. The fix is based on analysis of the KASAN report and code review showing the buffer head is freed before use.
[agruenba: Take buffer head reference in gfs2_iomap_begin() to avoid leaks in gfs2_iomap_get() and gfs2_iomap_alloc().]
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A use-after-free in GFS2's inline data write path lets local attackers crash the kernel or potentially escalate privileges.
Vulnerability
The Linux kernel's GFS2 filesystem contains a use-after-free vulnerability in the inline data write path, introduced in commit 87d4954b5c59735a99ea98cb208d47130f6dce7d. In gfs2_iomap_begin(), the inline data buffer head (dibh) is released via release_metapath() before the iomap operation completes. The iomap->inline_data pointer still references dibh->b_data, leading to a use-after-free when iomap_write_end_inline() subsequently writes to that pointer. The issue affects all Linux kernel versions containing this commit, which was backported to stable branches. [1]
Exploitation
An attacker needs local access to a system with a GFS2 filesystem mounted and the ability to trigger inline data writes (e.g., writing small files that fit within the inode). No special privileges beyond a regular user account are required. The race window between the buffer release and the write operation is short (approximately 39 ms as observed), but an attacker can repeatedly trigger the write path to increase the likelihood of exploiting the freed memory. The KASAN report confirms the condition is reproducible without a dedicated C reproducer. [1]
Impact
Successful exploitation results in a use-after-free write to freed kernel memory. This can cause a system crash (denial of service) due to memory corruption, or potentially lead to privilege escalation if the attacker can control the contents of the freed memory. The highest impact is on system availability, and in certain configurations, confidentiality or integrity may be compromised. [1]
Mitigation
The fix is included in kernel commits that properly retain the buffer head reference by storing dibh in iomap->private and calling get_bh() in gfs2_iomap_begin(), then releasing it in gfs2_iomap_end(). All stable kernels that received the vulnerable commit have been updated with the fix. Users should apply the latest kernel updates from their distribution. No workaround exists; the vulnerable code path is inherent to the inline data write mechanism. The CVE is not listed on CISA's Known Exploited Vulnerabilities catalog as of publication. [1]
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
1Patches
16764c3c84b568gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 9f0dec17fa4318..8a17c3bad4ca05 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1112,10 +1112,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1129,6 +1137,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
d87268326b27gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index b837a2c2dd571f..bc0f7023adcf33 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1126,10 +1126,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1143,6 +1151,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
1403989d1b50gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 1e0e350b5a5a54..10b0151b2789d7 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1115,10 +1115,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1132,6 +1140,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
1cae1bafdf9cgfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 7425c90e47eb55..1e95d777f67370 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1114,10 +1114,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1131,6 +1139,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
87d4954b5c59gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 28ad07b0034844..776090fbc9aa5f 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1124,10 +1124,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1141,6 +1149,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
6d76febba07cgfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6be..fdcac8e3f2ba26 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
815ddd27c0c7gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6be..fdcac8e3f2ba26 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
faddeb848305gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6be..fdcac8e3f2ba26 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
d87268326b27gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index b837a2c2dd571f..bc0f7023adcf33 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1126,10 +1126,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1143,6 +1151,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
faddeb848305gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6be..fdcac8e3f2ba26 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
1403989d1b50gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 1e0e350b5a5a54..10b0151b2789d7 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1115,10 +1115,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1132,6 +1140,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
1cae1bafdf9cgfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 7425c90e47eb55..1e95d777f67370 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1114,10 +1114,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1131,6 +1139,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
6d76febba07cgfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6be..fdcac8e3f2ba26 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
764c3c84b568gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 9f0dec17fa4318..8a17c3bad4ca05 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1112,10 +1112,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1129,6 +1137,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
815ddd27c0c7gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 131091520de6be..fdcac8e3f2ba26 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1127,10 +1127,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1144,6 +1152,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
87d4954b5c59gfs2: Fix use-after-free in iomap inline data write path
1 file changed · +12 −2
fs/gfs2/bmap.c+12 −2 modifieddiff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 28ad07b0034844..776090fbc9aa5f 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1124,10 +1124,18 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, goto out_unlock; break; default: - goto out_unlock; + goto out; } ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + +out: + if (iomap->type == IOMAP_INLINE) { + iomap->private = metapath_dibh(&mp); + get_bh(iomap->private); + } out_unlock: release_metapath(&mp); @@ -1141,6 +1149,9 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + if (iomap->private) + brelse(iomap->private); + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { case IOMAP_WRITE: if (flags & IOMAP_DIRECT) -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Premature release of the inline data buffer head (dibh) in gfs2_iomap_begin() via release_metapath() while iomap->inline_data still points to dibh->b_data, causing a use-after-free when iomap_write_end_inline() later writes to the freed memory."
Attack vector
An attacker triggers a write to a GFS2 inode that uses inline data storage. In gfs2_iomap_begin(), the function reads the inode metadata buffer head (dibh) via gfs2_meta_inode_buffer() and sets iomap->inline_data to point into dibh->b_data. It then calls release_metapath(), which calls brelse(dibh) and drops the buffer's refcount to zero. The kernel's kswapd can reclaim the underlying page (~39ms later in the syzbot report). When iomap_write_end_inline() subsequently performs a memcpy() to iomap->inline_data, it writes to freed memory, which KASAN detects as a use-after-free write. No special privileges beyond the ability to write to a GFS2 filesystem are required.
Affected code
The vulnerability is in fs/gfs2/bmap.c, in the functions gfs2_iomap_begin() and gfs2_iomap_end() [patch_id=2660715]. The bug occurs when the inline data path is taken: gfs2_iomap_begin() sets iomap->inline_data to point into the buffer head's data area but then releases that buffer head via release_metapath() before the iomap write completes.
What the fix does
The patch stores the dibh pointer in iomap->private and increments its reference count with get_bh() before release_metapath() is called [patch_id=2660715]. This prevents the buffer head from being freed prematurely. In gfs2_iomap_end(), the patch adds a corresponding brelse(iomap->private) to release the reference after the inline write completes [patch_id=2660715]. The fix also adjusts the control flow so that the dibh reference is taken even on the error path (the default case now jumps to the new "out" label instead of "out_unlock"), ensuring the buffer head is properly tracked for all IOMAP_INLINE exits.
Preconditions
- inputThe attacker must be able to perform write operations on a GFS2 filesystem that uses inline data storage for inodes.
- configThe filesystem must be mounted and the target inode must be small enough to use inline data (data stored within the inode block itself).
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- git.kernel.org/stable/c/1403989d1b502f4a2c0d0b42ccf1c25748442effnvd
- git.kernel.org/stable/c/1cae1bafdf9caa9b462b19af06b1a06902e4e142nvd
- git.kernel.org/stable/c/6d76febba07c40bcf358f63216d36ea68cf1c215nvd
- git.kernel.org/stable/c/764c3c84b5683e608f43735c803a5f415046686cnvd
- git.kernel.org/stable/c/815ddd27c0c7171a99fe802fdb19098ddef8b19dnvd
- git.kernel.org/stable/c/87d4954b5c59735a99ea98cb208d47130f6dce7dnvd
- git.kernel.org/stable/c/d87268326b277af3665237ac76a73dd9fa8e21b4nvd
- git.kernel.org/stable/c/faddeb848305e79db89ee0479bb0e33380656321nvd
News mentions
0No linked articles in our index yet.