From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Cong Zhang <cong.zhang@oss.qualcomm.com>,
Jason Wang <jasowang@redhat.com>, Jens Axboe <axboe@kernel.dk>,
Sasha Levin <sashal@kernel.org>,
mst@redhat.com, virtualization@lists.linux.dev,
linux-block@vger.kernel.org
Subject: [PATCH AUTOSEL 6.18-6.12] virtio_blk: NULL out vqs to avoid double free on failed resume
Date: Mon, 8 Dec 2025 19:15:10 -0500 [thread overview]
Message-ID: <20251209001610.611575-18-sashal@kernel.org> (raw)
In-Reply-To: <20251209001610.611575-1-sashal@kernel.org>
From: Cong Zhang <cong.zhang@oss.qualcomm.com>
[ Upstream commit 0739c2c6a015604a7c01506bea28200a2cc2e08c ]
The vblk->vqs releases during freeze. If resume fails before vblk->vqs
is allocated, later freeze/remove may attempt to free vqs again.
Set vblk->vqs to NULL after freeing to avoid double free.
Signed-off-by: Cong Zhang <cong.zhang@oss.qualcomm.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Good. `kfree(NULL)` is safe - it does nothing. This is a well-known
C/Linux convention.
Now let me create my final comprehensive analysis:
## COMPLETE ANALYSIS
### 1. COMMIT MESSAGE ANALYSIS
**Subject**: `virtio_blk: NULL out vqs to avoid double free on failed
resume`
**Key points from commit message:**
- The `vblk->vqs` is released during freeze
- If resume fails before `vblk->vqs` is allocated, later freeze/remove
may attempt to free vqs again
- Solution: Set `vblk->vqs` to NULL after freeing to avoid double free
**Acks/Reviews:**
- Acked-by: Jason Wang <jasowang@redhat.com> (virtio maintainer)
- Signed-off-by: Jens Axboe <axboe@kernel.dk> (block subsystem
maintainer)
**Missing tags:**
- No `Cc: stable@vger.kernel.org` tag
- No `Fixes:` tag explicitly pointing to the bug-introducing commit
### 2. CODE CHANGE ANALYSIS
**Changes made:** Two modifications in `drivers/block/virtio_blk.c`:
#### Change 1: In `init_vq()` error path (lines 1029-1032)
**Before:**
```c
if (err)
kfree(vblk->vqs);
return err;
```
**After:**
```c
if (err) {
kfree(vblk->vqs);
/*
- Set to NULL to prevent freeing vqs again during freezing.
*/
vblk->vqs = NULL;
}
return err;
```
#### Change 2: In `virtblk_freeze_priv()` (lines 1599-1600)
**Before:**
```c
vdev->config->del_vqs(vdev);
kfree(vblk->vqs);
return 0;
```
**After:**
```c
vdev->config->del_vqs(vdev);
kfree(vblk->vqs);
/*
- Set to NULL to prevent freeing vqs again after a failed vqs
- allocation during resume. Note that kfree() already handles NULL
- pointers safely.
*/
vblk->vqs = NULL;
return 0;
```
### 3. BUG MECHANISM (Root Cause Analysis)
The double-free vulnerability occurs in the following scenario:
**Trigger Sequence:**
1. **virtblk_freeze_priv()** is called (suspend/PM freeze, or
reset_prepare via FLR)
- Frees `vblk->vqs` at line 1600
- `vblk->vqs` **still points to the freed memory** (dangling pointer)
2. **virtblk_restore_priv()** is called (resume/PM restore, or
reset_done)
- Calls `init_vq(vblk)` at line 1610
3. **init_vq()** fails (e.g., `kmalloc_array()` fails or
`virtio_find_vqs()` fails)
- `init_vq()` allocates `vblk->vqs` at line 993
- If allocation succeeds but later `virtio_find_vqs()` fails (line
1016), the error path at line 1030 calls `kfree(vblk->vqs)`
- But if allocation at line 993 fails, `vblk->vqs` is never
reassigned and still points to the OLD freed memory from step 1
- Error path at line 1030: `kfree(vblk->vqs)` - **FIRST FREE of the
OLD pointer**
4. **Second freeze/remove attempt:**
- If another freeze cycle or `virtblk_remove()` is called
- `kfree(vblk->vqs)` is called again - **SECOND FREE of the same
memory = DOUBLE FREE**
**Alternative scenario:**
- Even in `init_vq()` success path, if `vqs_info` or `vqs` temp
allocation fails before line 997-999, and the error `goto out` is hit,
the same dangling pointer issue occurs.
### 4. CLASSIFICATION
- **Type**: Bug fix (memory safety - double-free vulnerability)
- **Security relevance**: Potentially exploitable memory corruption bug
- **Category**: Does NOT fall into exceptions (device IDs, quirks, DT,
build fixes)
- **Impact area**: virtio-blk block device driver, PM suspend/resume and
transport reset recovery
### 5. SCOPE AND RISK ASSESSMENT
**Lines changed**: ~10 lines (including comments)
**Files touched**: 1 file (`drivers/block/virtio_blk.c`)
**Complexity**: Very low - simple NULL assignment after kfree
**Subsystem**: virtio-blk - a mature, widely-used block device driver
for virtual machines
- Used in QEMU/KVM guests
- Used in cloud VM instances (AWS, GCP, Azure etc.)
- Used in container environments
**Risk assessment**: **VERY LOW**
- The fix is trivial: just setting pointer to NULL after free
- `kfree(NULL)` is explicitly safe (no-op)
- No behavioral change in normal operation
- Only affects error recovery paths
- Cannot introduce new bugs
### 6. USER IMPACT
**Who is affected:**
- Users using virtio-blk devices in virtual machines
- Systems that undergo suspend/resume cycles
- Systems using PCI Function Level Reset (FLR) on virtio devices
- Particularly affects systems with constrained memory where allocation
might fail
**Severity if bug hits:**
- Double-free can cause kernel panic/crash
- Potential memory corruption
- Potential security vulnerability (though exploitation would be
difficult)
**Trigger conditions:**
- Requires PM suspend/resume OR transport reset (FLR)
- AND memory allocation failure during resume
- Relatively rare in practice but can happen under memory pressure
### 7. STABILITY INDICATORS
**Positive indicators:**
- Acked-by: Jason Wang (virtio maintainer)
- Signed-off-by: Jens Axboe (block maintainer)
- The fix is obviously correct and minimal
- Follows the standard kernel pattern of NULLing after free
**Negative indicators:**
- No `Cc: stable@vger.kernel.org` tag
- No `Fixes:` tag
- No `Tested-by:` tag
### 8. DEPENDENCY CHECK
**Dependencies:**
- Requires commit `5820a3b089879` ("virtio_blk: Add support for
transport error recovery") which introduced `virtblk_freeze_priv()`
and `virtblk_restore_priv()` as shared functions
- This commit (`5820a3b089879`) was merged in v6.14-rc1
**Earlier bug introduction:**
- The original bug was introduced in `b71ba22e7c6c6` ("virtio-blk: Fix
memory leak among suspend/resume procedure") in v5.14-rc1
- That commit added `kfree(vblk->vqs)` in freeze without NULLing it
**For stable backport:**
- For kernels v6.14+: should apply cleanly
- For kernels v5.14 to v6.13: would need different backport (the code
structure is different)
- The `virtblk_freeze_priv` function only exists starting from v6.14
### 9. CONCLUSION
**This commit SHOULD be backported to stable trees** because:
1. **Fixes a real bug**: Double-free is a serious memory safety bug that
can cause crashes and potentially be exploited
2. **Obvious correctness**: Setting pointer to NULL after free is the
standard kernel idiom and cannot introduce new bugs
3. **Small and contained**: Only ~10 lines changed, very localized to
two specific error handling paths
4. **No new features**: Pure bug fix with no behavioral changes in
normal operation
5. **Affects important infrastructure**: virtio-blk is used extensively
in cloud/virtualization environments
6. **Low risk**: The fix is trivial and follows standard patterns;
`kfree(NULL)` is explicitly safe
**Caveats for backporting:**
- The fix in its current form is ONLY applicable to v6.14+ kernels where
`virtblk_freeze_priv()` exists
- For older stable kernels (v5.14 to v6.13), a different (simpler)
backport would be needed that just NULLs `vblk->vqs` in
`virtblk_freeze()` directly
- The maintainers did not explicitly tag this for stable (`Cc:
stable@vger.kernel.org`), which might indicate they didn't consider
backporting, or the bug wasn't seen as critical
The fix is small, surgical, obviously correct, and addresses a real
memory safety bug. It meets all stable kernel criteria.
**YES**
drivers/block/virtio_blk.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index f061420dfb10c..746795066d7f5 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -1026,8 +1026,13 @@ static int init_vq(struct virtio_blk *vblk)
out:
kfree(vqs);
kfree(vqs_info);
- if (err)
+ if (err) {
kfree(vblk->vqs);
+ /*
+ * Set to NULL to prevent freeing vqs again during freezing.
+ */
+ vblk->vqs = NULL;
+ }
return err;
}
@@ -1598,6 +1603,12 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
vdev->config->del_vqs(vdev);
kfree(vblk->vqs);
+ /*
+ * Set to NULL to prevent freeing vqs again after a failed vqs
+ * allocation during resume. Note that kfree() already handles NULL
+ * pointers safely.
+ */
+ vblk->vqs = NULL;
return 0;
}
--
2.51.0
next prev parent reply other threads:[~2025-12-09 0:17 UTC|newest]
Thread overview: 45+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-09 0:14 [PATCH AUTOSEL 6.18-6.1] ksmbd: fix use-after-free in ksmbd_tree_connect_put under concurrency Sasha Levin
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.17] wifi: rtw89: use skb_dequeue() for queued ROC packets to prevent racing Sasha Levin
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.6] ipv6: clean up routes when manually removing address with a lifetime Sasha Levin
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-5.10] ext4: remove page offset calculation in ext4_block_zero_page_range() Sasha Levin
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.6] fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list Sasha Levin
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.6] btrfs: abort transaction on item count overflow in __push_leaf_left() Sasha Levin
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.1] smb/server: fix return value of smb2_ioctl() Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.1] gfs2: Fix use of bio_chain Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] Bluetooth: btusb: Add new VID/PID 13d3/3533 for RTL8821CE Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] wifi: mac80211: reset CRC valid after CSA Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] Bluetooth: btusb: Add new VID/PID 0x0489/0xE12F for RTL8852BE-VT Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] wifi: mt76: mmio_*_copy fix byte order and alignment Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] btrfs: scrub: always update btrfs_scrub_progress::last_physical Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] bpf: Skip bounds adjustment for conditional jumps on same scalar register Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] wifi: rtl8xxxu: Fix HT40 channel config for RTL8192CU, RTL8723AU Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] Bluetooth: btusb: MT7920: Add VID/PID 0489/e135 Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] Bluetooth: btusb: MT7922: Add VID/PID 0489/e170 Sasha Levin
2025-12-09 0:15 ` Sasha Levin [this message]
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.1] kbuild: Use objtree for module signing key path Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.17] btrfs: use kvcalloc for btrfs_bio::csum allocation Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] net: sched: Don't use WARN_ON_ONCE() for -ENOMEM in tcf_classify() Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] hfsplus: Verify inode mode when loading from disk Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.6] gfs2: fix remote evict for read-only filesystems Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] net: amd-xgbe: use EOPNOTSUPP instead of ENOTSUPP in xgbe_phy_mii_read_c45 Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] net: init shinfo->gso_segs from qdisc_pkt_len_init() Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.17] Bluetooth: btusb: add new custom firmwares Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] hfsplus: fix missing hfs_bnode_get() in __hfs_bnode_create Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] cxgb4: Rename sched_class to avoid type clash Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] net: mana: Drop TX skb on post_work_request failure and unmap resources Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] hfsplus: fix volume corruption issue for generic/070 Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.17] wifi: rtw89: rtw8852bu: Added dev id for ASUS AX57 NANO USB Wifi dongle Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] net: restore napi_consume_skb()'s NULL-handling Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.15] fs/ntfs3: Support timestamps prior to epoch Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.1] smb/server: fix return value of smb2_query_dir() Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.17] wifi: rtw88: Add BUFFALO WI-U3-866DHP to the USB ID list Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.6] Bluetooth: btusb: Add new VID/PID 2b89/6275 for RTL8761BUV Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] bpf: Disable file_alloc_security hook Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.1] wifi: rtw89: phy: fix out-of-bounds access in rtw89_phy_read_txpwr_limit() Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.6] ntfs: set dummy blocksize to read boot_block when mounting Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.10] hfsplus: fix volume corruption issue for generic/073 Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] wifi: mt76: mt792x: fix wifi init fail by setting MCU_RUNNING after CLC load Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] gfs2: Fix "gfs2: Switch to wait_event in gfs2_quotad" Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.6] ksmbd: vfs: fix race on m_flags in vfs_cache Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.1] wifi: rtw89: flush TX queue before deleting key Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.12] ice: Allow 100M speed for E825C SGMII device Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251209001610.611575-18-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=axboe@kernel.dk \
--cc=cong.zhang@oss.qualcomm.com \
--cc=jasowang@redhat.com \
--cc=linux-block@vger.kernel.org \
--cc=mst@redhat.com \
--cc=patches@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox