| 0/0 |
2026/09/15 02:47 |
flow |
assessment-security |
35m
Results: map[DenialOfService:false Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the security impact:
**1. Bug Mechanism & Exploitability:**
The bug is a lockdep warning (`WARNING: suspicious RCU usage`) triggered in
`ublk_try_buf_match()` when it calls `mas_walk(&mas)` to traverse the
`ub->buf_tree` maple tree. The warning occurs because the function does not
explicitly hold the RCU read lock (`rcu_read_lock()`) or the maple tree lock
during the traversal.
However, this is a false positive in terms of actual memory safety. Write
modifications to `ub->buf_tree` (such as adding or removing buffers) are
protected by `ublk_lock_buf_tree()`, which calls `blk_mq_freeze_queue()`.
Freezing the queue ensures that all in-flight block requests are fully drained
before any modifications can proceed. Since `ublk_try_buf_match()` is executed
as part of dispatching an in-flight request, it is guaranteed that no concurrent
writes to the maple tree can occur while it is running.
Because there is no actual data race or memory corruption, the bug is **not
exploitable**.
**2. Denial of Service (DoS):**
In production kernel builds, debug options like `CONFIG_PROVE_RCU` (lockdep) are
disabled. Therefore, this warning will not even be evaluated or printed, and it
will not cause a kernel panic, hang, or resource leak. The system will continue
to operate normally. Thus, it does not pose a Denial of Service risk.
**3. Accessibility:**
To trigger this code path, a user must interact with the `/dev/ublk-control`
character device to set up a userspace block device and configure it with the
`UBLK_F_SHMEM_ZC` flag. By default, standard Linux distributions restrict access
to `/dev/ublk-control` to the `root` user (or a privileged group like `disk`).
While the kernel code does have provisions for unprivileged device creation
(`UBLK_F_UNPRIVILEGED_DEV`), it still fundamentally relies on the user having
permission to open the device node in the first place. Therefore, it is
typically **not accessible from unprivileged processes or user namespaces**.
**Conclusion:**
The bug is a benign missing lockdep annotation (missing `rcu_read_lock()`) that
does not lead to any memory corruption or system instability. It has no security
impact on production environments. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/09/15 02:47 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#ifndef __NR_io_uring_enter
#define __NR_io_uring_enter 426
#endif
#define BITMASK(bf_off,bf_len) (((1ull << (bf_len)) - 1) << (bf_off))
#define STORE_BY_BITMASK(type,htobe,addr,val,bf_off,bf_len) *(type*)(addr) = htobe((htobe(*(type*)(addr)) & ~BITMASK((bf_off), (bf_len))) | (((type)(val) << (bf_off)) & BITMASK((bf_off), (bf_len))))
#define SIZEOF_IO_URING_SQE 64
#define SIZEOF_IO_URING_CQE 16
#define IORING_SETUP_SQE128 (1U << 10)
#define IORING_SETUP_CQE32 (1U << 11)
struct io_sqring_offsets {
uint32_t head;
uint32_t tail;
uint32_t ring_mask;
uint32_t ring_entries;
uint32_t flags;
uint32_t dropped;
uint32_t array;
uint32_t resv1;
uint64_t user_addr;
};
struct io_cqring_offsets {
uint32_t head;
uint32_t tail;
uint32_t ring_mask;
uint32_t ring_entries;
uint32_t overflow;
uint32_t cqes;
uint32_t flags;
uint32_t resv1;
uint64_t user_addr;
};
struct io_uring_params {
uint32_t sq_entries;
uint32_t cq_entries;
uint32_t flags;
uint32_t sq_thread_cpu;
uint32_t sq_thread_idle;
uint32_t features;
uint32_t resv[4];
struct io_sqring_offsets sq_off;
struct io_cqring_offsets cq_off;
};
static long io_uring_sqe_size(struct io_uring_params* params)
{
return SIZEOF_IO_URING_SQE << !!(params->flags & IORING_SETUP_SQE128);
}
static long io_uring_cqe_size(struct io_uring_params* params)
{
return SIZEOF_IO_URING_CQE << !!(params->flags & IORING_SETUP_CQE32);
}
#define IORING_OFF_SQ_RING 0ULL
#define IORING_OFF_SQES 0x10000000ULL
static long syz_io_uring_setup(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4)
{
uint32_t entries = (uint32_t)a0;
struct io_uring_params* setup_params = (struct io_uring_params*)a1;
void** ring_params_ptr_out = (void**)a2;
void** ring_ptr_out = (void**)a3;
void** sqes_ptr_out = (void**)a4;
uint32_t fd_io_uring = syscall(__NR_io_uring_setup, entries, setup_params);
*ring_params_ptr_out = (void*)setup_params;
uint32_t sq_ring_sz = setup_params->sq_off.array + setup_params->sq_entries * sizeof(uint32_t);
uint32_t cq_ring_sz = setup_params->cq_off.cqes + setup_params->cq_entries * io_uring_cqe_size(setup_params);
uint32_t ring_sz = sq_ring_sz > cq_ring_sz ? sq_ring_sz : cq_ring_sz;
*ring_ptr_out = mmap(0, ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd_io_uring, IORING_OFF_SQ_RING);
uint32_t sqes_sz = setup_params->sq_entries * io_uring_sqe_size(setup_params);
*sqes_ptr_out = mmap(0, sqes_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd_io_uring, IORING_OFF_SQES);
uint32_t* array = (uint32_t*)((uintptr_t)*ring_ptr_out + setup_params->sq_off.array);
for (uint32_t index = 0; index < setup_params->sq_entries; index++)
array[index] = index;
return fd_io_uring;
}
static long syz_io_uring_submit(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
struct io_uring_params* params = (struct io_uring_params*)a0;
char* ring_ptr = (char*)a1;
char* sqes_ptr = (char*)a2;
char* sqe = (char*)a3;
uint32_t sq_ring_mask = *(uint32_t*)(ring_ptr + params->sq_off.ring_mask);
uint32_t* sq_tail_ptr = (uint32_t*)(ring_ptr + params->sq_off.tail);
uint32_t sq_tail = *sq_tail_ptr & sq_ring_mask;
uint32_t sqe_size = io_uring_sqe_size(params);
char* sqe_dest = sqes_ptr + sq_tail * sqe_size;
memcpy(sqe_dest, sqe, sqe_size);
uint32_t sq_tail_next = *sq_tail_ptr + 1;
__atomic_store_n(sq_tail_ptr, sq_tail_next, __ATOMIC_RELEASE);
return 0;
}
static long syz_ublk_setup_io_uring(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4)
{
struct io_uring_params* params = (struct io_uring_params*)a1;
params->flags |= IORING_SETUP_SQE128 | IORING_SETUP_CQE32;
return syz_io_uring_setup(a0, (long)params, a2, a3, a4);
}
#define UBLK_F_USER_COPY (1UL << 7)
#define UBLK_IO_RES_OK 0
#define UBLK_IO_RES_NEED_GET_DATA 1
#define UBLK_IO_COMMIT_AND_FETCH_REQ 0x21
#define UBLK_IO_NEED_GET_DATA 0x22
#define UBLK_U_IO_COMMIT_AND_FETCH_REQ _IOWR('u', UBLK_IO_COMMIT_AND_FETCH_REQ, struct ublksrv_io_cmd)
#define UBLK_U_IO_NEED_GET_DATA _IOWR('u', UBLK_IO_NEED_GET_DATA, struct ublksrv_io_cmd)
struct ublksrv_ctrl_cmd {
__u32 dev_id;
__u16 queue_id;
__u16 len;
__u64 addr;
__u64 data[1];
__u16 dev_path_len;
__u16 pad;
__u32 reserved;
};
struct ublksrv_ctrl_dev_info {
__u16 nr_hw_queues;
__u16 queue_depth;
__u16 state;
__u16 pad0;
__u32 max_io_buf_bytes;
__u32 dev_id;
__s32 ublksrv_pid;
__u32 pad1;
__u64 flags;
__u64 ublksrv_flags;
__u32 owner_uid;
__u32 owner_gid;
__u64 reserved1;
__u64 reserved2;
};
struct ublksrv_io_desc {
__u32 op_flags;
union {
__u32 nr_sectors;
__u32 nr_zones;
};
__u64 start_sector;
__u64 addr;
};
struct ublksrv_io_cmd {
__u16 q_id;
__u16 tag;
__s32 result;
union {
__u64 addr;
__u64 zone_append_lba;
};
};
struct io_uring_sqe {
__u8 opcode;
__u8 flags;
__u16 ioprio;
__s32 fd;
union {
__u64 off;
__u64 addr2;
struct {
__u32 cmd_op;
__u32 __pad1;
};
};
union {
__u64 addr;
__u64 splice_off_in;
struct {
__u32 level;
__u32 optname;
};
};
__u32 len;
union {
__kernel_rwf_t rw_flags;
__u32 fsync_flags;
__u16 poll_events;
__u32 poll32_events;
__u32 sync_range_flags;
__u32 msg_flags;
__u32 timeout_flags;
__u32 accept_flags;
__u32 cancel_flags;
__u32 open_flags;
__u32 statx_flags;
__u32 fadvise_advice;
__u32 splice_flags;
__u32 rename_flags;
__u32 unlink_flags;
__u32 hardlink_flags;
__u32 xattr_flags;
__u32 msg_ring_flags;
__u32 uring_cmd_flags;
__u32 waitid_flags;
__u32 futex_flags;
__u32 install_fd_flags;
__u32 nop_flags;
__u32 pipe_flags;
};
__u64 user_data;
union {
__u16 buf_index;
__u16 buf_group;
} __attribute__((packed));
__u16 personality;
union {
__s32 splice_fd_in;
__u32 file_index;
__u32 zcrx_ifq_idx;
__u32 optlen;
struct {
__u16 addr_len;
__u16 __pad3[1];
};
};
union {
struct {
__u64 addr3;
__u64 __pad2[1];
};
struct {
__u64 attr_ptr;
__u64 attr_type_mask;
};
__u64 optval;
__u8 cmd[0];
};
};
static long syz_ublk_add_dev(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5)
{
__u32 ring_fd = (__u32)a0;
struct io_uring_params* params = (struct io_uring_params*)a1;
char* ring_ptr = (char*)a2;
char* sqes_ptr = (char*)a3;
struct io_uring_sqe* sqe = (struct io_uring_sqe*)a4;
void** dev_info_ptr_out = (void**)a5;
struct ublksrv_ctrl_cmd* cmd = (struct ublksrv_ctrl_cmd*)(sqe->cmd);
struct ublksrv_ctrl_dev_info* dev_info = (struct ublksrv_ctrl_dev_info*)(unsigned long)(cmd->addr);
dev_info->flags |= UBLK_F_USER_COPY;
int ret = syz_io_uring_submit((long)params, (long)ring_ptr, (long)sqes_ptr, (long)sqe);
if (ret < 0)
return (long)ret;
syscall(__NR_io_uring_enter, ring_fd, 1, 1, 1, NULL, _NSIG / 8);
*dev_info_ptr_out = (void*)dev_info;
return dev_info->dev_id;
}
struct ublk_queue {
__u16 q_id;
__u32 char_dev_fd;
struct io_uring_params params;
__u32 ring_fd;
char* ring_ptr;
char* sqes_ptr;
char* io_cmd_buf;
};
#define UBLK_MAX_QUEUE_DEPTH 4096
#define UBLK_QUEUES_OFFSET 0
#define round_up(x,y) (((x) + (y) - 1) / (y) * (y))
static long syz_ublk_setup_queues(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6)
{
int char_dev_fd = (int)a0;
struct ublksrv_ctrl_dev_info* dev_info = (struct ublksrv_ctrl_dev_info*)a1;
struct io_uring_params* params = (struct io_uring_params*)a2;
struct ublk_queue* queues = (struct ublk_queue*)a3;
int queues_len = (int)a4;
struct io_uring_sqe* sqe = (struct io_uring_sqe*)a5;
void** queues_ptr_out = (void**)a6;
if (char_dev_fd < 0 || !dev_info)
return -1;
memset(queues, 0, sizeof(struct ublk_queue) * queues_len);
int queue_depth = dev_info->queue_depth;
for (int q_id = 0; q_id < dev_info->nr_hw_queues; q_id++) {
struct ublk_queue* queue = &queues[q_id];
queue->q_id = q_id;
queue->char_dev_fd = char_dev_fd;
queue->params = *params;
struct io_uring_params* temp = NULL;
queue->ring_fd = syz_ublk_setup_io_uring((long)queue_depth, (long)&queue->params, (long)&temp, (long)&queue->ring_ptr, (long)&queue->sqes_ptr);
if (queue->ring_fd < 0) {
return queue->ring_fd;
}
unsigned long cmd_buf_size = round_up(queue_depth * sizeof(struct ublksrv_io_desc), getpagesize());
unsigned long off = UBLK_QUEUES_OFFSET + q_id * round_up(UBLK_MAX_QUEUE_DEPTH * sizeof(struct ublksrv_io_desc), getpagesize());
queue->io_cmd_buf = (char*)mmap(0, cmd_buf_size, PROT_READ, MAP_SHARED | MAP_POPULATE, char_dev_fd, off);
if (queue->io_cmd_buf == MAP_FAILED) {
return -1;
}
for (int j = 0; j < queue_depth; j++) {
struct ublksrv_io_cmd* cmd = (struct ublksrv_io_cmd*)(&sqe->cmd);
cmd->q_id = q_id;
cmd->tag = j;
cmd->addr = 0;
sqe->user_data = j | (sqe->cmd_op << 16);
if (syz_io_uring_submit((long)&queue->params, (long)queue->ring_ptr, (long)queue->sqes_ptr, (long)sqe) < 0) {
return -1;
}
syscall(__NR_io_uring_enter, queue->ring_fd, 1, 0, 0, NULL, _NSIG / 8);
}
}
*queues_ptr_out = (void*)queues;
return 0;
}
static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
{
if (a0 == 0xc || a0 == 0xb) {
char buf[128];
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
return open(buf, O_RDWR, 0);
} else {
unsigned long nb = a1;
char buf[1024];
char* hash;
strncpy(buf, (char*)a0, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
while ((hash = strchr(buf, '#'))) {
*hash = '0' + (char)(nb % 10);
nb /= 10;
}
return open(buf, a2 & ~O_CREAT, 0);
}
}
uint64_t r[9] = {0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// openat$ublk_ctrl arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 75 62 6c 6b 2d 63 6f 6e 74 72 6f 6c 00} (length 0x12)
// }
// flags: const = 0x2 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_ublk_ctrl
memcpy((void*)0x200000000000, "/dev/ublk-control\000", 18);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000000ul, /*flags=*/2, /*mode=*/0);
if (res != -1)
r[0] = res;
// syz_ublk_setup_io_uring arguments: [
// entries: int32 = 0x20 (4 bytes)
// params: ptr[inout, io_uring_params] {
// io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: {00 00 00 00 00 00 00 00 00 00 00 00} (length 0xc)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// }
// ring_params_ptr: ptr[out, ring_params_ptr] {
// ring_params_ptr (resource)
// }
// ring_ptr: ptr[out, ring_ptr] {
// ring_ptr (resource)
// }
// sqes_ptr: ptr[out, sqes_ptr] {
// sqes_ptr (resource)
// }
// ]
// returns fd_io_uring
*(uint32_t*)0x200000000044 = 0;
*(uint32_t*)0x200000000048 = 0;
*(uint32_t*)0x20000000004c = 0;
*(uint32_t*)0x200000000050 = 0;
*(uint32_t*)0x200000000058 = -1;
memset((void*)0x20000000005c, 0, 12);
res = -1;
res = syz_ublk_setup_io_uring(/*entries=*/0x20, /*params=*/0x200000000040, /*ring_params_ptr=*/0x2000000000c0, /*ring_ptr=*/0x200000000100, /*sqes_ptr=*/0x200000000140);
if (res != -1) {
r[1] = res;
r[2] = *(uint64_t*)0x2000000000c0;
r[3] = *(uint64_t*)0x200000000100;
r[4] = *(uint64_t*)0x200000000140;
}
// syz_ublk_add_dev arguments: [
// fd_io_uring: fd_io_uring (resource)
// ring_params_ptr: ring_params_ptr (resource)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// sqe: ptr[inout, io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_ADD_DEV, int32], ublk_ctrl_cmd$add_dev, int64]] {
// io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_ADD_DEV, int32], ublk_ctrl_cmd$add_dev, int64] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ctrl (resource)
// cmd_op: const = 0xc0207504 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: int64 = 0x0 (8 bytes)
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_ctrl_cmd[const[-1, int32], len[addr, int16], ptr64[in, ublk_ctrl_dev_info_new], const[0, int64]] {
// dev_id: const = 0xffffffff (4 bytes)
// queue_id: const = 0xffff (2 bytes)
// len: len = 0x48 (2 bytes)
// addr: ptr[in, ublk_ctrl_dev_info_new] {
// union ublk_ctrl_dev_info_new {
// new_dev: ublk_ctrl_dev_info[const[-1, int32]] {
// nr_hw_queues: int16 = 0x1 (2 bytes)
// queue_depth: int16 = 0x4 (2 bytes)
// state: const = 0x0 (2 bytes)
// pad0: const = 0x0 (2 bytes)
// max_io_buf_bytes: const = 0x1000 (4 bytes)
// dev_id: const = 0xffffffff (4 bytes)
// ublk_pid: pid (resource)
// pad1: const = 0x0 (4 bytes)
// flags: ublk_f_flags = 0x80000 (8 bytes)
// ublk_flags: const = 0x0 (8 bytes)
// owner_uid: const = 0x0 (4 bytes)
// owner_gid: const = 0x0 (4 bytes)
// reserved1: const = 0x0 (8 bytes)
// reserved2: const = 0x0 (8 bytes)
// }
// }
// }
// data: const = 0x0 (8 bytes)
// dev_path_len: const = 0x0 (2 bytes)
// pad: const = 0x0 (2 bytes)
// reserved: const = 0x0 (4 bytes)
// pad2_unused: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x30)
// }
// }
// }
// dev_info_ptr: ptr[out, dev_info_ptr] {
// dev_info_ptr (resource)
// }
// ]
// returns ublk_dev_id
*(uint8_t*)0x200000000200 = 0x2e;
*(uint8_t*)0x200000000201 = 0;
*(uint16_t*)0x200000000202 = 0;
*(uint32_t*)0x200000000204 = r[0];
*(uint32_t*)0x200000000208 = 0xc0207504;
*(uint32_t*)0x20000000020c = 0;
*(uint64_t*)0x200000000210 = 0;
*(uint32_t*)0x200000000218 = 0;
*(uint32_t*)0x20000000021c = 0;
*(uint64_t*)0x200000000220 = 0;
*(uint16_t*)0x200000000228 = 0;
*(uint16_t*)0x20000000022a = 0;
memset((void*)0x20000000022c, 0, 4);
*(uint32_t*)0x200000000230 = -1;
*(uint16_t*)0x200000000234 = -1;
*(uint16_t*)0x200000000236 = 0x48;
*(uint64_t*)0x200000000238 = 0x200000000500;
*(uint16_t*)0x200000000500 = 1;
*(uint16_t*)0x200000000502 = 4;
*(uint16_t*)0x200000000504 = 0;
*(uint16_t*)0x200000000506 = 0;
*(uint32_t*)0x200000000508 = 0x1000;
*(uint32_t*)0x20000000050c = -1;
*(uint32_t*)0x200000000510 = 0;
*(uint32_t*)0x200000000514 = 0;
*(uint64_t*)0x200000000518 = 0x80000;
*(uint64_t*)0x200000000520 = 0;
*(uint32_t*)0x200000000528 = 0;
*(uint32_t*)0x20000000052c = 0;
*(uint64_t*)0x200000000530 = 0;
*(uint64_t*)0x200000000538 = 0;
*(uint64_t*)0x200000000240 = 0;
*(uint16_t*)0x200000000248 = 0;
*(uint16_t*)0x20000000024a = 0;
*(uint32_t*)0x20000000024c = 0;
memset((void*)0x200000000250, 0, 48);
res = -1;
res = syz_ublk_add_dev(/*fd_io_uring=*/r[1], /*ring_params_ptr=*/r[2], /*ring_ptr=*/r[3], /*sqes_ptr=*/r[4], /*sqe=*/0x200000000200, /*dev_info_ptr=*/0x200000000300);
if (res != -1) {
r[5] = res;
r[6] = *(uint64_t*)0x200000000300;
}
// io_uring_enter arguments: [
// fd: fd_io_uring (resource)
// to_submit: int32 = 0x1 (4 bytes)
// min_complete: int32 = 0x1 (4 bytes)
// flags: io_uring_enter_flags = 0x1 (8 bytes)
// sigmask: nil
// size: len = 0x0 (8 bytes)
// ]
syscall(__NR_io_uring_enter, /*fd=*/r[1], /*to_submit=*/1, /*min_complete=*/1, /*flags=IORING_ENTER_GETEVENTS*/1ul, /*sigmask=*/0ul, /*size=*/0ul);
// syz_io_uring_submit$UBLK arguments: [
// ring_params_ptr: ring_params_ptr (resource)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// sqe: ptr[inout, ublk_cmd_sqe_u] {
// union ublk_cmd_sqe_u {
// ublk_set_params_sqe: io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_SET_PARAMS, int32], ublk_ctrl_cmd$set_params, int64] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ctrl (resource)
// cmd_op: const = 0xc0207508 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: int64 = 0x0 (8 bytes)
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_ctrl_cmd[ublk_dev_id, len[addr, int16], ptr64[in, ublk_params], const[0, int64]] {
// dev_id: ublk_dev_id (resource)
// queue_id: const = 0xffff (2 bytes)
// len: len = 0x30 (2 bytes)
// addr: ptr[in, ublk_params] {
// ublk_params {
// len: len = 0x30 (4 bytes)
// types: ublk_param_types = 0x1 (4 bytes)
// basic: ublk_param_basic {
// attrs: ublk_attr_flags = 0x0 (4 bytes)
// logical_bs_shift: int8 = 0x9 (1 bytes)
// physical_bs_shift: int8 = 0xc (1 bytes)
// io_opt_shift: int8 = 0xc (1 bytes)
// io_min_shift: int8 = 0x9 (1 bytes)
// max_sectors: int32 = 0x8 (4 bytes)
// chunk_sectors: int32 = 0x0 (4 bytes)
// dev_sectors: int64 = 0x1000 (8 bytes)
// virt_boundary_mask: int64 = 0x0 (8 bytes)
// }
// discard: ublk_param_discard {
// discard_alignment: int32 = 0x0 (4 bytes)
// discard_granularity: int32 = 0x0 (4 bytes)
// max_discard_sectors: int32 = 0x0 (4 bytes)
// max_write_zeroes_sectors: int32 = 0x0 (4 bytes)
// max_discard_segments: int16 = 0x0 (2 bytes)
// reserved0: const = 0x0 (2 bytes)
// }
// devt: ublk_param_devt {
// char_major: int32 = 0x0 (4 bytes)
// char_minor: int32 = 0x0 (4 bytes)
// disk_major: int32 = 0x0 (4 bytes)
// disk_minor: int32 = 0x0 (4 bytes)
// }
// zoned: ublk_param_zoned {
// max_open_zones: int32 = 0x0 (4 bytes)
// max_active_zones: int32 = 0x0 (4 bytes)
// max_zone_append_sectors: int32 = 0x0 (4 bytes)
// reserved: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x14)
// }
// pad = 0x0 (4 bytes)
// }
// }
// data: const = 0x0 (8 bytes)
// dev_path_len: const = 0x0 (2 bytes)
// pad: const = 0x0 (2 bytes)
// reserved: const = 0x0 (4 bytes)
// pad2_unused: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x30)
// }
// }
// }
// }
// ]
*(uint8_t*)0x200000000600 = 0x2e;
*(uint8_t*)0x200000000601 = 0;
*(uint16_t*)0x200000000602 = 0;
*(uint32_t*)0x200000000604 = r[0];
*(uint32_t*)0x200000000608 = 0xc0207508;
*(uint32_t*)0x20000000060c = 0;
*(uint64_t*)0x200000000610 = 0;
*(uint32_t*)0x200000000618 = 0;
*(uint32_t*)0x20000000061c = 0;
*(uint64_t*)0x200000000620 = 0;
*(uint16_t*)0x200000000628 = 0;
*(uint16_t*)0x20000000062a = 0;
memset((void*)0x20000000062c, 0, 4);
*(uint32_t*)0x200000000630 = r[5];
*(uint16_t*)0x200000000634 = -1;
*(uint16_t*)0x200000000636 = 0x30;
*(uint64_t*)0x200000000638 = 0x200000000680;
*(uint32_t*)0x200000000680 = 0x30;
*(uint32_t*)0x200000000684 = 1;
*(uint32_t*)0x200000000688 = 0;
*(uint8_t*)0x20000000068c = 9;
*(uint8_t*)0x20000000068d = 0xc;
*(uint8_t*)0x20000000068e = 0xc;
*(uint8_t*)0x20000000068f = 9;
*(uint32_t*)0x200000000690 = 8;
*(uint32_t*)0x200000000694 = 0;
*(uint64_t*)0x200000000698 = 0x1000;
*(uint64_t*)0x2000000006a0 = 0;
*(uint32_t*)0x2000000006a8 = 0;
*(uint32_t*)0x2000000006ac = 0;
*(uint32_t*)0x2000000006b0 = 0;
*(uint32_t*)0x2000000006b4 = 0;
*(uint16_t*)0x2000000006b8 = 0;
*(uint16_t*)0x2000000006ba = 0;
*(uint32_t*)0x2000000006bc = 0;
*(uint32_t*)0x2000000006c0 = 0;
*(uint32_t*)0x2000000006c4 = 0;
*(uint32_t*)0x2000000006c8 = 0;
*(uint32_t*)0x2000000006cc = 0;
*(uint32_t*)0x2000000006d0 = 0;
*(uint32_t*)0x2000000006d4 = 0;
memset((void*)0x2000000006d8, 0, 20);
*(uint64_t*)0x200000000640 = 0;
*(uint16_t*)0x200000000648 = 0;
*(uint16_t*)0x20000000064a = 0;
*(uint32_t*)0x20000000064c = 0;
memset((void*)0x200000000650, 0, 48);
syz_io_uring_submit(/*ring_params_ptr=*/r[2], /*ring_ptr=*/r[3], /*sqes_ptr=*/r[4], /*sqe=*/0x200000000600);
// io_uring_enter arguments: [
// fd: fd_io_uring (resource)
// to_submit: int32 = 0x1 (4 bytes)
// min_complete: int32 = 0x1 (4 bytes)
// flags: io_uring_enter_flags = 0x1 (8 bytes)
// sigmask: nil
// size: len = 0x0 (8 bytes)
// ]
syscall(__NR_io_uring_enter, /*fd=*/r[1], /*to_submit=*/1, /*min_complete=*/1, /*flags=IORING_ENTER_GETEVENTS*/1ul, /*sigmask=*/0ul, /*size=*/0ul);
// syz_open_dev$ublk_chdev arguments: [
// dev: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 75 62 6c 6b 63 23 00} (length 0xc)
// }
// id: ublk_dev_id (resource)
// flags: open_flags = 0x2 (8 bytes)
// ]
// returns fd_ublk_ch
memcpy((void*)0x200000002000, "/dev/ublkc#\000", 12);
res = -1;
res = syz_open_dev(/*dev=*/0x200000002000, /*id=*/r[5], /*flags=O_RDWR*/2);
if (res != -1)
r[7] = res;
// syz_ublk_setup_queues arguments: [
// fd_ublk_ch: fd_ublk_ch (resource)
// dev_info_ptr: dev_info_ptr (resource)
// params: ptr[in, io_uring_params] {
// io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: {00 00 00 00 00 00 00 00 00 00 00 00} (length 0xc)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// }
// queues: ptr[out, array[ublk_queue]] {
// array[ublk_queue] {
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// }
// }
// nr_queues: const = 0x1 (8 bytes)
// sqe: ptr[inout, io_uring_sqe_uring[fd_ublk_ch, const[UBLK_U_IO_FETCH_REQ, int32], ublk_io_cmd$fetch_req, user_data[UBLK_U_IO_FETCH_REQ]]] {
// io_uring_sqe_uring[fd_ublk_ch, const[UBLK_U_IO_FETCH_REQ, int32], ublk_io_cmd$fetch_req, user_data[UBLK_U_IO_FETCH_REQ]] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ch (resource)
// cmd_op: const = 0xc0107520 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: user_data[UBLK_U_IO_FETCH_REQ] {
// tag: int64 = 0x0 (0 bytes)
// cmd: const = 0xc0107520 (0 bytes)
// pad: const = 0x0 (8 bytes)
// }
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_io_cmd[int16[0:UBLK_MAX_HW_QUEUES], int16[0:UBLK_MAX_QUEUE_DEPTH], const[0, int32], ptr64[inout, ublk_io_buf_u]] {
// q_id: int16 = 0x0 (2 bytes)
// tag: int16 = 0x0 (2 bytes)
// result: const = 0x0 (4 bytes)
// addr: nil
// pad: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x40)
// }
// }
// }
// ublk_queues_ptr: nil
// ]
*(uint32_t*)0x200000000704 = 0;
*(uint32_t*)0x200000000708 = 0;
*(uint32_t*)0x20000000070c = 0;
*(uint32_t*)0x200000000710 = 0;
*(uint32_t*)0x200000000718 = -1;
memset((void*)0x20000000071c, 0, 12);
*(uint32_t*)0x20000000080c = 0;
*(uint32_t*)0x200000000810 = 0;
*(uint32_t*)0x200000000814 = 0;
*(uint32_t*)0x200000000818 = 0;
*(uint32_t*)0x200000000820 = -1;
*(uint32_t*)0x2000000008ac = 0;
*(uint32_t*)0x2000000008b0 = 0;
*(uint32_t*)0x2000000008b4 = 0;
*(uint32_t*)0x2000000008b8 = 0;
*(uint32_t*)0x2000000008c0 = -1;
*(uint32_t*)0x20000000094c = 0;
*(uint32_t*)0x200000000950 = 0;
*(uint32_t*)0x200000000954 = 0;
*(uint32_t*)0x200000000958 = 0;
*(uint32_t*)0x200000000960 = -1;
*(uint32_t*)0x2000000009ec = 0;
*(uint32_t*)0x2000000009f0 = 0;
*(uint32_t*)0x2000000009f4 = 0;
*(uint32_t*)0x2000000009f8 = 0;
*(uint32_t*)0x200000000a00 = -1;
*(uint8_t*)0x200000000900 = 0x2e;
*(uint8_t*)0x200000000901 = 0;
*(uint16_t*)0x200000000902 = 0;
*(uint32_t*)0x200000000904 = r[7];
*(uint32_t*)0x200000000908 = 0xc0107520;
*(uint32_t*)0x20000000090c = 0;
*(uint64_t*)0x200000000910 = 0;
*(uint32_t*)0x200000000918 = 0;
*(uint32_t*)0x20000000091c = 0;
STORE_BY_BITMASK(uint64_t, , 0x200000000920, 0, 0, 16);
STORE_BY_BITMASK(uint64_t, , 0x200000000920, 0xc0107520, 16, 32);
STORE_BY_BITMASK(uint64_t, , 0x200000000920, 0, 48, 16);
*(uint16_t*)0x200000000928 = 0;
*(uint16_t*)0x20000000092a = 0;
memset((void*)0x20000000092c, 0, 4);
*(uint16_t*)0x200000000930 = 0;
*(uint16_t*)0x200000000932 = 0;
*(uint32_t*)0x200000000934 = 0;
*(uint64_t*)0x200000000938 = 0;
memset((void*)0x200000000940, 0, 64);
syz_ublk_setup_queues(/*fd_ublk_ch=*/r[7], /*dev_info_ptr=*/r[6], /*params=*/0x200000000700, /*queues=*/0x200000000800, /*nr_queues=*/1, /*sqe=*/0x200000000900, /*ublk_queues_ptr=*/0);
// getpid arguments: [
// ]
// returns pid
res = syscall(__NR_getpid);
if (res != -1)
r[8] = res;
// syz_io_uring_submit$UBLK arguments: [
// ring_params_ptr: ring_params_ptr (resource)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// sqe: ptr[inout, ublk_cmd_sqe_u] {
// union ublk_cmd_sqe_u {
// ublk_start_dev_sqe: io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_START_DEV, int32], ublk_ctrl_cmd$start_dev, int64] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ctrl (resource)
// cmd_op: const = 0xc0207506 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: int64 = 0x0 (8 bytes)
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_ctrl_cmd[ublk_dev_id, const[0, int16], const[0, int64], pid] {
// dev_id: ublk_dev_id (resource)
// queue_id: const = 0xffff (2 bytes)
// len: const = 0x0 (2 bytes)
// addr: const = 0x0 (8 bytes)
// data: pid (resource)
// dev_path_len: const = 0x0 (2 bytes)
// pad: const = 0x0 (2 bytes)
// reserved: const = 0x0 (4 bytes)
// pad2_unused: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x30)
// pad = 0x0 (4 bytes)
// }
// }
// }
// }
// ]
*(uint8_t*)0x200000000c00 = 0x2e;
*(uint8_t*)0x200000000c01 = 0;
*(uint16_t*)0x200000000c02 = 0;
*(uint32_t*)0x200000000c04 = r[0];
*(uint32_t*)0x200000000c08 = 0xc0207506;
*(uint32_t*)0x200000000c0c = 0;
*(uint64_t*)0x200000000c10 = 0;
*(uint32_t*)0x200000000c18 = 0;
*(uint32_t*)0x200000000c1c = 0;
*(uint64_t*)0x200000000c20 = 0;
*(uint16_t*)0x200000000c28 = 0;
*(uint16_t*)0x200000000c2a = 0;
memset((void*)0x200000000c2c, 0, 4);
*(uint32_t*)0x200000000c30 = r[5];
*(uint16_t*)0x200000000c34 = -1;
*(uint16_t*)0x200000000c36 = 0;
*(uint64_t*)0x200000000c38 = 0;
*(uint32_t*)0x200000000c40 = r[8];
*(uint16_t*)0x200000000c44 = 0;
*(uint16_t*)0x200000000c46 = 0;
*(uint32_t*)0x200000000c48 = 0;
memset((void*)0x200000000c4c, 0, 48);
syz_io_uring_submit(/*ring_params_ptr=*/r[2], /*ring_ptr=*/r[3], /*sqes_ptr=*/r[4], /*sqe=*/0x200000000c00);
// io_uring_enter arguments: [
// fd: fd_io_uring (resource)
// to_submit: int32 = 0x1 (4 bytes)
// min_complete: int32 = 0x1 (4 bytes)
// flags: io_uring_enter_flags = 0x1 (8 bytes)
// sigmask: nil
// size: len = 0x0 (8 bytes)
// ]
syscall(__NR_io_uring_enter, /*fd=*/r[1], /*to_submit=*/1, /*min_complete=*/1, /*flags=IORING_ENTER_GETEVENTS*/1ul, /*sigmask=*/0ul, /*size=*/0ul);
return 0;
}
]
|
| 2/1 |
2026/09/15 02:47 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/84a958cdd6340c89fc180bcaf2cf85eabad4b178]
|
| 3/1 |
2026/09/15 02:47 |
action |
kernel-builder |
17m
Results: map[KernelObj:/app/workdir/cache/build/b90dfab9ed0d2038ca76d92b397df0a21ab36106]
|
| 4/1 |
2026/09/15 03:04 |
action |
codesearch-prepare |
11m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/09/15 03:16 |
agent |
expert |
7m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
=============================
WARNING: suspicious RCU usage
syzkaller #0 Not tainted
-----------------------------
lib/maple_tree.c:720 suspicious rcu_dereference_check() usage!
other info that might help us debug this:
rcu_scheduler_active = 2, debug_locks = 1
locks held by syz-executor208/5631: 1, last CPU#1:
#0: ffff8880792b60a0 (&ctx->uring_lock){+.+.}-{4:4}, at: tctx_task_work_run+0x2d3/0xa80 io_uring/tw.c:92
stack backtrace:
CPU: 1 UID: 0 PID: 5631 Comm: syz-executor208 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/26/2026
Call Trace:
<TASK>
dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
lockdep_rcu_suspicious+0x140/0x1d0 kernel/locking/lockdep.c:6972
mas_root lib/maple_tree.c:720 [inline]
mas_start+0x3cc/0x640 lib/maple_tree.c:1250
mas_state_walk lib/maple_tree.c:3006 [inline]
mas_walk+0x1db/0x490 lib/maple_tree.c:4458
ublk_try_buf_match drivers/block/ublk_drv.c:5639 [inline]
ublk_init_iod+0x5d2/0x1120 drivers/block/ublk_drv.c:521
ublk_dispatch_req+0x97/0x9c0 drivers/block/ublk_drv.c:1776
tctx_task_work_run+0x73d/0xa80 io_uring/tw.c:96
tctx_task_work+0x3f/0x60 io_uring/tw.c:133
task_work_run+0x1d9/0x270 kernel/task_work.c:233
exit_task_work include/linux/task_work.h:40 [inline]
do_exit+0x73a/0x2360 kernel/exit.c:1011
do_group_exit+0x22d/0x2f0 kernel/exit.c:1154
__do_sys_exit_group kernel/exit.c:1165 [inline]
__se_sys_exit_group kernel/exit.c:1163 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1163
x64_sys_call+0x18c6/0x18d0 arch/x86/include/generated/asm/syscalls_64.h:232
do_syscall_x64 arch/x86/entry/syscall_64.c:61 [inline]
do_syscall_64+0x166/0x520 arch/x86/entry/syscall_64.c:84
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f6413fb1325
Code: Unable to access opcode bytes at 0x7f6413fb12fb.
RSP: 002b:00007ffec9819178 EFLAGS: 00000206 ORIG_RAX: 00000000000000e7
RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 00007f6413fb1325
RDX: 00000000000000e7 RSI: ffffffffffffffd8 RDI: 0000000000000000
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000206 R12: 0000000000000000
R13: 00007f641404a158 R14: 00007f641404c408 R15: 00007f641404c420
</TASK>
I/O error, dev ublkb0, sector 0 op 0x0:(READ) flags 0x800000 phys_seg 1 prio class 2
Buffer I/O error on dev ublkb0, logical block 0, async page read
It is reproducible with the followint program.
Keep in mind that it may lack the precise threading, sandboxing, and some arguments of a working reproducer.
But it should give an idea of the involved syscalls.
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/fs.h>
#include <linux/types.h>
#ifndef __NR_io_uring_enter
#define __NR_io_uring_enter 426
#endif
#define BITMASK(bf_off,bf_len) (((1ull << (bf_len)) - 1) << (bf_off))
#define STORE_BY_BITMASK(type,htobe,addr,val,bf_off,bf_len) *(type*)(addr) = htobe((htobe(*(type*)(addr)) & ~BITMASK((bf_off), (bf_len))) | (((type)(val) << (bf_off)) & BITMASK((bf_off), (bf_len))))
#define SIZEOF_IO_URING_SQE 64
#define SIZEOF_IO_URING_CQE 16
#define IORING_SETUP_SQE128 (1U << 10)
#define IORING_SETUP_CQE32 (1U << 11)
struct io_sqring_offsets {
uint32_t head;
uint32_t tail;
uint32_t ring_mask;
uint32_t ring_entries;
uint32_t flags;
uint32_t dropped;
uint32_t array;
uint32_t resv1;
uint64_t user_addr;
};
struct io_cqring_offsets {
uint32_t head;
uint32_t tail;
uint32_t ring_mask;
uint32_t ring_entries;
uint32_t overflow;
uint32_t cqes;
uint32_t flags;
uint32_t resv1;
uint64_t user_addr;
};
struct io_uring_params {
uint32_t sq_entries;
uint32_t cq_entries;
uint32_t flags;
uint32_t sq_thread_cpu;
uint32_t sq_thread_idle;
uint32_t features;
uint32_t resv[4];
struct io_sqring_offsets sq_off;
struct io_cqring_offsets cq_off;
};
static long io_uring_sqe_size(struct io_uring_params* params)
{
return SIZEOF_IO_URING_SQE << !!(params->flags & IORING_SETUP_SQE128);
}
static long io_uring_cqe_size(struct io_uring_params* params)
{
return SIZEOF_IO_URING_CQE << !!(params->flags & IORING_SETUP_CQE32);
}
#define IORING_OFF_SQ_RING 0ULL
#define IORING_OFF_SQES 0x10000000ULL
static long syz_io_uring_setup(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4)
{
uint32_t entries = (uint32_t)a0;
struct io_uring_params* setup_params = (struct io_uring_params*)a1;
void** ring_params_ptr_out = (void**)a2;
void** ring_ptr_out = (void**)a3;
void** sqes_ptr_out = (void**)a4;
uint32_t fd_io_uring = syscall(__NR_io_uring_setup, entries, setup_params);
*ring_params_ptr_out = (void*)setup_params;
uint32_t sq_ring_sz = setup_params->sq_off.array + setup_params->sq_entries * sizeof(uint32_t);
uint32_t cq_ring_sz = setup_params->cq_off.cqes + setup_params->cq_entries * io_uring_cqe_size(setup_params);
uint32_t ring_sz = sq_ring_sz > cq_ring_sz ? sq_ring_sz : cq_ring_sz;
*ring_ptr_out = mmap(0, ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd_io_uring, IORING_OFF_SQ_RING);
uint32_t sqes_sz = setup_params->sq_entries * io_uring_sqe_size(setup_params);
*sqes_ptr_out = mmap(0, sqes_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd_io_uring, IORING_OFF_SQES);
uint32_t* array = (uint32_t*)((uintptr_t)*ring_ptr_out + setup_params->sq_off.array);
for (uint32_t index = 0; index < setup_params->sq_entries; index++)
array[index] = index;
return fd_io_uring;
}
static long syz_io_uring_submit(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
struct io_uring_params* params = (struct io_uring_params*)a0;
char* ring_ptr = (char*)a1;
char* sqes_ptr = (char*)a2;
char* sqe = (char*)a3;
uint32_t sq_ring_mask = *(uint32_t*)(ring_ptr + params->sq_off.ring_mask);
uint32_t* sq_tail_ptr = (uint32_t*)(ring_ptr + params->sq_off.tail);
uint32_t sq_tail = *sq_tail_ptr & sq_ring_mask;
uint32_t sqe_size = io_uring_sqe_size(params);
char* sqe_dest = sqes_ptr + sq_tail * sqe_size;
memcpy(sqe_dest, sqe, sqe_size);
uint32_t sq_tail_next = *sq_tail_ptr + 1;
__atomic_store_n(sq_tail_ptr, sq_tail_next, __ATOMIC_RELEASE);
return 0;
}
static long syz_ublk_setup_io_uring(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4)
{
struct io_uring_params* params = (struct io_uring_params*)a1;
params->flags |= IORING_SETUP_SQE128 | IORING_SETUP_CQE32;
return syz_io_uring_setup(a0, (long)params, a2, a3, a4);
}
#define UBLK_F_USER_COPY (1UL << 7)
#define UBLK_IO_RES_OK 0
#define UBLK_IO_RES_NEED_GET_DATA 1
#define UBLK_IO_COMMIT_AND_FETCH_REQ 0x21
#define UBLK_IO_NEED_GET_DATA 0x22
#define UBLK_U_IO_COMMIT_AND_FETCH_REQ _IOWR('u', UBLK_IO_COMMIT_AND_FETCH_REQ, struct ublksrv_io_cmd)
#define UBLK_U_IO_NEED_GET_DATA _IOWR('u', UBLK_IO_NEED_GET_DATA, struct ublksrv_io_cmd)
struct ublksrv_ctrl_cmd {
__u32 dev_id;
__u16 queue_id;
__u16 len;
__u64 addr;
__u64 data[1];
__u16 dev_path_len;
__u16 pad;
__u32 reserved;
};
struct ublksrv_ctrl_dev_info {
__u16 nr_hw_queues;
__u16 queue_depth;
__u16 state;
__u16 pad0;
__u32 max_io_buf_bytes;
__u32 dev_id;
__s32 ublksrv_pid;
__u32 pad1;
__u64 flags;
__u64 ublksrv_flags;
__u32 owner_uid;
__u32 owner_gid;
__u64 reserved1;
__u64 reserved2;
};
struct ublksrv_io_desc {
__u32 op_flags;
union {
__u32 nr_sectors;
__u32 nr_zones;
};
__u64 start_sector;
__u64 addr;
};
struct ublksrv_io_cmd {
__u16 q_id;
__u16 tag;
__s32 result;
union {
__u64 addr;
__u64 zone_append_lba;
};
};
struct io_uring_sqe {
__u8 opcode;
__u8 flags;
__u16 ioprio;
__s32 fd;
union {
__u64 off;
__u64 addr2;
struct {
__u32 cmd_op;
__u32 __pad1;
};
};
union {
__u64 addr;
__u64 splice_off_in;
struct {
__u32 level;
__u32 optname;
};
};
__u32 len;
union {
__kernel_rwf_t rw_flags;
__u32 fsync_flags;
__u16 poll_events;
__u32 poll32_events;
__u32 sync_range_flags;
__u32 msg_flags;
__u32 timeout_flags;
__u32 accept_flags;
__u32 cancel_flags;
__u32 open_flags;
__u32 statx_flags;
__u32 fadvise_advice;
__u32 splice_flags;
__u32 rename_flags;
__u32 unlink_flags;
__u32 hardlink_flags;
__u32 xattr_flags;
__u32 msg_ring_flags;
__u32 uring_cmd_flags;
__u32 waitid_flags;
__u32 futex_flags;
__u32 install_fd_flags;
__u32 nop_flags;
__u32 pipe_flags;
};
__u64 user_data;
union {
__u16 buf_index;
__u16 buf_group;
} __attribute__((packed));
__u16 personality;
union {
__s32 splice_fd_in;
__u32 file_index;
__u32 zcrx_ifq_idx;
__u32 optlen;
struct {
__u16 addr_len;
__u16 __pad3[1];
};
};
union {
struct {
__u64 addr3;
__u64 __pad2[1];
};
struct {
__u64 attr_ptr;
__u64 attr_type_mask;
};
__u64 optval;
__u8 cmd[0];
};
};
static long syz_ublk_add_dev(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5)
{
__u32 ring_fd = (__u32)a0;
struct io_uring_params* params = (struct io_uring_params*)a1;
char* ring_ptr = (char*)a2;
char* sqes_ptr = (char*)a3;
struct io_uring_sqe* sqe = (struct io_uring_sqe*)a4;
void** dev_info_ptr_out = (void**)a5;
struct ublksrv_ctrl_cmd* cmd = (struct ublksrv_ctrl_cmd*)(sqe->cmd);
struct ublksrv_ctrl_dev_info* dev_info = (struct ublksrv_ctrl_dev_info*)(unsigned long)(cmd->addr);
dev_info->flags |= UBLK_F_USER_COPY;
int ret = syz_io_uring_submit((long)params, (long)ring_ptr, (long)sqes_ptr, (long)sqe);
if (ret < 0)
return (long)ret;
syscall(__NR_io_uring_enter, ring_fd, 1, 1, 1, NULL, _NSIG / 8);
*dev_info_ptr_out = (void*)dev_info;
return dev_info->dev_id;
}
struct ublk_queue {
__u16 q_id;
__u32 char_dev_fd;
struct io_uring_params params;
__u32 ring_fd;
char* ring_ptr;
char* sqes_ptr;
char* io_cmd_buf;
};
#define UBLK_MAX_QUEUE_DEPTH 4096
#define UBLK_QUEUES_OFFSET 0
#define round_up(x,y) (((x) + (y) - 1) / (y) * (y))
static long syz_ublk_setup_queues(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6)
{
int char_dev_fd = (int)a0;
struct ublksrv_ctrl_dev_info* dev_info = (struct ublksrv_ctrl_dev_info*)a1;
struct io_uring_params* params = (struct io_uring_params*)a2;
struct ublk_queue* queues = (struct ublk_queue*)a3;
int queues_len = (int)a4;
struct io_uring_sqe* sqe = (struct io_uring_sqe*)a5;
void** queues_ptr_out = (void**)a6;
if (char_dev_fd < 0 || !dev_info)
return -1;
memset(queues, 0, sizeof(struct ublk_queue) * queues_len);
int queue_depth = dev_info->queue_depth;
for (int q_id = 0; q_id < dev_info->nr_hw_queues; q_id++) {
struct ublk_queue* queue = &queues[q_id];
queue->q_id = q_id;
queue->char_dev_fd = char_dev_fd;
queue->params = *params;
struct io_uring_params* temp = NULL;
queue->ring_fd = syz_ublk_setup_io_uring((long)queue_depth, (long)&queue->params, (long)&temp, (long)&queue->ring_ptr, (long)&queue->sqes_ptr);
if (queue->ring_fd < 0) {
return queue->ring_fd;
}
unsigned long cmd_buf_size = round_up(queue_depth * sizeof(struct ublksrv_io_desc), getpagesize());
unsigned long off = UBLK_QUEUES_OFFSET + q_id * round_up(UBLK_MAX_QUEUE_DEPTH * sizeof(struct ublksrv_io_desc), getpagesize());
queue->io_cmd_buf = (char*)mmap(0, cmd_buf_size, PROT_READ, MAP_SHARED | MAP_POPULATE, char_dev_fd, off);
if (queue->io_cmd_buf == MAP_FAILED) {
return -1;
}
for (int j = 0; j < queue_depth; j++) {
struct ublksrv_io_cmd* cmd = (struct ublksrv_io_cmd*)(&sqe->cmd);
cmd->q_id = q_id;
cmd->tag = j;
cmd->addr = 0;
sqe->user_data = j | (sqe->cmd_op << 16);
if (syz_io_uring_submit((long)&queue->params, (long)queue->ring_ptr, (long)queue->sqes_ptr, (long)sqe) < 0) {
return -1;
}
syscall(__NR_io_uring_enter, queue->ring_fd, 1, 0, 0, NULL, _NSIG / 8);
}
}
*queues_ptr_out = (void*)queues;
return 0;
}
static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
{
if (a0 == 0xc || a0 == 0xb) {
char buf[128];
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
return open(buf, O_RDWR, 0);
} else {
unsigned long nb = a1;
char buf[1024];
char* hash;
strncpy(buf, (char*)a0, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
while ((hash = strchr(buf, '#'))) {
*hash = '0' + (char)(nb % 10);
nb /= 10;
}
return open(buf, a2 & ~O_CREAT, 0);
}
}
uint64_t r[9] = {0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0xffffffffffffffff, 0x0};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// openat$ublk_ctrl arguments: [
// fd: const = 0xffffffffffffff9c (8 bytes)
// file: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 75 62 6c 6b 2d 63 6f 6e 74 72 6f 6c 00} (length 0x12)
// }
// flags: const = 0x2 (4 bytes)
// mode: const = 0x0 (2 bytes)
// ]
// returns fd_ublk_ctrl
memcpy((void*)0x200000000000, "/dev/ublk-control\000", 18);
res = syscall(__NR_openat, /*fd=*/0xffffffffffffff9cul, /*file=*/0x200000000000ul, /*flags=*/2, /*mode=*/0);
if (res != -1)
r[0] = res;
// syz_ublk_setup_io_uring arguments: [
// entries: int32 = 0x20 (4 bytes)
// params: ptr[inout, io_uring_params] {
// io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: {00 00 00 00 00 00 00 00 00 00 00 00} (length 0xc)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// }
// ring_params_ptr: ptr[out, ring_params_ptr] {
// ring_params_ptr (resource)
// }
// ring_ptr: ptr[out, ring_ptr] {
// ring_ptr (resource)
// }
// sqes_ptr: ptr[out, sqes_ptr] {
// sqes_ptr (resource)
// }
// ]
// returns fd_io_uring
*(uint32_t*)0x200000000044 = 0;
*(uint32_t*)0x200000000048 = 0;
*(uint32_t*)0x20000000004c = 0;
*(uint32_t*)0x200000000050 = 0;
*(uint32_t*)0x200000000058 = -1;
memset((void*)0x20000000005c, 0, 12);
res = -1;
res = syz_ublk_setup_io_uring(/*entries=*/0x20, /*params=*/0x200000000040, /*ring_params_ptr=*/0x2000000000c0, /*ring_ptr=*/0x200000000100, /*sqes_ptr=*/0x200000000140);
if (res != -1) {
r[1] = res;
r[2] = *(uint64_t*)0x2000000000c0;
r[3] = *(uint64_t*)0x200000000100;
r[4] = *(uint64_t*)0x200000000140;
}
// syz_ublk_add_dev arguments: [
// fd_io_uring: fd_io_uring (resource)
// ring_params_ptr: ring_params_ptr (resource)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// sqe: ptr[inout, io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_ADD_DEV, int32], ublk_ctrl_cmd$add_dev, int64]] {
// io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_ADD_DEV, int32], ublk_ctrl_cmd$add_dev, int64] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ctrl (resource)
// cmd_op: const = 0xc0207504 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: int64 = 0x0 (8 bytes)
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_ctrl_cmd[const[-1, int32], len[addr, int16], ptr64[in, ublk_ctrl_dev_info_new], const[0, int64]] {
// dev_id: const = 0xffffffff (4 bytes)
// queue_id: const = 0xffff (2 bytes)
// len: len = 0x48 (2 bytes)
// addr: ptr[in, ublk_ctrl_dev_info_new] {
// union ublk_ctrl_dev_info_new {
// new_dev: ublk_ctrl_dev_info[const[-1, int32]] {
// nr_hw_queues: int16 = 0x1 (2 bytes)
// queue_depth: int16 = 0x4 (2 bytes)
// state: const = 0x0 (2 bytes)
// pad0: const = 0x0 (2 bytes)
// max_io_buf_bytes: const = 0x1000 (4 bytes)
// dev_id: const = 0xffffffff (4 bytes)
// ublk_pid: pid (resource)
// pad1: const = 0x0 (4 bytes)
// flags: ublk_f_flags = 0x80000 (8 bytes)
// ublk_flags: const = 0x0 (8 bytes)
// owner_uid: const = 0x0 (4 bytes)
// owner_gid: const = 0x0 (4 bytes)
// reserved1: const = 0x0 (8 bytes)
// reserved2: const = 0x0 (8 bytes)
// }
// }
// }
// data: const = 0x0 (8 bytes)
// dev_path_len: const = 0x0 (2 bytes)
// pad: const = 0x0 (2 bytes)
// reserved: const = 0x0 (4 bytes)
// pad2_unused: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x30)
// }
// }
// }
// dev_info_ptr: ptr[out, dev_info_ptr] {
// dev_info_ptr (resource)
// }
// ]
// returns ublk_dev_id
*(uint8_t*)0x200000000200 = 0x2e;
*(uint8_t*)0x200000000201 = 0;
*(uint16_t*)0x200000000202 = 0;
*(uint32_t*)0x200000000204 = r[0];
*(uint32_t*)0x200000000208 = 0xc0207504;
*(uint32_t*)0x20000000020c = 0;
*(uint64_t*)0x200000000210 = 0;
*(uint32_t*)0x200000000218 = 0;
*(uint32_t*)0x20000000021c = 0;
*(uint64_t*)0x200000000220 = 0;
*(uint16_t*)0x200000000228 = 0;
*(uint16_t*)0x20000000022a = 0;
memset((void*)0x20000000022c, 0, 4);
*(uint32_t*)0x200000000230 = -1;
*(uint16_t*)0x200000000234 = -1;
*(uint16_t*)0x200000000236 = 0x48;
*(uint64_t*)0x200000000238 = 0x200000000500;
*(uint16_t*)0x200000000500 = 1;
*(uint16_t*)0x200000000502 = 4;
*(uint16_t*)0x200000000504 = 0;
*(uint16_t*)0x200000000506 = 0;
*(uint32_t*)0x200000000508 = 0x1000;
*(uint32_t*)0x20000000050c = -1;
*(uint32_t*)0x200000000510 = 0;
*(uint32_t*)0x200000000514 = 0;
*(uint64_t*)0x200000000518 = 0x80000;
*(uint64_t*)0x200000000520 = 0;
*(uint32_t*)0x200000000528 = 0;
*(uint32_t*)0x20000000052c = 0;
*(uint64_t*)0x200000000530 = 0;
*(uint64_t*)0x200000000538 = 0;
*(uint64_t*)0x200000000240 = 0;
*(uint16_t*)0x200000000248 = 0;
*(uint16_t*)0x20000000024a = 0;
*(uint32_t*)0x20000000024c = 0;
memset((void*)0x200000000250, 0, 48);
res = -1;
res = syz_ublk_add_dev(/*fd_io_uring=*/r[1], /*ring_params_ptr=*/r[2], /*ring_ptr=*/r[3], /*sqes_ptr=*/r[4], /*sqe=*/0x200000000200, /*dev_info_ptr=*/0x200000000300);
if (res != -1) {
r[5] = res;
r[6] = *(uint64_t*)0x200000000300;
}
// io_uring_enter arguments: [
// fd: fd_io_uring (resource)
// to_submit: int32 = 0x1 (4 bytes)
// min_complete: int32 = 0x1 (4 bytes)
// flags: io_uring_enter_flags = 0x1 (8 bytes)
// sigmask: nil
// size: len = 0x0 (8 bytes)
// ]
syscall(__NR_io_uring_enter, /*fd=*/r[1], /*to_submit=*/1, /*min_complete=*/1, /*flags=IORING_ENTER_GETEVENTS*/1ul, /*sigmask=*/0ul, /*size=*/0ul);
// syz_io_uring_submit$UBLK arguments: [
// ring_params_ptr: ring_params_ptr (resource)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// sqe: ptr[inout, ublk_cmd_sqe_u] {
// union ublk_cmd_sqe_u {
// ublk_set_params_sqe: io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_SET_PARAMS, int32], ublk_ctrl_cmd$set_params, int64] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ctrl (resource)
// cmd_op: const = 0xc0207508 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: int64 = 0x0 (8 bytes)
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_ctrl_cmd[ublk_dev_id, len[addr, int16], ptr64[in, ublk_params], const[0, int64]] {
// dev_id: ublk_dev_id (resource)
// queue_id: const = 0xffff (2 bytes)
// len: len = 0x30 (2 bytes)
// addr: ptr[in, ublk_params] {
// ublk_params {
// len: len = 0x30 (4 bytes)
// types: ublk_param_types = 0x1 (4 bytes)
// basic: ublk_param_basic {
// attrs: ublk_attr_flags = 0x0 (4 bytes)
// logical_bs_shift: int8 = 0x9 (1 bytes)
// physical_bs_shift: int8 = 0xc (1 bytes)
// io_opt_shift: int8 = 0xc (1 bytes)
// io_min_shift: int8 = 0x9 (1 bytes)
// max_sectors: int32 = 0x8 (4 bytes)
// chunk_sectors: int32 = 0x0 (4 bytes)
// dev_sectors: int64 = 0x1000 (8 bytes)
// virt_boundary_mask: int64 = 0x0 (8 bytes)
// }
// discard: ublk_param_discard {
// discard_alignment: int32 = 0x0 (4 bytes)
// discard_granularity: int32 = 0x0 (4 bytes)
// max_discard_sectors: int32 = 0x0 (4 bytes)
// max_write_zeroes_sectors: int32 = 0x0 (4 bytes)
// max_discard_segments: int16 = 0x0 (2 bytes)
// reserved0: const = 0x0 (2 bytes)
// }
// devt: ublk_param_devt {
// char_major: int32 = 0x0 (4 bytes)
// char_minor: int32 = 0x0 (4 bytes)
// disk_major: int32 = 0x0 (4 bytes)
// disk_minor: int32 = 0x0 (4 bytes)
// }
// zoned: ublk_param_zoned {
// max_open_zones: int32 = 0x0 (4 bytes)
// max_active_zones: int32 = 0x0 (4 bytes)
// max_zone_append_sectors: int32 = 0x0 (4 bytes)
// reserved: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x14)
// }
// pad = 0x0 (4 bytes)
// }
// }
// data: const = 0x0 (8 bytes)
// dev_path_len: const = 0x0 (2 bytes)
// pad: const = 0x0 (2 bytes)
// reserved: const = 0x0 (4 bytes)
// pad2_unused: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x30)
// }
// }
// }
// }
// ]
*(uint8_t*)0x200000000600 = 0x2e;
*(uint8_t*)0x200000000601 = 0;
*(uint16_t*)0x200000000602 = 0;
*(uint32_t*)0x200000000604 = r[0];
*(uint32_t*)0x200000000608 = 0xc0207508;
*(uint32_t*)0x20000000060c = 0;
*(uint64_t*)0x200000000610 = 0;
*(uint32_t*)0x200000000618 = 0;
*(uint32_t*)0x20000000061c = 0;
*(uint64_t*)0x200000000620 = 0;
*(uint16_t*)0x200000000628 = 0;
*(uint16_t*)0x20000000062a = 0;
memset((void*)0x20000000062c, 0, 4);
*(uint32_t*)0x200000000630 = r[5];
*(uint16_t*)0x200000000634 = -1;
*(uint16_t*)0x200000000636 = 0x30;
*(uint64_t*)0x200000000638 = 0x200000000680;
*(uint32_t*)0x200000000680 = 0x30;
*(uint32_t*)0x200000000684 = 1;
*(uint32_t*)0x200000000688 = 0;
*(uint8_t*)0x20000000068c = 9;
*(uint8_t*)0x20000000068d = 0xc;
*(uint8_t*)0x20000000068e = 0xc;
*(uint8_t*)0x20000000068f = 9;
*(uint32_t*)0x200000000690 = 8;
*(uint32_t*)0x200000000694 = 0;
*(uint64_t*)0x200000000698 = 0x1000;
*(uint64_t*)0x2000000006a0 = 0;
*(uint32_t*)0x2000000006a8 = 0;
*(uint32_t*)0x2000000006ac = 0;
*(uint32_t*)0x2000000006b0 = 0;
*(uint32_t*)0x2000000006b4 = 0;
*(uint16_t*)0x2000000006b8 = 0;
*(uint16_t*)0x2000000006ba = 0;
*(uint32_t*)0x2000000006bc = 0;
*(uint32_t*)0x2000000006c0 = 0;
*(uint32_t*)0x2000000006c4 = 0;
*(uint32_t*)0x2000000006c8 = 0;
*(uint32_t*)0x2000000006cc = 0;
*(uint32_t*)0x2000000006d0 = 0;
*(uint32_t*)0x2000000006d4 = 0;
memset((void*)0x2000000006d8, 0, 20);
*(uint64_t*)0x200000000640 = 0;
*(uint16_t*)0x200000000648 = 0;
*(uint16_t*)0x20000000064a = 0;
*(uint32_t*)0x20000000064c = 0;
memset((void*)0x200000000650, 0, 48);
syz_io_uring_submit(/*ring_params_ptr=*/r[2], /*ring_ptr=*/r[3], /*sqes_ptr=*/r[4], /*sqe=*/0x200000000600);
// io_uring_enter arguments: [
// fd: fd_io_uring (resource)
// to_submit: int32 = 0x1 (4 bytes)
// min_complete: int32 = 0x1 (4 bytes)
// flags: io_uring_enter_flags = 0x1 (8 bytes)
// sigmask: nil
// size: len = 0x0 (8 bytes)
// ]
syscall(__NR_io_uring_enter, /*fd=*/r[1], /*to_submit=*/1, /*min_complete=*/1, /*flags=IORING_ENTER_GETEVENTS*/1ul, /*sigmask=*/0ul, /*size=*/0ul);
// syz_open_dev$ublk_chdev arguments: [
// dev: ptr[in, buffer] {
// buffer: {2f 64 65 76 2f 75 62 6c 6b 63 23 00} (length 0xc)
// }
// id: ublk_dev_id (resource)
// flags: open_flags = 0x2 (8 bytes)
// ]
// returns fd_ublk_ch
memcpy((void*)0x200000002000, "/dev/ublkc#\000", 12);
res = -1;
res = syz_open_dev(/*dev=*/0x200000002000, /*id=*/r[5], /*flags=O_RDWR*/2);
if (res != -1)
r[7] = res;
// syz_ublk_setup_queues arguments: [
// fd_ublk_ch: fd_ublk_ch (resource)
// dev_info_ptr: dev_info_ptr (resource)
// params: ptr[in, io_uring_params] {
// io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: {00 00 00 00 00 00 00 00 00 00 00 00} (length 0xc)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// }
// queues: ptr[out, array[ublk_queue]] {
// array[ublk_queue] {
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// ublk_queue {
// queue_id: int16 = 0x0 (2 bytes)
// pad1: const = 0x0 (2 bytes)
// fd_ublk_ch: fd_ublk_ch (resource)
// params: io_uring_params {
// sq_entries: int32 = 0x0 (4 bytes)
// cq_entries: int32 = 0x0 (4 bytes)
// flags: io_uring_setup_flags = 0x0 (4 bytes)
// sq_thread_cpu: int32 = 0x0 (4 bytes)
// sq_thread_idle: int32 = 0x0 (4 bytes)
// features: int32 = 0x0 (4 bytes)
// wq_fd: fd_io_uring (resource)
// resv: buffer: (DirOut)
// sq_off: io_sqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// dropped: int32 = 0x0 (4 bytes)
// array: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// cq_off: io_cqring_offsets {
// head: int32 = 0x0 (4 bytes)
// tail: int32 = 0x0 (4 bytes)
// ring_mask: int32 = 0x0 (4 bytes)
// ring_entries: int32 = 0x0 (4 bytes)
// overflow: int32 = 0x0 (4 bytes)
// cqes: int32 = 0x0 (4 bytes)
// flags: int32 = 0x0 (4 bytes)
// resv1: int32 = 0x0 (4 bytes)
// user_addr: int64 = 0x0 (8 bytes)
// }
// }
// ring_fd: fd_io_uring (resource)
// pad2: const = 0x0 (4 bytes)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// io_cmd_buf: intptr = 0x0 (8 bytes)
// }
// }
// }
// nr_queues: const = 0x1 (8 bytes)
// sqe: ptr[inout, io_uring_sqe_uring[fd_ublk_ch, const[UBLK_U_IO_FETCH_REQ, int32], ublk_io_cmd$fetch_req, user_data[UBLK_U_IO_FETCH_REQ]]] {
// io_uring_sqe_uring[fd_ublk_ch, const[UBLK_U_IO_FETCH_REQ, int32], ublk_io_cmd$fetch_req, user_data[UBLK_U_IO_FETCH_REQ]] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ch (resource)
// cmd_op: const = 0xc0107520 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: user_data[UBLK_U_IO_FETCH_REQ] {
// tag: int64 = 0x0 (0 bytes)
// cmd: const = 0xc0107520 (0 bytes)
// pad: const = 0x0 (8 bytes)
// }
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_io_cmd[int16[0:UBLK_MAX_HW_QUEUES], int16[0:UBLK_MAX_QUEUE_DEPTH], const[0, int32], ptr64[inout, ublk_io_buf_u]] {
// q_id: int16 = 0x0 (2 bytes)
// tag: int16 = 0x0 (2 bytes)
// result: const = 0x0 (4 bytes)
// addr: nil
// pad: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x40)
// }
// }
// }
// ublk_queues_ptr: nil
// ]
*(uint32_t*)0x200000000704 = 0;
*(uint32_t*)0x200000000708 = 0;
*(uint32_t*)0x20000000070c = 0;
*(uint32_t*)0x200000000710 = 0;
*(uint32_t*)0x200000000718 = -1;
memset((void*)0x20000000071c, 0, 12);
*(uint32_t*)0x20000000080c = 0;
*(uint32_t*)0x200000000810 = 0;
*(uint32_t*)0x200000000814 = 0;
*(uint32_t*)0x200000000818 = 0;
*(uint32_t*)0x200000000820 = -1;
*(uint32_t*)0x2000000008ac = 0;
*(uint32_t*)0x2000000008b0 = 0;
*(uint32_t*)0x2000000008b4 = 0;
*(uint32_t*)0x2000000008b8 = 0;
*(uint32_t*)0x2000000008c0 = -1;
*(uint32_t*)0x20000000094c = 0;
*(uint32_t*)0x200000000950 = 0;
*(uint32_t*)0x200000000954 = 0;
*(uint32_t*)0x200000000958 = 0;
*(uint32_t*)0x200000000960 = -1;
*(uint32_t*)0x2000000009ec = 0;
*(uint32_t*)0x2000000009f0 = 0;
*(uint32_t*)0x2000000009f4 = 0;
*(uint32_t*)0x2000000009f8 = 0;
*(uint32_t*)0x200000000a00 = -1;
*(uint8_t*)0x200000000900 = 0x2e;
*(uint8_t*)0x200000000901 = 0;
*(uint16_t*)0x200000000902 = 0;
*(uint32_t*)0x200000000904 = r[7];
*(uint32_t*)0x200000000908 = 0xc0107520;
*(uint32_t*)0x20000000090c = 0;
*(uint64_t*)0x200000000910 = 0;
*(uint32_t*)0x200000000918 = 0;
*(uint32_t*)0x20000000091c = 0;
STORE_BY_BITMASK(uint64_t, , 0x200000000920, 0, 0, 16);
STORE_BY_BITMASK(uint64_t, , 0x200000000920, 0xc0107520, 16, 32);
STORE_BY_BITMASK(uint64_t, , 0x200000000920, 0, 48, 16);
*(uint16_t*)0x200000000928 = 0;
*(uint16_t*)0x20000000092a = 0;
memset((void*)0x20000000092c, 0, 4);
*(uint16_t*)0x200000000930 = 0;
*(uint16_t*)0x200000000932 = 0;
*(uint32_t*)0x200000000934 = 0;
*(uint64_t*)0x200000000938 = 0;
memset((void*)0x200000000940, 0, 64);
syz_ublk_setup_queues(/*fd_ublk_ch=*/r[7], /*dev_info_ptr=*/r[6], /*params=*/0x200000000700, /*queues=*/0x200000000800, /*nr_queues=*/1, /*sqe=*/0x200000000900, /*ublk_queues_ptr=*/0);
// getpid arguments: [
// ]
// returns pid
res = syscall(__NR_getpid);
if (res != -1)
r[8] = res;
// syz_io_uring_submit$UBLK arguments: [
// ring_params_ptr: ring_params_ptr (resource)
// ring_ptr: ring_ptr (resource)
// sqes_ptr: sqes_ptr (resource)
// sqe: ptr[inout, ublk_cmd_sqe_u] {
// union ublk_cmd_sqe_u {
// ublk_start_dev_sqe: io_uring_sqe_uring[fd_ublk_ctrl, const[UBLK_U_CMD_START_DEV, int32], ublk_ctrl_cmd$start_dev, int64] {
// opcode: const = 0x2e (1 bytes)
// flags: iosqe_flags = 0x0 (1 bytes)
// ioprio: const = 0x0 (2 bytes)
// fd: fd_ublk_ctrl (resource)
// cmd_op: const = 0xc0207506 (4 bytes)
// pad1: const = 0x0 (4 bytes)
// addr: const = 0x0 (8 bytes)
// len: const = 0x0 (4 bytes)
// uring_flags: uring_cmd_flags = 0x0 (4 bytes)
// user_data: int64 = 0x0 (8 bytes)
// buf_index_unused: const = 0x0 (2 bytes)
// ioring_personality_id: ioring_personality_id (resource)
// pad_unused: buffer: {00 00 00 00} (length 0x4)
// cmd: ublk_ctrl_cmd[ublk_dev_id, const[0, int16], const[0, int64], pid] {
// dev_id: ublk_dev_id (resource)
// queue_id: const = 0xffff (2 bytes)
// len: const = 0x0 (2 bytes)
// addr: const = 0x0 (8 bytes)
// data: pid (resource)
// dev_path_len: const = 0x0 (2 bytes)
// pad: const = 0x0 (2 bytes)
// reserved: const = 0x0 (4 bytes)
// pad2_unused: buffer: {00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x30)
// pad = 0x0 (4 bytes)
// }
// }
// }
// }
// ]
*(uint8_t*)0x200000000c00 = 0x2e;
*(uint8_t*)0x200000000c01 = 0;
*(uint16_t*)0x200000000c02 = 0;
*(uint32_t*)0x200000000c04 = r[0];
*(uint32_t*)0x200000000c08 = 0xc0207506;
*(uint32_t*)0x200000000c0c = 0;
*(uint64_t*)0x200000000c10 = 0;
*(uint32_t*)0x200000000c18 = 0;
*(uint32_t*)0x200000000c1c = 0;
*(uint64_t*)0x200000000c20 = 0;
*(uint16_t*)0x200000000c28 = 0;
*(uint16_t*)0x200000000c2a = 0;
memset((void*)0x200000000c2c, 0, 4);
*(uint32_t*)0x200000000c30 = r[5];
*(uint16_t*)0x200000000c34 = -1;
*(uint16_t*)0x200000000c36 = 0;
*(uint64_t*)0x200000000c38 = 0;
*(uint32_t*)0x200000000c40 = r[8];
*(uint16_t*)0x200000000c44 = 0;
*(uint16_t*)0x200000000c46 = 0;
*(uint32_t*)0x200000000c48 = 0;
memset((void*)0x200000000c4c, 0, 48);
syz_io_uring_submit(/*ring_params_ptr=*/r[2], /*ring_ptr=*/r[3], /*sqes_ptr=*/r[4], /*sqe=*/0x200000000c00);
// io_uring_enter arguments: [
// fd: fd_io_uring (resource)
// to_submit: int32 = 0x1 (4 bytes)
// min_complete: int32 = 0x1 (4 bytes)
// flags: io_uring_enter_flags = 0x1 (8 bytes)
// sigmask: nil
// size: len = 0x0 (8 bytes)
// ]
syscall(__NR_io_uring_enter, /*fd=*/r[1], /*to_submit=*/1, /*min_complete=*/1, /*flags=IORING_ENTER_GETEVENTS*/1ul, /*sigmask=*/0ul, /*size=*/0ul);
return 0;
}
Reply: Based on the analysis of the bug report and the kernel source code, here is the assessment of the security impact:
**1. Bug Mechanism & Exploitability:**
The bug is a lockdep warning (`WARNING: suspicious RCU usage`) triggered in `ublk_try_buf_match()` when it calls `mas_walk(&mas)` to traverse the `ub->buf_tree` maple tree. The warning occurs because the function does not explicitly hold the RCU read lock (`rcu_read_lock()`) or the maple tree lock during the traversal.
However, this is a false positive in terms of actual memory safety. Write modifications to `ub->buf_tree` (such as adding or removing buffers) are protected by `ublk_lock_buf_tree()`, which calls `blk_mq_freeze_queue()`. Freezing the queue ensures that all in-flight block requests are fully drained before any modifications can proceed. Since `ublk_try_buf_match()` is executed as part of dispatching an in-flight request, it is guaranteed that no concurrent writes to the maple tree can occur while it is running.
Because there is no actual data race or memory corruption, the bug is **not exploitable**.
**2. Denial of Service (DoS):**
In production kernel builds, debug options like `CONFIG_PROVE_RCU` (lockdep) are disabled. Therefore, this warning will not even be evaluated or printed, and it will not cause a kernel panic, hang, or resource leak. The system will continue to operate normally. Thus, it does not pose a Denial of Service risk.
**3. Accessibility:**
To trigger this code path, a user must interact with the `/dev/ublk-control` character device to set up a userspace block device and configure it with the `UBLK_F_SHMEM_ZC` flag. By default, standard Linux distributions restrict access to `/dev/ublk-control` to the `root` user (or a privileged group like `disk`). While the kernel code does have provisions for unprivileged device creation (`UBLK_F_UNPRIVILEGED_DEV`), it still fundamentally relies on the user having permission to open the device node in the first place. Therefore, it is typically **not accessible from unprivileged processes or user namespaces**.
**Conclusion:**
The bug is a benign missing lockdep annotation (missing `rcu_read_lock()`) that does not lead to any memory corruption or system instability. It has no security impact on production environments.
|
| 6/2 |
2026/09/15 03:16 |
llm |
expert |
0m
Model:
Tokens:
input: 26416
output: 22
thoughts: 445
Thoughts: **RCU Suspicion in the Maple Tree: A Deep Dive**
Okay, so I've got this syzkaller report, "WARNING: suspicious RCU usage" popping up right in the `lib/maple_tree.c:720` region. The stack trace is pretty clear; it's pinpointing `mas_root`, which is called within `mas_start`. The issue seems to propagate through the `mas_walk` function and into the `ublk_try_buf_match` function within `drivers/block/ublk_drv.c:5639` and also within `ublk_init_iod`, ultimately getting triggered within `ublk_dispatch_req` of the same module. From there, it seems to be propagating within the io_uring subsystem, and eventually leading to `do_exit`.
My immediate thought is to zero in on that `ublk_try_buf_match` call. Since the RCU warning is originating in the maple tree code, I need to check how `ublk_try_buf_match` is using the tree or if it's interacting with any shared resources. It looks like the issue is related to how the maple tree is being accessed/modified within the context of the UBLK driver, and the RCU is somehow getting tripped up. I need to understand how these routines are interacting and if there's a potential race condition or improper synchronization within the UBLK driver code that's causing this suspicious RCU usage. I'll focus on what resources are being held at which point. Let's see if we can find the cause in this area.
|
| 7/2 |
2026/09/15 03:16 |
tool |
grepper |
0m
Args: map[Expression:ublk_try_buf_match]
Results: map[Output:drivers/block/ublk_drv.c=358=static void ublk_stop_dev_unlocked(struct ublk_device *ub);
drivers/block/ublk_drv.c:359:static bool ublk_try_buf_match(struct ublk_device *ub, struct request *rq,
drivers/block/ublk_drv.c-360- u32 *buf_idx, u32 *buf_off);
--
drivers/block/ublk_drv.c=506=static void ublk_init_iod(struct ublk_queue *ubq, struct request *req,
--
drivers/block/ublk_drv.c-520-
drivers/block/ublk_drv.c:521: if (ublk_try_buf_match(ubq->dev, req, &buf_idx, &buf_off)) {
drivers/block/ublk_drv.c-522- iod->op_flags |= UBLK_IO_F_SHMEM_ZC;
--
drivers/block/ublk_drv.c=5613=static void ublk_buf_cleanup(struct ublk_device *ub)
--
drivers/block/ublk_drv.c-5620-/* Check if request pages match a registered shared memory buffer */
drivers/block/ublk_drv.c:5621:static bool ublk_try_buf_match(struct ublk_device *ub,
drivers/block/ublk_drv.c-5622- struct request *rq,
]
|
| 8/2 |
2026/09/15 03:16 |
llm |
expert |
0m
Model:
Tokens:
input: 27619
output: 42
thoughts: 16
Thoughts: Here's a summary of my thought process, as if I were analyzing that code:
**Diving into `ublk_try_buf_match`**
Okay, time to dissect `ublk_try_buf_match`. I need to understand its core function. My initial thought is to quickly identify its purpose, likely a buffering mechanism. "Try" suggests an attempt, likely a conditional test before something happens. "Match" implies comparison or verification. I'm guessing it relates to comparing a request buffer against some pre-existing data or condition. I'll need to look for what exactly the "buf" parameter refers to. Does it indicate a data buffer, a control structure, or something else entirely? I'm hoping there's a good comment block to explain. The implementation details are the key: What's the matching criteria? What are the potential outcomes? Does it return a boolean, an offset, or some other status? Let's get to the code.
|
| 9/2 |
2026/09/15 03:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_try_buf_match]
Results: map[SourceCode:5620: /* Check if request pages match a registered shared memory buffer */
5621: static bool ublk_try_buf_match(struct ublk_device *ub,
5622: struct request *rq,
5623: u32 *buf_idx, u32 *buf_off)
5624: {
5625: struct req_iterator iter;
5626: struct bio_vec bv;
5627: int index = -1;
5628: unsigned long expected_offset = 0;
5629: bool first = true;
5630:
5631: rq_for_each_bvec(bv, rq, iter) {
5632: unsigned long pfn = page_to_pfn(bv.bv_page);
5633: unsigned long end_pfn = pfn +
5634: ((bv.bv_offset + bv.bv_len - 1) >> PAGE_SHIFT);
5635: struct ublk_buf_range *range;
5636: unsigned long off;
5637: MA_STATE(mas, &ub->buf_tree, pfn, pfn);
5638:
5639: range = mas_walk(&mas);
5640: if (!range)
5641: return false;
5642:
5643: /* verify all pages in this bvec fall within the range */
5644: if (end_pfn > mas.last)
5645: return false;
5646:
5647: off = range->base_offset +
5648: (pfn - mas.index) * PAGE_SIZE + bv.bv_offset;
5649:
5650: if (first) {
5651: /* Read-only buffer can't serve READ (kernel writes) */
5652: if ((range->flags & UBLK_SHMEM_BUF_READ_ONLY) &&
5653: req_op(rq) != REQ_OP_WRITE)
5654: return false;
5655: index = range->buf_index;
5656: expected_offset = off;
5657: *buf_off = off;
5658: first = false;
5659: } else {
5660: if (range->buf_index != index)
5661: return false;
5662: if (off != expected_offset)
5663: return false;
5664: }
5665: expected_offset += bv.bv_len;
5666: }
5667:
5668: if (first)
5669: return false;
5670:
5671: *buf_idx = index;
5672: return true;
5673: }
SourceFile:drivers/block/ublk_drv.c]
|
| 10/2 |
2026/09/15 03:16 |
llm |
expert |
0m
Model:
Tokens:
input: 28687
output: 40
thoughts: 392
Thoughts: **RCU Dereference Check in ublk Context**
Okay, so I'm seeing this `suspicious rcu_dereference_check()` warning in `mas_root`, specifically called from `mas_start`, which then calls `mas_state_walk` and finally `mas_walk`. The crucial point is that `mas_walk` is being called from within `ublk_try_buf_match` at line 5639. My immediate concern is the locking context. `mas_walk` demands either the RCU read lock *or* the maple tree lock be held. I need to trace this back and understand the calling context to see if this is being handled correctly.
Let's start tracing the call chain. `ublk_try_buf_match` is called from `ublk_init_iod`, which in turn is called from `ublk_dispatch_req`. I was originally thinking `ublk_dispatch_req` might be called from within the block layer's queue request handling, like `ublk_queue_rq` or something similar, but that's not what the stack trace is showing.
The stack trace clearly indicates that `ublk_dispatch_req` is being called directly from `tctx_task_work_run` via the `io_uring` task work, which is not what I expected. This is a bit unusual to see a dispatch function like this, called directly from task work. Now I need to check the definition of `ublk_dispatch_req` in `drivers/block/ublk_drv.c` to see if there's anything obvious there.
|
| 11/2 |
2026/09/15 03:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_dispatch_req]
Results: map[SourceCode:1770: static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req)
1771: {
1772: unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
1773: u16 tag = req->tag;
1774: struct ublk_io *io = &ubq->ios[tag];
1775:
1776: ublk_setup_iod(ubq, req);
1777: pr_devel("%s: complete: qid %d tag %d io_flags %x addr %llx\n",
1778: __func__, ubq->q_id, req->tag, io->flags,
1779: ublk_get_iod(ubq, req->tag)->addr);
1780:
1781: /*
1782: * Task is exiting if either:
1783: *
1784: * (1) current != io->task.
1785: * io_uring_cmd_complete_in_task() tries to run task_work
1786: * in a workqueue if cmd's task is PF_EXITING.
1787: *
1788: * (2) current->flags & PF_EXITING.
1789: */
1790: if (unlikely(current != io->task || current->flags & PF_EXITING)) {
1791: __ublk_abort_rq(ubq, req);
1792: return;
1793: }
1794:
1795: if (ublk_need_get_data(ubq) && ublk_need_map_req(req)) {
1796: /*
1797: * We have not handled UBLK_IO_NEED_GET_DATA command yet,
1798: * so immediately pass UBLK_IO_RES_NEED_GET_DATA to ublksrv
1799: * and notify it.
1800: */
1801: io->flags |= UBLK_IO_FLAG_NEED_GET_DATA;
1802: pr_devel("%s: need get data. qid %d tag %d io_flags %x\n",
1803: __func__, ubq->q_id, req->tag, io->flags);
1804: ublk_complete_io_cmd(io, req, UBLK_IO_RES_NEED_GET_DATA,
1805: issue_flags);
1806: return;
1807: }
1808:
1809: if (!ublk_start_io(ubq, req, io))
1810: return;
1811:
1812: if (ublk_support_auto_buf_reg(ubq) && blk_rq_has_data(req)) {
1813: ublk_auto_buf_dispatch(ubq, req, io, io->cmd, issue_flags);
1814: } else {
1815: ublk_init_req_ref(ubq, io);
1816: ublk_complete_io_cmd(io, req, UBLK_IO_RES_OK, issue_flags);
1817: }
1818: }
SourceFile:drivers/block/ublk_drv.c]
|
| 12/2 |
2026/09/15 03:16 |
llm |
expert |
0m
Model:
Tokens:
input: 30375
output: 40
thoughts: 19
Thoughts: Here's a summary of my thought process, as if I were thinking it:
**Digging into the Ublk Layer**
Okay, I'm tracing through this ublk code, and I'm seeing something interesting. I'm focusing on the I/O setup path, specifically the function `ublk_setup_iod`. I was expecting a direct initialization of I/O data structures. Then I noticed, hang on a second, that function *calls* `ublk_init_iod`. That's not what I initially anticipated. It looks like it's taking a two-step approach to I/O data structure initialization. Curious. I need to figure out the specific responsibilities of each of these functions to understand why they are separated. My initial thought process was too simplistic, I need to revisit my assumption about the control flow.
|
| 13/2 |
2026/09/15 03:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_setup_iod]
Results: map[SourceCode:1511: static void ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
1512: {
1513: u32 ublk_op;
1514:
1515: switch (req_op(req)) {
1516: case REQ_OP_READ:
1517: ublk_op = UBLK_IO_OP_READ;
1518: break;
1519: case REQ_OP_WRITE:
1520: ublk_op = UBLK_IO_OP_WRITE;
1521: break;
1522: case REQ_OP_FLUSH:
1523: ublk_op = UBLK_IO_OP_FLUSH;
1524: break;
1525: case REQ_OP_DISCARD:
1526: ublk_op = UBLK_IO_OP_DISCARD;
1527: break;
1528: case REQ_OP_WRITE_ZEROES:
1529: ublk_op = UBLK_IO_OP_WRITE_ZEROES;
1530: break;
1531: default:
1532: ublk_setup_iod_zoned(ubq, req);
1533: return;
1534: }
1535:
1536: ublk_init_iod(ubq, req, ublk_op, blk_rq_sectors(req), blk_rq_pos(req));
1537: }
SourceFile:drivers/block/ublk_drv.c]
|
| 14/2 |
2026/09/15 03:16 |
llm |
expert |
0m
Model:
Tokens:
input: 31060
output: 40
thoughts: 20
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Kernel Block Device Initialization: Understanding the Sequence**
Okay, so I'm looking at the initialization flow for this Ublk device. My focus is narrowing in on the `ublk_init_iod` function. I know this is where the I/O descriptor gets set up, but I need to understand its relationship to the broader system. I'm immediately noticing that within `ublk_init_iod`, there's a critical call to `ublk_try_buf_match`. This is the point of interest. What is the intent of `ublk_try_buf_match`? What does it attempt to match and why? Is this a buffer allocation strategy, a checking phase to make sure data is handled correctly? I know this likely involves ensuring that buffers associated with the I/O requests are compatible with the specific device's capabilities, especially in terms of alignment and size. It must also ensure that the buffers conform to the driver's requirements. This connection is key. I need to trace this function and see exactly what checks are made and how the buffer association is verified. That understanding is essential to understanding the correct operation of the entire system. I'm focusing on the buffer matching aspect because it can be an area where subtle issues can arise, potentially leading to performance bottlenecks or even data corruption if handled incorrectly.
|
| 15/2 |
2026/09/15 03:16 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_init_iod]
Results: map[SourceCode: 506: static void ublk_init_iod(struct ublk_queue *ubq, struct request *req,
507: uint8_t ublk_op, uint32_t nr_sectors,
508: uint64_t start_sector)
509: {
510: struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag);
511: struct ublk_io *io = &ubq->ios[req->tag];
512:
513: iod->op_flags = ublk_op | ublk_req_build_flags(req);
514: iod->nr_sectors = nr_sectors;
515: iod->start_sector = start_sector;
516:
517: /* Try shmem zero-copy match before setting addr */
518: if (ublk_support_shmem_zc(ubq) && blk_rq_has_data(req)) {
519: u32 buf_idx, buf_off;
520:
521: if (ublk_try_buf_match(ubq->dev, req, &buf_idx, &buf_off)) {
522: iod->op_flags |= UBLK_IO_F_SHMEM_ZC;
523: iod->addr = ublk_shmem_zc_addr(buf_idx, buf_off);
524: return;
525: }
526: }
527:
528: iod->addr = io->buf.addr;
529: }
SourceFile:drivers/block/ublk_drv.c]
|
| 16/2 |
2026/09/15 03:16 |
llm |
expert |
0m
Model:
Tokens:
input: 31895
output: 42
thoughts: 87
Thoughts: Okay, here's my take on those thoughts, framed from my own perspective:
**Investigating a Potentially Problematic RCU Dereference**
Alright, this `suspicious rcu_dereference_check()` warning in `mas_root` is a red flag. It's triggered within a call chain that includes `mas_start`, `mas_state_walk`, and finally `mas_walk`. My immediate concern is the context of `mas_walk`. I know that function has critical requirements: it *needs* either the RCU read lock or the maple tree lock held to be safe. That's the core of what protects it from concurrent modifications.
So, the warning suggests that one of those protection mechanisms is *missing* or, at least, being used incorrectly in the path the code is taking.
Since that looks like an rcu_dereference issue, let me revisit `ublk_try_buf_match`. I need to re-examine that function and the surrounding logic to make sure there isn't a race condition happening, or a case where the right locks aren't being acquired. It is the most probable suspect as it is the first call, so, if I can find the problem in there, that is where I will begin debugging.
|
| 17/2 |
2026/09/15 03:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_try_buf_match]
Results: map[SourceCode:5620: /* Check if request pages match a registered shared memory buffer */
5621: static bool ublk_try_buf_match(struct ublk_device *ub,
5622: struct request *rq,
5623: u32 *buf_idx, u32 *buf_off)
5624: {
5625: struct req_iterator iter;
5626: struct bio_vec bv;
5627: int index = -1;
5628: unsigned long expected_offset = 0;
5629: bool first = true;
5630:
5631: rq_for_each_bvec(bv, rq, iter) {
5632: unsigned long pfn = page_to_pfn(bv.bv_page);
5633: unsigned long end_pfn = pfn +
5634: ((bv.bv_offset + bv.bv_len - 1) >> PAGE_SHIFT);
5635: struct ublk_buf_range *range;
5636: unsigned long off;
5637: MA_STATE(mas, &ub->buf_tree, pfn, pfn);
5638:
5639: range = mas_walk(&mas);
5640: if (!range)
5641: return false;
5642:
5643: /* verify all pages in this bvec fall within the range */
5644: if (end_pfn > mas.last)
5645: return false;
5646:
5647: off = range->base_offset +
5648: (pfn - mas.index) * PAGE_SIZE + bv.bv_offset;
5649:
5650: if (first) {
5651: /* Read-only buffer can't serve READ (kernel writes) */
5652: if ((range->flags & UBLK_SHMEM_BUF_READ_ONLY) &&
5653: req_op(rq) != REQ_OP_WRITE)
5654: return false;
5655: index = range->buf_index;
5656: expected_offset = off;
5657: *buf_off = off;
5658: first = false;
5659: } else {
5660: if (range->buf_index != index)
5661: return false;
5662: if (off != expected_offset)
5663: return false;
5664: }
5665: expected_offset += bv.bv_len;
5666: }
5667:
5668: if (first)
5669: return false;
5670:
5671: *buf_idx = index;
5672: return true;
5673: }
SourceFile:drivers/block/ublk_drv.c]
|
| 18/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 33096
output: 17
thoughts: 253
Thoughts: **My Concerns Regarding ublk_try_buf_match and Maple Tree Safety**
Okay, so `ublk_try_buf_match` is calling `mas_walk(&mas)` and it's immediately apparent that it's *not* holding either the `rcu_read_lock()` or any kind of mutex lock that I'd expect to protect the maple tree traversal. That's a red flag. Let's see if `ublk_try_buf_match` itself is called with any locks held at all, just to be sure.
The stack trace… ah, right, it shows that the calling context, `tctx_task_work_run`, *does* hold the `uring_lock`. Good, but that's not the critical one. Crucially, it *does not* hold the `rcu_read_lock()`.
Wait a minute... `mas_walk`, as I recall, absolutely *requires* the RCU read lock because it's traversing that maple tree. If that lock isn't held, then the `mas_walk` operation could be racing with concurrent modifications to the tree. That could lead to some really nasty bugs – potential use-after-free conditions, memory corruption, the whole nine yards. We need to be very careful here.
So, the next step is crystal clear: I need to investigate how `ub->buf_tree` is actually being modified. I need to understand the write side to determine if there's a race happening.
|
| 19/2 |
2026/09/15 03:17 |
tool |
grepper |
0m
Args:
Results: map[Output:drivers/block/ublk_drv.c=311=struct ublk_device {
--
drivers/block/ublk_drv.c-344- /* shared memory zero copy */
drivers/block/ublk_drv.c:345: struct maple_tree buf_tree;
drivers/block/ublk_drv.c-346- struct ida buf_ida;
--
drivers/block/ublk_drv.c=4664=static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
--
drivers/block/ublk_drv.c-4778- mutex_init(&ub->cancel_mutex);
drivers/block/ublk_drv.c:4779: mt_init(&ub->buf_tree);
drivers/block/ublk_drv.c-4780- ida_init(&ub->buf_ida);
--
drivers/block/ublk_drv.c=5315=static int ublk_char_dev_permission(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5348-*/
drivers/block/ublk_drv.c:5349:static unsigned int ublk_lock_buf_tree(struct ublk_device *ub)
drivers/block/ublk_drv.c-5350-{
--
drivers/block/ublk_drv.c-5359-
drivers/block/ublk_drv.c:5360:static void ublk_unlock_buf_tree(struct ublk_device *ub, unsigned int memflags)
drivers/block/ublk_drv.c-5361-{
--
drivers/block/ublk_drv.c=5368=static void ublk_buf_erase_ranges(struct ublk_device *ub, int buf_index)
drivers/block/ublk_drv.c-5369-{
drivers/block/ublk_drv.c:5370: MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
drivers/block/ublk_drv.c-5371- struct ublk_buf_range *range;
--
drivers/block/ublk_drv.c=5383=static int __ublk_ctrl_reg_buf(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5408-
drivers/block/ublk_drv.c:5409: ret = mtree_insert_range(&ub->buf_tree, pfn,
drivers/block/ublk_drv.c-5410- pfn + (i - start),
--
drivers/block/ublk_drv.c=5429=static int ublk_ctrl_reg_buf(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5480-
drivers/block/ublk_drv.c:5481: memflags = ublk_lock_buf_tree(ub);
drivers/block/ublk_drv.c-5482-
--
drivers/block/ublk_drv.c-5494-
drivers/block/ublk_drv.c:5495: ublk_unlock_buf_tree(ub, memflags);
drivers/block/ublk_drv.c-5496- kvfree(pages);
--
drivers/block/ublk_drv.c-5499-err_unlock:
drivers/block/ublk_drv.c:5500: ublk_unlock_buf_tree(ub, memflags);
drivers/block/ublk_drv.c-5501-err_unpin:
--
drivers/block/ublk_drv.c=5541=static bool __ublk_shmem_remove_ranges(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5543-{
drivers/block/ublk_drv.c:5544: MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
drivers/block/ublk_drv.c-5545- struct ublk_buf_range *range;
--
drivers/block/ublk_drv.c=5590=static int ublk_ctrl_unreg_buf(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5602-
drivers/block/ublk_drv.c:5603: memflags = ublk_lock_buf_tree(ub);
drivers/block/ublk_drv.c-5604-
--
drivers/block/ublk_drv.c-5608-
drivers/block/ublk_drv.c:5609: ublk_unlock_buf_tree(ub, memflags);
drivers/block/ublk_drv.c-5610- return ret;
--
drivers/block/ublk_drv.c=5613=static void ublk_buf_cleanup(struct ublk_device *ub)
--
drivers/block/ublk_drv.c-5615- ublk_shmem_remove_ranges(ub, -1);
drivers/block/ublk_drv.c:5616: mtree_destroy(&ub->buf_tree);
drivers/block/ublk_drv.c-5617- ida_destroy(&ub->buf_ida);
--
drivers/block/ublk_drv.c=5621=static bool ublk_try_buf_match(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5636- unsigned long off;
drivers/block/ublk_drv.c:5637: MA_STATE(mas, &ub->buf_tree, pfn, pfn);
drivers/block/ublk_drv.c-5638-
]
|
| 20/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 35166
output: 42
thoughts: 25
Thoughts: Okay, here's my interpretation of those thoughts, summarized from an expert's perspective, presented as a personal thought process:
**Diving into ublk_lock_buf_tree and ublk_unlock_buf_tree**
Alright, time to get my hands dirty. I need to take a look at `ublk_lock_buf_tree` and `ublk_unlock_buf_tree`. That immediately tells me we're dealing with buffer management within the Ublk framework, and specifically, the locking/unlocking of those buffers within a B-tree-like structure. Given the names, I'm guessing these are core functions for ensuring data integrity and preventing race conditions during I/O operations. I'm probably going to be digging into how concurrency is handled around those buffers, how the locks are implemented (spinlocks, mutexes, RCU maybe?), and the overall performance implications of this locking strategy. I wonder if there's any cleverness in the locking hierarchy to optimize for common access patterns or avoid deadlocks. This is where the rubber meets the road in terms of high-performance block I/O. I need to figure out what type of buffers these functions refer to and also identify the locking strategy. This will involve the nuances of the data structures involved, and the conditions under which these functions are called. Hopefully the code is well-commented and easy to trace. I'm expecting to see calls to memory barriers or other synchronization primitives in there as well. Time to get to work!
|
| 21/2 |
2026/09/15 03:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_lock_buf_tree]
Results: map[SourceCode:5341: /*
5342: * Lock for maple tree modification: acquire ub->mutex, then freeze queue
5343: * if device is started. If device is not yet started, only mutex is
5344: * needed since no I/O path can access the tree.
5345: *
5346: * This ordering (mutex -> freeze) is safe because ublk_stop_dev_unlocked()
5347: * already holds ub->mutex when calling del_gendisk() which freezes the queue.
5348: */
5349: static unsigned int ublk_lock_buf_tree(struct ublk_device *ub)
5350: {
5351: unsigned int memflags = 0;
5352:
5353: mutex_lock(&ub->mutex);
5354: if (ub->ub_disk)
5355: memflags = blk_mq_freeze_queue(ub->ub_disk->queue);
5356:
5357: return memflags;
5358: }
SourceFile:drivers/block/ublk_drv.c]
|
| 22/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 35864
output: 40
thoughts: 227
Thoughts: **My Current Ublk Thoughts**
Okay, so `ublk_lock_buf_tree` is the culprit, freezing the queue when the device starts. That makes sense, in that, when the queue is frozen, no new I/O is allowed and all existing operations finish. I was digging into `ublk_try_buf_match` because I remember it being called from `ublk_init_iod` which is called from `ublk_dispatch_req`. `ublk_dispatch_req` is the interesting part here, as it's triggered from `tctx_task_work_run`, which the io_uring subsystem calls when a user submits a request. But if the queue is frozen, `blk_mq_freeze_queue` waits for all the inflight requests to complete. Hmmm... wait a minute. `ublk_dispatch_req` isn't a *block layer* dispatch, is it? It's handling a command *from the ublk server*, the userspace daemon. So, `ublk_dispatch_req` is being called when the ublk server issues a command to get a request. I should really check that function again...
|
| 23/2 |
2026/09/15 03:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_dispatch_req]
Results: map[SourceCode:1770: static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req)
1771: {
1772: unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
1773: u16 tag = req->tag;
1774: struct ublk_io *io = &ubq->ios[tag];
1775:
1776: ublk_setup_iod(ubq, req);
1777: pr_devel("%s: complete: qid %d tag %d io_flags %x addr %llx\n",
1778: __func__, ubq->q_id, req->tag, io->flags,
1779: ublk_get_iod(ubq, req->tag)->addr);
1780:
1781: /*
1782: * Task is exiting if either:
1783: *
1784: * (1) current != io->task.
1785: * io_uring_cmd_complete_in_task() tries to run task_work
1786: * in a workqueue if cmd's task is PF_EXITING.
1787: *
1788: * (2) current->flags & PF_EXITING.
1789: */
1790: if (unlikely(current != io->task || current->flags & PF_EXITING)) {
1791: __ublk_abort_rq(ubq, req);
1792: return;
1793: }
1794:
1795: if (ublk_need_get_data(ubq) && ublk_need_map_req(req)) {
1796: /*
1797: * We have not handled UBLK_IO_NEED_GET_DATA command yet,
1798: * so immediately pass UBLK_IO_RES_NEED_GET_DATA to ublksrv
1799: * and notify it.
1800: */
1801: io->flags |= UBLK_IO_FLAG_NEED_GET_DATA;
1802: pr_devel("%s: need get data. qid %d tag %d io_flags %x\n",
1803: __func__, ubq->q_id, req->tag, io->flags);
1804: ublk_complete_io_cmd(io, req, UBLK_IO_RES_NEED_GET_DATA,
1805: issue_flags);
1806: return;
1807: }
1808:
1809: if (!ublk_start_io(ubq, req, io))
1810: return;
1811:
1812: if (ublk_support_auto_buf_reg(ubq) && blk_rq_has_data(req)) {
1813: ublk_auto_buf_dispatch(ubq, req, io, io->cmd, issue_flags);
1814: } else {
1815: ublk_init_req_ref(ubq, io);
1816: ublk_complete_io_cmd(io, req, UBLK_IO_RES_OK, issue_flags);
1817: }
1818: }
SourceFile:drivers/block/ublk_drv.c]
|
| 24/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 37290
output: 53
thoughts: 183
Thoughts: **My Analysis of the `ublk_dispatch_req` Execution Path**
Okay, so I was under the impression that `ublk_dispatch_req` was being called directly from `ublk_queue_rq`. But the stack trace is telling a different story here. It's clear that isn't the case. Looking at the trace, I can see that `ublk_dispatch_req` is being called indirectly, specifically, after the `ublk_init_iod` function. Furthermore, I see that the function `tctx_task_work_run` is also calling `ublk_dispatch_req`. This immediately tells me that `ublk_dispatch_req` is executed as task work.
Now, that's crucial. Task work means asynchronous processing, which explains why I wasn't seeing it called directly from the queuing function. My next question is, who is responsible for scheduling this task work? Understanding the scheduling source will be vital for debugging and performance tuning, so I need to find the specific code path that queues this particular task work. This is the next thing I need to investigate.
|
| 25/2 |
2026/09/15 03:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_dispatch_req SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_cmd_list_tw_cb SourceFile:drivers/block/ublk_drv.c SourceLine:2104] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_cmd_tw_cb SourceFile:drivers/block/ublk_drv.c SourceLine:2067]] TruncatedOutput:false]
|
| 26/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 37896
output: 42
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Deep Dive into `ublk_cmd_tw_cb`**
Okay, let's get right into this. I need to understand what's happening with `ublk_cmd_tw_cb`. This callback is critical in the Ublk context, obviously, and I need to solidify my grasp on its role. I'm assuming we're dealing with the block layer interface here, likely related to target/writeback handling, or possibly even the I/O path itself, though I'd need more context to be certain. Given that it's a callback, I need to understand *when* it's invoked, *what* arguments it receives, and *what* its expected return value means in terms of the broader operation.
The "tw" portion implies that the callback is possibly involved with the target/writeback, so likely the callback is invoked when a transfer is complete or a specific writeback decision has been made. I need to trace this back. Is it a function pointer in the `ublk_cmd` structure? Or something else? Where is it assigned? Is it assigned during the command submission, or is it bound later, perhaps based on a particular I/O flow or device characteristic? I’ll have to look for its definition and cross-references. I expect to be able to understand the data structures involved, the locks it might acquire or release, and whether there are any potential race conditions. Is this callback responsible for releasing resources, updating completion status, or triggering the next stage in the I/O pipeline? The deeper questions are: what is the performance impact of this callback? Are there any potential bottlenecks I need to be aware of? Are there areas for optimization? Let's dive in.
|
| 27/2 |
2026/09/15 03:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_cmd_tw_cb]
Results: map[SourceCode:2061: static void ublk_cmd_tw_cb(struct io_tw_req tw_req, io_tw_token_t tw)
2062: {
2063: struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
2064: struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
2065: struct ublk_queue *ubq = pdu->ubq;
2066:
2067: ublk_dispatch_req(ubq, pdu->req);
2068: }
SourceFile:drivers/block/ublk_drv.c]
|
| 28/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 38539
output: 55
thoughts: 14
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Unraveling the `ublk_cmd_tw_cb` Enqueue**
Okay, so I'm digging into the `ublk` code again. My main question right now is: specifically, which paths lead to the enqueueing of `ublk_cmd_tw_cb`? I know it's a critical callback, but I want to understand the *exact* trigger points. I need to trace the execution flow and identify the functions or modules responsible for calling this function. Are we looking at hardware completion paths, software queuing, or something else entirely? Understanding the initiator of this callback is key to grasping the overall command lifecycle within the `ublk` framework and pinpointing potential bottlenecks or areas for optimization. I need to be sure I'm not missing any edge cases; I really need to get a clear picture of *all* possible scenarios that result in this callback getting queued. It's a fundamental aspect of this architecture, and I need a precise answer.
|
| 29/2 |
2026/09/15 03:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_cmd_tw_cb SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ublk_queue_cmd SourceFile:drivers/block/ublk_drv.c SourceLine:2091]] TruncatedOutput:false]
|
| 30/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 38908
output: 40
thoughts: 12
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `ublk_queue_cmd`**
Alright, let's get down to brass tacks. I need to take a look at `ublk_queue_cmd`. I'm already visualizing the likely function signature, the expected input arguments, and the internal workings of that function in the context of the underlying ublk architecture. I'm immediately thinking about how it fits into the request lifecycle. Is it responsible for enqueuing the command into the hardware queue, or is it dealing with pre-processing, translation, or possibly both? I need to quickly ascertain the level of abstraction it's operating at. I expect to see interactions with the blk-mq framework, likely calls into `blk_mq_submit_request` or similar primitives. The critical path through that function is what I want to understand. What are the spinlock interactions? How are completion callbacks managed? Any potential contention points? And how does it handle error conditions? Specifically, I'm going to be looking for how it deals with the case where the underlying ublk device is busy. I need to understand the implications of a queue full scenario and the backpressure mechanisms in place. And of course, I'm expecting some pretty close interaction with the ublk device driver's internal state. This is where the rubber meets the road.
|
| 31/2 |
2026/09/15 03:17 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_queue_cmd]
Results: map[SourceCode:2085: static void ublk_queue_cmd(struct ublk_queue *ubq, struct request *rq)
2086: {
2087: struct io_uring_cmd *cmd = ubq->ios[rq->tag].cmd;
2088: struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
2089:
2090: pdu->req = rq;
2091: io_uring_cmd_complete_in_task(cmd, ublk_cmd_tw_cb);
2092: }
SourceFile:drivers/block/ublk_drv.c]
|
| 32/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 39446
output: 53
thoughts: 20
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already deep in the weeds of block device driver design:
**My Internal Mapping of `ublk_queue_cmd`**
Alright, so I'm thinking about the flow, and it's crystal clear: `ublk_queue_cmd` is definitely the function that gets called *directly* from `ublk_queue_rq`. That's the key linkage. `ublk_queue_rq` is where the request comes in, the scheduling happens (or is initiated), and then, bam, to actually submit the command to the underlying device or virtualization layer, it calls `ublk_queue_cmd`. It's a fundamental connection in this architecture. Knowing that call relationship is crucial for debugging, performance analysis, and any kind of modification to the queueing mechanism itself. I need to keep that firmly in mind. It's a clean and efficient way to handle request submission within the ublk framework.
|
| 33/2 |
2026/09/15 03:17 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_queue_cmd SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_queue_rq SourceFile:drivers/block/ublk_drv.c SourceLine:2215]] TruncatedOutput:false]
|
| 34/2 |
2026/09/15 03:17 |
llm |
expert |
0m
Model:
Tokens:
input: 39801
output: 42
thoughts: 1199
Thoughts: **Analyzing Concurrency in the Ublk Block Layer**
Okay, let's break this down. My thought process is circling around a potential race condition within the Ublk block layer, specifically involving the `ublk->buf_tree` data structure and how it's accessed concurrently with I/O operations.
The typical flow is: a block layer request triggers `ublk_queue_rq`, which queues task work using `io_uring_cmd_complete_in_task`. That task work, running in the Ublk server context, then calls `ublk_dispatch_req`, eventually leading to `ublk_try_buf_match`. This function is key because it calls `mas_walk(&mas)` on `ub->buf_tree`. I remember that this `ub->buf_tree` is modified by `ublk_ctrl_reg_buf` and `ublk_ctrl_unreg_buf`. Here's where it gets interesting.
Those control functions take `ublk_lock_buf_tree(ub)`, which acquires `ub->mutex` and, critically, freezes the block queue using `blk_mq_freeze_queue`. Freezing the queue is meant to serialize access to the block device. It effectively waits for all in-flight requests to complete before it allows any further modification.
The question I'm grappling with is: does this freezing mechanism *really* prevent `ublk_try_buf_match` from running concurrently with those control operations? Let's trace it carefully.
`blk_mq_freeze_queue` decrements `q->q_usage_counter` to zero and waits. `ublk_queue_rq` is called *after* a request has successfully entered the queue, meaning it holds a reference to `q_usage_counter`. When `ublk_queue_rq` is called, it returns `BLK_STS_OK` and queues the task work. The request is now considered "in-flight". `blk_mq_freeze_queue` *should* wait for this in-flight request to finish, which happens when the Ublk server processes the request and calls `blk_mq_end_request`. If a request is in-flight (meaning `ublk_queue_rq` has been called but `blk_mq_end_request` hasn't), `blk_mq_freeze_queue` is *supposed* to block.
If `blk_mq_freeze_queue` blocks, this means either `ublk_ctrl_reg_buf` or `ublk_ctrl_unreg_buf` will also block, waiting for the current request to complete. However, `ublk_try_buf_match` is called from the task work, which executes *after* `ublk_queue_rq` returns. So, the request *is* in-flight while `ublk_try_buf_match` is running! Therefore, `blk_mq_freeze_queue` should be forced to wait for `ublk_try_buf_match` (and the entire request) to complete. That *should* mean that `ublk_ctrl_reg_buf` cannot modify `ub->buf_tree` while `ublk_try_buf_match` is running.
I need to verify the behavior of `blk_mq_freeze_queue`. Yes, `blk_mq_freeze_queue` does wait for the `q_usage_counter` to drop to zero. The counter is incremented before `ublk_queue_rq` is called, and decremented when the request completes via `blk_mq_end_request`. Any request that has passed `ublk_queue_rq` will keep this counter elevated until completion. Hence, `blk_mq_freeze_queue` will block until *all* such requests are finished. Because `ublk_try_buf_match` runs as part of the request processing (before completion), `blk_mq_freeze_queue` *will* wait for `ublk_try_buf_match` to finish. This means that `ub->buf_tree` CANNOT be modified concurrently with `ublk_try_buf_match`!
But wait, if it can't be modified concurrently, why is lockdep complaining? Lockdep warns because `mas_walk` checks if either `rcu_read_lock` is held, or the tree lock is held. Since neither is held, lockdep gets upset. But, is there an actual race? The lockdep complaint might be a false positive. Let me consider an edge case. What if `ublk_ctrl_reg_buf` is called *before* the device is started? `ublk_lock_buf_tree` checks `ub->ub_disk`. If `ub->ub_disk` is NULL
|
| 35/2 |
2026/09/15 03:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ctrl_start_dev]
Results: map[SourceCode:4454: static int ublk_ctrl_start_dev(struct ublk_device *ub,
4455: const struct ublksrv_ctrl_cmd *header)
4456: {
4457: const struct ublk_param_basic *p = &ub->params.basic;
4458: int ublksrv_pid = (int)header->data[0];
4459: struct queue_limits lim = {
4460: .logical_block_size = 1 << p->logical_bs_shift,
4461: .physical_block_size = 1 << p->physical_bs_shift,
4462: .io_min = 1 << p->io_min_shift,
4463: .io_opt = 1 << p->io_opt_shift,
4464: .max_hw_sectors = p->max_sectors,
4465: .chunk_sectors = p->chunk_sectors,
4466: .virt_boundary_mask = p->virt_boundary_mask,
4467: .max_segments = USHRT_MAX,
4468: .max_segment_size = UINT_MAX,
4469: .dma_alignment = 3,
4470: };
4471: struct gendisk *disk;
4472: int ret = -EINVAL;
4473:
4474: if (ublksrv_pid <= 0)
4475: return -EINVAL;
4476: if (!(ub->params.types & UBLK_PARAM_TYPE_BASIC))
4477: return -EINVAL;
4478:
4479: if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) {
4480: const struct ublk_param_discard *pd = &ub->params.discard;
4481:
4482: lim.discard_alignment = pd->discard_alignment;
4483: lim.discard_granularity = pd->discard_granularity;
4484: lim.max_hw_discard_sectors = pd->max_discard_sectors;
4485: lim.max_write_zeroes_sectors = pd->max_write_zeroes_sectors;
4486: lim.max_discard_segments = pd->max_discard_segments;
4487: }
4488:
4489: if (ub->params.types & UBLK_PARAM_TYPE_ZONED) {
4490: const struct ublk_param_zoned *p = &ub->params.zoned;
4491:
4492: if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED))
4493: return -EOPNOTSUPP;
4494:
4495: lim.features |= BLK_FEAT_ZONED;
4496: lim.max_active_zones = p->max_active_zones;
4497: lim.max_open_zones = p->max_open_zones;
4498: lim.max_hw_zone_append_sectors = p->max_zone_append_sectors;
4499: }
4500:
4501: if (ub->params.basic.attrs & UBLK_ATTR_VOLATILE_CACHE) {
4502: lim.features |= BLK_FEAT_WRITE_CACHE;
4503: if (ub->params.basic.attrs & UBLK_ATTR_FUA)
4504: lim.features |= BLK_FEAT_FUA;
4505: }
4506:
4507: if (ub->params.basic.attrs & UBLK_ATTR_ROTATIONAL)
4508: lim.features |= BLK_FEAT_ROTATIONAL;
4509:
4510: if (ub->params.types & UBLK_PARAM_TYPE_DMA_ALIGN)
4511: lim.dma_alignment = ub->params.dma.alignment;
4512:
4513: if (ub->params.types & UBLK_PARAM_TYPE_SEGMENT) {
4514: lim.seg_boundary_mask = ub->params.seg.seg_boundary_mask;
4515: lim.max_segment_size = ub->params.seg.max_segment_size;
4516: lim.max_segments = ub->params.seg.max_segments;
4517: }
4518:
4519: if (ub->params.types & UBLK_PARAM_TYPE_INTEGRITY) {
4520: const struct ublk_param_integrity *p = &ub->params.integrity;
4521: int pi_tuple_size = ublk_integrity_pi_tuple_size(p->csum_type);
4522:
4523: lim.max_integrity_segments =
4524: p->max_integrity_segments ?: USHRT_MAX;
4525: lim.integrity = (struct blk_integrity) {
4526: .flags = ublk_integrity_flags(p->flags),
4527: .csum_type = ublk_integrity_csum_type(p->csum_type),
4528: .metadata_size = p->metadata_size,
4529: .pi_offset = p->pi_offset,
4530: .interval_exp = p->interval_exp,
4531: .tag_size = p->tag_size,
4532: .pi_tuple_size = pi_tuple_size,
4533: };
4534: }
4535:
4536: if (ublk_wait_dev_ready_and_lock(ub))
4537: return -EINTR;
4538:
4539: if (!ublk_validate_user_pid(ub, ublksrv_pid)) {
4540: ret = -EINVAL;
4541: goto out_unlock;
4542: }
4543: if (ub->dev_info.state == UBLK_S_DEV_LIVE ||
4544: test_bit(UB_STATE_USED, &ub->state)) {
4545: ret = -EEXIST;
4546: goto out_unlock;
4547: }
4548:
4549: disk = blk_mq_alloc_disk(&ub->tag_set, &lim, NULL);
4550: if (IS_ERR(disk)) {
4551: ret = PTR_ERR(disk);
4552: goto out_unlock;
4553: }
4554: sprintf(disk->disk_name, "ublkb%d", ub->ub_number);
4555: disk->fops = &ub_fops;
4556: disk->private_data = ub;
4557:
4558: ub->dev_info.ublksrv_pid = ub->ublksrv_tgid;
4559: ub->ub_disk = disk;
4560:
4561: ublk_apply_params(ub);
4562:
4563: /*
4564: * Suppress partition scan to avoid potential IO hang.
4565: *
4566: * If ublk server error occurs during partition scan, the IO may
4567: * wait while holding ub->mutex, which can deadlock with other
4568: * operations that need the mutex. Defer partition scan to async
4569: * work.
4570: * For unprivileged daemons, keep GD_SUPPRESS_PART_SCAN set
4571: * permanently.
4572: */
4573: set_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
4574:
4575: ublk_get_device(ub);
4576: ub->dev_info.state = UBLK_S_DEV_LIVE;
4577:
4578: if (ublk_dev_is_zoned(ub)) {
4579: ret = ublk_revalidate_disk_zones(ub);
4580: if (ret)
4581: goto out_put_cdev;
4582: }
4583:
4584: ret = add_disk(disk);
4585: if (ret)
4586: goto out_put_cdev;
4587:
4588: set_bit(UB_STATE_USED, &ub->state);
4589:
4590: /* Skip partition scan if disabled by user */
4591: if (ub->dev_info.flags & UBLK_F_NO_AUTO_PART_SCAN) {
4592: /* Not clear for unprivileged daemons, see comment above */
4593: if (!ub->unprivileged_daemons)
4594: clear_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
4595: } else {
4596: /* Schedule async partition scan for trusted daemons */
4597: if (!ub->unprivileged_daemons)
4598: schedule_work(&ub->partition_scan_work);
4599: }
4600:
4601: out_put_cdev:
4602: if (ret) {
4603: ublk_detach_disk(ub);
4604: ublk_put_device(ub);
4605: }
4606: if (ret)
4607: put_disk(disk);
4608: out_unlock:
4609: mutex_unlock(&ub->mutex);
4610: return ret;
4611: }
SourceFile:drivers/block/ublk_drv.c]
|
| 36/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 44729
output: 42
thoughts: 1462
Thoughts: **Analyzing Potential Race Conditions in UBLK Device Registration and Buffer Matching**
Okay, let's break this down. My initial thought is that `ublk_ctrl_start_dev` creates the disk and sets `ub->ub_disk`, holding `ub->mutex` throughout. But then I see `ublk_lock_buf_tree` calls `mutex_lock(&ub->mutex)` and I start wondering, could `ublk_ctrl_reg_buf` be called concurrently with `ublk_ctrl_start_dev`? No, that's impossible; they both use `ub->mutex`.
Could `ublk_try_buf_match` be called while `ublk_ctrl_reg_buf` is running? If `ublk_ctrl_reg_buf` runs *after* `ublk_ctrl_start_dev`, `ub->ub_disk` isn't NULL, and `blk_mq_freeze_queue` is called. Freeze waits for in-flight requests to complete. `ublk_try_buf_match` runs in task work, meaning it completes *before* the request does. So, `blk_mq_freeze_queue` waits for `ublk_try_buf_match` to finish. Therefore, `ublk_try_buf_match` cannot run concurrently with `ublk_ctrl_reg_buf`. Wait, what if it happens *before*?
`ublk_try_buf_match` comes from `ublk_dispatch_req`, which processes a block request. Block requests only get submitted *after* `add_disk(disk)` is called in `ublk_ctrl_start_dev`. So `ublk_try_buf_match` *must* run *after* `ublk_ctrl_start_dev` completes. `ublk_try_buf_match` is guaranteed to run when `ub->ub_disk` isn't NULL, and `blk_mq_freeze_queue` waits for the buffer matching to complete. Seems like there's no race. The maple tree is protected by the queue freeze!
But, why is lockdep complaining?! It doesn't see the implicit synchronization through `blk_mq_freeze_queue`. It only looks for `rcu_read_lock` or tree locks. Neither is there, so it warns. Is this just a false positive?
Let's check if there's any other way for these calls to run concurrently. What if `ublk_ctrl_reg_buf` is called, freezes the queue, modifies the tree, and then unfreezes it? Can `ublk_try_buf_match` run *during* the tree modification? No, because the queue is frozen, so no new requests are entering the queue, and all existing requests have completed.
Wait, what if a request is submitted, `ublk_queue_rq` is called, task work is queued, and *then* the queue is frozen? If `ublk_queue_rq` has returned, the request is in-flight, and `blk_mq_freeze_queue` will wait for this request to complete. Completion happens when the ublk server sends a completion command. The ublk server is the one *calling* `ublk_ctrl_reg_buf`! If the server is blocked in `blk_mq_freeze_queue` it can't process requests, causing a deadlock!
If the ublk server is single-threaded, it deadlocks. If it's multi-threaded, another thread could complete the requests. But... `ublk_try_buf_match` is called from `ublk_dispatch_req`, and that's *task work* in the ublk server thread! If that server thread is blocked in `ublk_ctrl_reg_buf`, waiting for the queue to freeze, it can't execute task work!
And `blk_mq_freeze_queue` waits for `q_usage_counter` to drop to zero. If task work is queued, and the server thread is blocked, the work won't run. The request never gets dispatched, and never completes! Deadlock! So is `ublk_ctrl_reg_buf` meant to be called while requests are in flight? The ublk server must quiesce or ensure no in-flight requests. If it doesn't, it deadlocks and becomes a DoS.
But wait, the lockdep warning is about RCU usage, is there a race? Is it possible that `ublk_try_buf_match` runs concurrently with `ublk_ctrl_reg_buf`? Wait, `ublk_ctrl_reg_buf` takes `ub->mutex`. However, `ublk_try_buf_match` *does not*
|
| 37/2 |
2026/09/15 03:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ctrl_reg_buf]
Results: map[SourceCode:5424: /*
5425: * Register a shared memory buffer for zero-copy I/O.
5426: * Pins pages, builds PFN maple tree, freezes/unfreezes the queue
5427: * internally. Returns buffer index (>= 0) on success.
5428: */
5429: static int ublk_ctrl_reg_buf(struct ublk_device *ub,
5430: struct ublksrv_ctrl_cmd *header)
5431: {
5432: void __user *argp = (void __user *)(unsigned long)header->addr;
5433: struct ublk_shmem_buf_reg buf_reg;
5434: unsigned long nr_pages;
5435: struct page **pages = NULL;
5436: unsigned int gup_flags;
5437: unsigned int memflags;
5438: long pinned;
5439: int index;
5440: int ret;
5441:
5442: if (!ublk_dev_support_shmem_zc(ub))
5443: return -EOPNOTSUPP;
5444:
5445: memset(&buf_reg, 0, sizeof(buf_reg));
5446: if (copy_from_user(&buf_reg, argp,
5447: min_t(size_t, header->len, sizeof(buf_reg))))
5448: return -EFAULT;
5449:
5450: if (buf_reg.flags & ~UBLK_SHMEM_BUF_READ_ONLY)
5451: return -EINVAL;
5452:
5453: if (buf_reg.reserved)
5454: return -EINVAL;
5455:
5456: if (!buf_reg.len || buf_reg.len > UBLK_SHMEM_BUF_SIZE_MAX ||
5457: !PAGE_ALIGNED(buf_reg.len) || !PAGE_ALIGNED(buf_reg.addr))
5458: return -EINVAL;
5459:
5460: nr_pages = buf_reg.len >> PAGE_SHIFT;
5461:
5462: /* Pin pages before any locks (may sleep) */
5463: pages = kvmalloc_objs(*pages, nr_pages);
5464: if (!pages)
5465: return -ENOMEM;
5466:
5467: gup_flags = FOLL_LONGTERM;
5468: if (!(buf_reg.flags & UBLK_SHMEM_BUF_READ_ONLY))
5469: gup_flags |= FOLL_WRITE;
5470:
5471: pinned = pin_user_pages_fast(buf_reg.addr, nr_pages, gup_flags, pages);
5472: if (pinned < 0) {
5473: ret = pinned;
5474: goto err_free_pages;
5475: }
5476: if (pinned != nr_pages) {
5477: ret = -EFAULT;
5478: goto err_unpin;
5479: }
5480:
5481: memflags = ublk_lock_buf_tree(ub);
5482:
5483: index = ida_alloc_max(&ub->buf_ida, USHRT_MAX, GFP_KERNEL);
5484: if (index < 0) {
5485: ret = index;
5486: goto err_unlock;
5487: }
5488:
5489: ret = __ublk_ctrl_reg_buf(ub, pages, nr_pages, index, buf_reg.flags);
5490: if (ret) {
5491: ida_free(&ub->buf_ida, index);
5492: goto err_unlock;
5493: }
5494:
5495: ublk_unlock_buf_tree(ub, memflags);
5496: kvfree(pages);
5497: return index;
5498:
5499: err_unlock:
5500: ublk_unlock_buf_tree(ub, memflags);
5501: err_unpin:
5502: unpin_user_pages(pages, pinned);
5503: err_free_pages:
5504: kvfree(pages);
5505: return ret;
5506: }
SourceFile:drivers/block/ublk_drv.c]
|
| 38/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 48529
output: 53
thoughts: 329
Thoughts: **Analyzing Potential Data Race in Ublk Buffer Management**
Okay, so I'm looking at this `ublk_lock_buf_tree` function, and it's calling `blk_mq_freeze_queue`. Right, that means `ublk_ctrl_reg_buf` is going to block inside `blk_mq_freeze_queue` until all the in-flight requests are done. The important thing is that it *hasn't* touched the buffer tree yet while it's blocked. And simultaneously, `ublk_try_buf_match` is potentially running for an in-flight request. Crucially, `ublk_try_buf_match` is executing while the tree is *untouched*! The tree modification only happens *after* `blk_mq_freeze_queue` finishes, which is also *after* `ublk_try_buf_match` is done. So, in theory, there shouldn't be any concurrent modification, right?
But wait... what about `ublk_ctrl_unreg_buf`? Does that also modify the tree? Ah, yes, it calls `ublk_lock_buf_tree`, which also freezes the queue. So, this queue freeze mechanism should be preventing any concurrent modifications. Hmm, but lockdep doesn't know about this. It's only seeing that `ublk_try_buf_match` is using `mas_walk` without either holding an `rcu_read_lock` or the tree lock. It's going to flag this as a potential data race. Is this a false positive?
Okay, let's trace this back. `ublk_try_buf_match` is called from `ublk_init_iod`, which is, in turn, called from `ublk_dispatch_req`. So, that's one path. Are there any *other* code paths that could be modifying the buffer tree? What about `ublk_buf_cleanup`? Need to check that. I need to be sure that the buffer tree modifications are properly synchronized with everything else that's looking at it. This looks like it needs some more careful analysis.
|
| 39/2 |
2026/09/15 03:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_buf_cleanup SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_cdev_rel SourceFile:drivers/block/ublk_drv.c SourceLine:4333]] TruncatedOutput:false]
|
| 40/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 49447
output: 43
thoughts: 125
Thoughts: **My Analysis of the `ublk_cdev_rel` Function's Safety**
Okay, so `ublk_cdev_rel` is triggered when the character device I'm dealing with is closed. The immediate implications are clear: the device is brought to a halt and its associated queue is cleaned up. Logically, this implies that no requests should be actively in-flight anymore. But hold on, I need to consider a potential race condition. What if `ublk_try_buf_match` gets invoked from `ublk_init_iod` for a request that was enqueued *before* the device's shutdown sequence began? That could be a problem.
The device stopping itself involves a call to `ublk_stop_dev_unlocked`. That’s the critical function to examine. I need to dive into `ublk_stop_dev_unlocked` now to understand its mechanisms and ensure proper synchronization and request handling during the device's stop process. This is where the potential vulnerability lies, and I must verify if the requests are handled correctly prior to queue cleanup.
|
| 41/2 |
2026/09/15 03:18 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_stop_dev_unlocked]
Results: map[SourceCode:2994: static void ublk_stop_dev_unlocked(struct ublk_device *ub)
2995: __must_hold(&ub->mutex)
2996: {
2997: struct gendisk *disk;
2998:
2999: if (ub->dev_info.state == UBLK_S_DEV_DEAD)
3000: return;
3001:
3002: if (ublk_nosrv_dev_should_queue_io(ub))
3003: ublk_force_abort_dev(ub);
3004: del_gendisk(ub->ub_disk);
3005: disk = ublk_detach_disk(ub);
3006: put_disk(disk);
3007: }
SourceFile:drivers/block/ublk_drv.c]
|
| 42/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 50093
output: 55
thoughts: 646
Thoughts: **Analyzing Potential Concurrency Issues with `ublk_try_buf_match`**
Okay, let's break this down. My initial thought was about that lockdep warning. `del_gendisk` effectively pauses the queue and waits for all in-flight requests to finish, meaning `ublk_buf_cleanup` and `ublk_try_buf_match` shouldn't be running concurrently. This implies no concurrent modification of the maple tree while `ublk_try_buf_match` is executing. Therefore, the lockdep warning appears to be a false positive, since synchronization is handled by `blk_mq_freeze_queue`, not RCU or a lock.
Hmm, a false positive makes me think about exploitability. But, if there's no actual race, and the tree isn't concurrently modified, there's no way to trigger a problem.
Alright, let's explore if there are other ways to invoke `ublk_try_buf_match`. I know it's called from `ublk_init_iod`, which is, in turn, called from `ublk_setup_iod`, which originates from `ublk_dispatch_req`. This function is then called from `ublk_cmd_tw_cb` or `ublk_cmd_list_tw_cb`, which are both task work functions. These are queued by `ublk_queue_cmd`, which itself is called from `ublk_queue_rq`. So, it's tied to block requests, and those requests are synchronized with tree modifications via `blk_mq_freeze_queue`. Solid.
But, what if `ublk_ctrl_reg_buf` is called *before* `ublk_ctrl_start_dev`? In this scenario, `ub->ub_disk` is NULL, which means there's no queue freeze. But, hang on... if `ublk_ctrl_start_dev` hasn't happened yet, the block device doesn't exist, so there can be no block requests. So, `ublk_try_buf_match` can't run. That's good.
What about if `ublk_ctrl_reg_buf` is called *during* `ublk_ctrl_start_dev`? They both grab `ub->mutex`, so they can't run concurrently. Safe again.
Let's think. What about after `ublk_ctrl_start_dev` but before the device is fully initialized? `ublk_ctrl_start_dev` sets `ub->ub_disk` before releasing `ub->mutex`. So, any `ublk_ctrl_reg_buf` calls that happen after will see `ub->ub_disk` as not NULL and the queue is frozen.
Any other ways to bypass the queue freeze? If `ublk_ctrl_reg_buf` fails to freeze the queue somehow? But `blk_mq_freeze_queue` waits; it doesn't fail.
Wait a minute, what if `ublk_try_buf_match` *is* called from somewhere else? Let me check all the call paths of `ublk_try_buf_match` again...
|
| 43/2 |
2026/09/15 03:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_try_buf_match SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_init_iod SourceFile:drivers/block/ublk_drv.c SourceLine:521]] TruncatedOutput:false]
|
| 44/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 51551
output: 53
thoughts: 21
Thoughts: Here's my take on the key thoughts, summarized as if they were my own:
**Focus on ublk_init_iod and Its Callers**
Alright, so the core of my thinking right now revolves around a specific function: `ublk_init_iod`. My analysis needs to zoom in on it, like a laser. The primary objective? To understand it, yes, but more importantly, to identify *who* calls it. Knowing the upstream dependencies, the calling context – that's where the real insights lie. It's the `ublk_init_iod` function itself, along with the relationships formed when other functions and modules call it that matter. That's where I can gain a real understanding.
|
| 45/2 |
2026/09/15 03:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_init_iod SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_setup_iod SourceFile:drivers/block/ublk_drv.c SourceLine:1536] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_setup_iod_zoned SourceFile:drivers/block/ublk_drv.c SourceLine:750] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_setup_iod_zoned SourceFile:drivers/block/ublk_drv.c SourceLine:758]] TruncatedOutput:false]
|
| 46/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 51962
output: 53
thoughts: 22
Thoughts: Okay, here's my interpretation of those thoughts, summarized from a first-person expert perspective:
**My Perspective on ublk_setup_iod Functions**
Alright, so the focus is on the `ublk_setup_iod` and `ublk_setup_iod_zoned` functions, specifically. Thinking about these, I immediately go to the core: how are we initializing and setting up I/O descriptors within the Ublk (Userspace Block) framework? These functions are critical for preparing the data structures that will govern I/O operations, making them the entry points to establishing the context for these operations.
My assumption is that the `_zoned` variant introduces considerations for zoned block devices. That means we have to account for zone management – how to handle sequential write, the read/write restrictions, and the overall state transitions of these zones. That adds a layer of complexity to the general I/O setup. So, I need to keep in mind the differences in the setup parameters and logic between the regular and zoned versions.
Essentially, I'm analyzing how these functions are handling the fundamental connection between the userspace I/O request and the underlying block device. I need to understand how the I/O data is laid out, how the operation is defined, and how we handle the device-specific requirements. Any issues at this stage will ripple through the whole process, so thoroughness is crucial. I'll need to look at how we're ensuring data integrity and consistency as we translate from the userspace request into something the kernel can work with. Debugging errors with device setup can be difficult.
|
| 47/2 |
2026/09/15 03:18 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_setup_iod SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:__ublk_batch_prep_dispatch SourceFile:drivers/block/ublk_drv.c SourceLine:1830] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_dispatch_req SourceFile:drivers/block/ublk_drv.c SourceLine:1776]] TruncatedOutput:false]
|
| 48/2 |
2026/09/15 03:18 |
llm |
expert |
0m
Model:
Tokens:
input: 52502
output: 38
thoughts: 435
Thoughts: **My Analysis of ublk_batch_prep_dispatch's Synchronization**
Okay, let me break this down. `__ublk_batch_prep_dispatch` is triggered by `ublk_cmd_list_tw_cb`, and both are invoked through task work, which is itself queued by `ublk_queue_rq`. So, the typical scenario is that it's all synchronized with the queue freeze because it's essentially handled in a task work context that's queued after the request.
But wait a minute... I need to consider a potential race condition here. Could it happen that `ublk_queue_rq` gets called, the task work is queued, *and then* the request gets completed before that task work actually gets a chance to run? If that happens, then `q_usage_counter` gets decremented. If `q_usage_counter` goes to zero, `blk_mq_freeze_queue` *could* potentially return, right? Then `ublk_ctrl_reg_buf` could modify the tree while the task work is still running.
So, I need to investigate if the request can be completed before the task work even starts. Let's trace it through. `ublk_queue_rq` queues the task work using `io_uring_cmd_complete_in_task`, and then it returns `BLK_STS_OK`. The block layer still considers the request as in-flight. Now, who actually completes the request? It's either `ublk_complete_io_cmd` or the ublk server itself sending a completion command, right? Aha, but the ublk server's completion command, like `UBLK_IO_COMMIT_AND_FETCH_REQ`, completes the *previous* request and fetches a new one. The request I'm concerned about is the *new* one that's just been submitted. Could the block layer complete it?
What about aborting the request? `ublk_abort_requests` gets called when the device is stopped or the queue is torn down. But, critically, `ublk_abort_requests` is *called after* `del_gendisk`, which means the queue is already frozen before that. So that's not the issue. What about a timeout? If the request times out, `ublk_timeout` gets called. I'll need to look at the interactions between those functions to be sure.
|
| 49/2 |
2026/09/15 03:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_timeout]
Results: map[SourceCode:2119: static enum blk_eh_timer_return ublk_timeout(struct request *rq)
2120: {
2121: struct ublk_queue *ubq = rq->mq_hctx->driver_data;
2122: pid_t tgid = ubq->dev->ublksrv_tgid;
2123: struct task_struct *p;
2124: struct pid *pid;
2125:
2126: if (!(ubq->flags & UBLK_F_UNPRIVILEGED_DEV))
2127: return BLK_EH_RESET_TIMER;
2128:
2129: if (unlikely(!tgid))
2130: return BLK_EH_RESET_TIMER;
2131:
2132: rcu_read_lock();
2133: pid = find_vpid(tgid);
2134: p = pid_task(pid, PIDTYPE_PID);
2135: if (p)
2136: send_sig(SIGKILL, p, 0);
2137: rcu_read_unlock();
2138: return BLK_EH_DONE;
2139: }
SourceFile:drivers/block/ublk_drv.c]
|
| 50/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 53852
output: 42
thoughts: 480
Thoughts: **My Analysis of the `ublk_timeout` and Lockdep Warning**
Okay, let me break this down. When `ublk_timeout` hits, especially for an unprivileged device, it's just sending a SIGKILL to the ublk server. It *doesn't* actually complete the request itself. So, that timeout isn't resolving the request. I was thinking, could the request be completed *before* the task work even kicks off?
Ah, right, `ublk_dispatch_req` handles completion, whether it's a failure or a normal finish. But then I remember `ublk_try_buf_match` gets called *inside* `ublk_dispatch_req`, and *before* the request is marked as complete. So while that matching function is running, the request is still in-flight, and that `q_usage_counter` is elevated. That explains why `blk_mq_freeze_queue` can't return then.
But what if `ublk_queue_rq` returns an error status? Like `BLK_STS_RESOURCE`? If it does, the task work doesn't get queued. And if it returns `BLK_STS_OK`, the task work is queued, the request is in-flight. So the request stays in-flight as long as the task work is pending or running. And `blk_mq_freeze_queue` will *definitely* wait.
Now, wait a minute, is there *any* scenario where `ublk_try_buf_match` runs without that `q_usage_counter` being raised? No, it's only called when dispatching a `struct request`, so it's always elevated. Therefore, that lockdep warning is just a false positive.
But if it's a false positive, why did syzbot flag it? Lockdep is only checking for `rcu_read_lock` or tree locks. It doesn't have visibility into `blk_mq_freeze_queue`.
Okay, is there a way an unprivileged user can even trigger this warning? Yes, the syzbot reproducer uses `UBLK_F_USER_COPY` and creates a ublk device.
And wait, can an unprivileged user even *create* a ublk device? Time to check `ublk_ctrl_add_dev`.
|
| 51/2 |
2026/09/15 03:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ctrl_add_dev]
Results: map[SourceCode:4664: static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
4665: {
4666: void __user *argp = (void __user *)(unsigned long)header->addr;
4667: struct ublksrv_ctrl_dev_info info;
4668: struct ublk_device *ub;
4669: int ret = -EINVAL;
4670:
4671: if (header->len < sizeof(info) || !header->addr)
4672: return -EINVAL;
4673: if (header->queue_id != (u16)-1) {
4674: pr_warn("%s: queue_id is wrong %x\n",
4675: __func__, header->queue_id);
4676: return -EINVAL;
4677: }
4678:
4679: if (copy_from_user(&info, argp, sizeof(info)))
4680: return -EFAULT;
4681:
4682: if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || !info.queue_depth ||
4683: info.nr_hw_queues > UBLK_MAX_NR_QUEUES || !info.nr_hw_queues)
4684: return -EINVAL;
4685:
4686: if (capable(CAP_SYS_ADMIN))
4687: info.flags &= ~UBLK_F_UNPRIVILEGED_DEV;
4688: else if (!(info.flags & UBLK_F_UNPRIVILEGED_DEV))
4689: return -EPERM;
4690:
4691: /* forbid nonsense combinations of recovery flags */
4692: switch (info.flags & UBLK_F_ALL_RECOVERY_FLAGS) {
4693: case 0:
4694: case UBLK_F_USER_RECOVERY:
4695: case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE):
4696: case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_FAIL_IO):
4697: break;
4698: default:
4699: pr_warn("%s: invalid recovery flags %llx\n", __func__,
4700: info.flags & UBLK_F_ALL_RECOVERY_FLAGS);
4701: return -EINVAL;
4702: }
4703:
4704: if ((info.flags & UBLK_F_QUIESCE) && !(info.flags & UBLK_F_USER_RECOVERY)) {
4705: pr_warn("UBLK_F_QUIESCE requires UBLK_F_USER_RECOVERY\n");
4706: return -EINVAL;
4707: }
4708:
4709: /*
4710: * unprivileged device can't be trusted, but RECOVERY and
4711: * RECOVERY_REISSUE still may hang error handling, so can't
4712: * support recovery features for unprivileged ublk now
4713: *
4714: * TODO: provide forward progress for RECOVERY handler, so that
4715: * unprivileged device can benefit from it
4716: */
4717: if (info.flags & UBLK_F_UNPRIVILEGED_DEV) {
4718: info.flags &= ~(UBLK_F_USER_RECOVERY_REISSUE |
4719: UBLK_F_USER_RECOVERY);
4720:
4721: /*
4722: * For USER_COPY, we depends on userspace to fill request
4723: * buffer by pwrite() to ublk char device, which can't be
4724: * used for unprivileged device
4725: *
4726: * Same with zero copy or auto buffer register.
4727: */
4728: if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
4729: UBLK_F_AUTO_BUF_REG))
4730: return -EINVAL;
4731: }
4732:
4733: /* User copy is required to access integrity buffer */
4734: if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
4735: return -EINVAL;
4736:
4737: if (info.flags & UBLK_F_IO_DESC_SIZE) {
4738: if (info.io_desc_size < sizeof(struct ublksrv_io_desc) ||
4739: info.io_desc_size % _Alignof(struct ublksrv_io_desc) ||
4740: info.io_desc_size > UBLK_MAX_IO_DESC_SIZE)
4741: return -EINVAL;
4742: } else {
4743: info.io_desc_size = sizeof(struct ublksrv_io_desc);
4744: }
4745:
4746: /* the created device is always owned by current user */
4747: ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
4748:
4749: if (header->dev_id != info.dev_id) {
4750: pr_warn("%s: dev id not match %u %u\n",
4751: __func__, header->dev_id, info.dev_id);
4752: return -EINVAL;
4753: }
4754:
4755: if (header->dev_id != U32_MAX && header->dev_id >= UBLK_MAX_UBLKS) {
4756: pr_warn("%s: dev id is too large. Max supported is %d\n",
4757: __func__, UBLK_MAX_UBLKS - 1);
4758: return -EINVAL;
4759: }
4760:
4761: ublk_dump_dev_info(&info);
4762:
4763: ret = mutex_lock_killable(&ublk_ctl_mutex);
4764: if (ret)
4765: return ret;
4766:
4767: ret = -EACCES;
4768: if ((info.flags & UBLK_F_UNPRIVILEGED_DEV) &&
4769: unprivileged_ublks_added >= unprivileged_ublks_max)
4770: goto out_unlock;
4771:
4772: ret = -ENOMEM;
4773: ub = kzalloc_flex(*ub, queues, info.nr_hw_queues);
4774: if (!ub)
4775: goto out_unlock;
4776: mutex_init(&ub->mutex);
4777: spin_lock_init(&ub->lock);
4778: mutex_init(&ub->cancel_mutex);
4779: mt_init(&ub->buf_tree);
4780: ida_init(&ub->buf_ida);
4781: INIT_WORK(&ub->partition_scan_work, ublk_partition_scan_work);
4782:
4783: ret = ublk_alloc_dev_number(ub, header->dev_id);
4784: if (ret < 0)
4785: goto out_free_ub;
4786:
4787: memcpy(&ub->dev_info, &info, sizeof(info));
4788:
4789: /* update device id */
4790: ub->dev_info.dev_id = ub->ub_number;
4791:
4792: /*
4793: * ->state and ->ublksrv_pid are owned by the driver and only read back
4794: * by userspace, but they come from the copied-in dev_info, so reset
4795: * them. Otherwise a device added with ->state != DEAD looks live while
4796: * ->ub_disk is still NULL.
4797: */
4798: ub->dev_info.state = UBLK_S_DEV_DEAD;
4799: ub->dev_info.ublksrv_pid = -1;
4800:
4801: /*
4802: * 64bit flags will be copied back to userspace as feature
4803: * negotiation result, so have to clear flags which driver
4804: * doesn't support yet, then userspace can get correct flags
4805: * (features) to handle.
4806: */
4807: ub->dev_info.flags &= UBLK_F_ALL;
4808:
4809: ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE |
4810: UBLK_F_URING_CMD_COMP_IN_TASK |
4811: UBLK_F_PER_IO_DAEMON |
4812: UBLK_F_BUF_REG_OFF_DAEMON |
4813: UBLK_F_SAFE_STOP_DEV;
4814:
4815: /* So far, UBLK_F_PER_IO_DAEMON won't be exposed for BATCH_IO */
4816: if (ublk_dev_support_batch_io(ub))
4817: ub->dev_info.flags &= ~UBLK_F_PER_IO_DAEMON;
4818:
4819: /* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */
4820: if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
4821: UBLK_F_AUTO_BUF_REG))
4822: ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
4823:
4824: /* UBLK_F_BATCH_IO doesn't support GET_DATA */
4825: if (ublk_dev_support_batch_io(ub))
4826: ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
4827:
4828: /*
4829: * Zoned storage support requires reuse `ublksrv_io_cmd->addr` for
4830: * returning write_append_lba, which is only allowed in case of
4831: * user copy or zero copy
4832: */
4833: if (ublk_dev_is_zoned(ub) &&
4834: (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) || !(ub->dev_info.flags &
4835: (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY)))) {
4836: ret = -EINVAL;
4837: goto out_free_dev_number;
4838: }
4839:
4840: ub->dev_info.nr_hw_queues = min_t(unsigned int,
4841: ub->dev_info.nr_hw_queues, nr_cpu_ids);
4842: ublk_align_max_io_size(ub);
4843:
4844: ret = ublk_add_tag_set(ub);
4845: if (ret)
4846: goto out_free_dev_number;
4847:
4848: ret = ublk_init_queues(ub);
4849: if (ret)
4850: goto out_free_tag_set;
4851:
4852: ret = -EFAULT;
4853: if (copy_to_user(argp, &ub->dev_info, sizeof(info)))
4854: goto out_deinit_queues;
4855:
4856: /*
4857: * Add the char dev so that ublksrv daemon can be setup.
4858: * ublk_add_chdev() will cleanup everything if it fails.
4859: */
4860: ret = ublk_add_chdev(ub);
4861: goto out_unlock;
4862:
4863: out_deinit_queues:
4864: ublk_deinit_queues(ub);
4865: out_free_tag_set:
4866: blk_mq_free_tag_set(&ub->tag_set);
4867: out_free_dev_number:
4868: ublk_free_dev_number(ub);
4869: out_free_ub:
4870: mutex_destroy(&ub->mutex);
4871: mutex_destroy(&ub->cancel_mutex);
4872: kfree(ub);
4873: out_unlock:
4874: mutex_unlock(&ublk_ctl_mutex);
4875: return ret;
4876: }
SourceFile:drivers/block/ublk_drv.c]
|
| 52/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 58511
output: 43
thoughts: 377
Thoughts: **My Analysis of ublk Device Creation**
Okay, let me break down this `ublk_ctrl_add_dev` function. The first thing that jumps out is the capability check. It confirms that a user with `CAP_SYS_ADMIN` can create a ublk device without any restrictions. Specifically, the `UBLK_F_UNPRIVILEGED_DEV` flag can be cleared, effectively granting the privileged user full control.
Now, the interesting bit – unprivileged users. The code allows them to create a ublk device *only* if `UBLK_F_UNPRIVILEGED_DEV` is explicitly set. That seems fair enough on the surface, allowing controlled access.
But the plot thickens. If `UBLK_F_UNPRIVILEGED_DEV` *is* set, a bunch of features are immediately disabled via an `EINVAL` error. Specifically, `UBLK_F_USER_COPY`, `UBLK_F_SUPPORT_ZERO_COPY`, and `UBLK_F_AUTO_BUF_REG` are all rejected. It makes sense, given that the explanation notes how `USER_COPY` relies on userspace `pwrite()` calls, which introduces potential security concerns in an unprivileged context. The code seems to be explicitly preventing unprivileged devices from leveraging these more advanced, potentially unsafe, memory management features.
So now I'm thinking about how zero-copy works. I know that `ublk_try_buf_match` is relevant here, and it's only called if `ublk_support_shmem_zc` is true. That function must be the key to enabling (or disabling) zero-copy for ublk. I'll need to dig into `ublk_support_shmem_zc` to understand the full implications.
|
| 53/2 |
2026/09/15 03:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_support_shmem_zc]
Results: map[SourceCode: 425: static inline bool ublk_support_shmem_zc(const struct ublk_queue *ubq)
426: {
427: return ubq->flags & UBLK_F_SHMEM_ZC;
428: }
SourceFile:drivers/block/ublk_drv.c]
|
| 54/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 59416
output: 56
thoughts: 47
Thoughts: **My Initial Concerns and Investigation**
Okay, hold on a second. Something doesn't feel right. I was under the impression that `UBLK_F_SHMEM_ZC` and `UBLK_F_SUPPORT_ZERO_COPY` were essentially synonymous, that they meant the same thing in this context. But now I'm getting a nagging feeling that they might not be. I need to be absolutely sure about this, especially given the implications for zero-copy operations.
The first thing I need to do is verify exactly where `UBLK_F_SHMEM_ZC` is being set within the code. Let me quickly trace its definition and see where it's being used and, critically, *how* it's being used. I need to understand the relationship between these two flags to confidently proceed. This could be a critical misunderstanding, and I need to nip it in the bud now.
|
| 55/2 |
2026/09/15 03:19 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:UBLK_F_SHMEM_ZC SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 56/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 59731
output: 23
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Macro Detection and Initial Assessment**
Okay, so I've encountered something that's behaving suspiciously – it's structured in a way that suggests a macro is at play. My immediate instinct is to confirm this suspicion and get a clearer picture of its nature. Given the context, this isn't just a hunch; it warrants a targeted investigation.
Therefore, my first action is going to be a quick `grep`. I'll use `grep` (or the equivalent, given my environment) to rapidly scan the source for the macro in question. This will either confirm its presence and give me some context, or fail, thus requiring a different strategy. Either way, this is the most efficient first step to take in this specific instance.
|
| 57/2 |
2026/09/15 03:19 |
tool |
grepper |
0m
Args: map[Expression:UBLK_F_SHMEM_ZC]
Results: map[Output:Documentation/block/ublk.rst=479=Limitations
--
Documentation/block/ublk.rst-487-
Documentation/block/ublk.rst:488:Shared Memory Zero Copy (UBLK_F_SHMEM_ZC)
Documentation/block/ublk.rst-489-------------------------------------------
Documentation/block/ublk.rst-490-
Documentation/block/ublk.rst:491:The ``UBLK_F_SHMEM_ZC`` feature provides an alternative zero-copy path
Documentation/block/ublk.rst-492-that works by sharing physical memory pages between the client application
--
Documentation/block/ublk.rst=506=tells the server where the data already lives.
Documentation/block/ublk.rst-507-
Documentation/block/ublk.rst:508:``UBLK_F_SHMEM_ZC`` can be thought of as a supplement for optimized client
Documentation/block/ublk.rst-509-applications — when the client is willing to allocate I/O buffers from
--
drivers/block/ublk_drv.c-91- | UBLK_F_NO_AUTO_PART_SCAN \
drivers/block/ublk_drv.c:92: | UBLK_F_SHMEM_ZC \
drivers/block/ublk_drv.c-93- | UBLK_F_IO_DESC_SIZE)
--
drivers/block/ublk_drv.c=425=static inline bool ublk_support_shmem_zc(const struct ublk_queue *ubq)
drivers/block/ublk_drv.c-426-{
drivers/block/ublk_drv.c:427: return ubq->flags & UBLK_F_SHMEM_ZC;
drivers/block/ublk_drv.c-428-}
--
drivers/block/ublk_drv.c=435=static inline bool ublk_dev_support_shmem_zc(const struct ublk_device *ub)
drivers/block/ublk_drv.c-436-{
drivers/block/ublk_drv.c:437: return ub->dev_info.flags & UBLK_F_SHMEM_ZC;
drivers/block/ublk_drv.c-438-}
--
include/uapi/linux/ublk_cmd.h=90=struct ublk_shmem_buf_reg {
--
include/uapi/linux/ublk_cmd.h-417- */
include/uapi/linux/ublk_cmd.h:418:#define UBLK_F_SHMEM_ZC (1ULL << 19)
include/uapi/linux/ublk_cmd.h-419-
--
tools/testing/selftests/ublk/kublk.c=2060=static int cmd_dev_get_features(void)
--
tools/testing/selftests/ublk/kublk.c-2083- FEAT_NAME(UBLK_F_NO_AUTO_PART_SCAN),
tools/testing/selftests/ublk/kublk.c:2084: FEAT_NAME(UBLK_F_SHMEM_ZC),
tools/testing/selftests/ublk/kublk.c-2085- FEAT_NAME(UBLK_F_IO_DESC_SIZE),
--
tools/testing/selftests/ublk/kublk.c=2230=int main(int argc, char *argv[])
--
tools/testing/selftests/ublk/kublk.c-2410- if (!strcmp(longopts[option_idx].name, "shmem_zc"))
tools/testing/selftests/ublk/kublk.c:2411: ctx.flags |= UBLK_F_SHMEM_ZC;
tools/testing/selftests/ublk/kublk.c-2412- if (!strcmp(longopts[option_idx].name, "htlb"))
]
|
| 58/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 60928
output: 42
thoughts: 36
Thoughts: Okay, let's dive into this. Here's how I'd approach that line of thinking, assuming I'm an expert in this area:
**Checking `ublk_ctrl_add_dev` and `UBLK_F_SHMEM_ZC` Permission**
Alright, I need to revisit `ublk_ctrl_add_dev`. I seem to recall some potential restrictions around the flag `UBLK_F_SHMEM_ZC`. My gut feeling is that we might be inadvertently rejecting this flag for users who aren't sufficiently privileged. I need to confirm whether that's actually the case. If so, I need to identify the code path where this rejection occurs. It’s important to see *why* it's being rejected - is it intentional security hardening, or is it a bug that's preventing legitimate use? I suspect that, if the check exists, it's there to protect against potential security issues, but I have to be thorough and verify the logic. I need to pinpoint the exact location in the code where this check takes place so that I can analyze it carefully. If it's a bug, it will be a high-priority fix. If it's intentional, I'll need to examine the reasoning, security implications, and determine if it's still appropriate given the current security landscape and the intended use of this feature. In short, time to roll up my sleeves and dive back into that function!
|
| 59/2 |
2026/09/15 03:19 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ctrl_add_dev]
Results: map[SourceCode:4664: static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
4665: {
4666: void __user *argp = (void __user *)(unsigned long)header->addr;
4667: struct ublksrv_ctrl_dev_info info;
4668: struct ublk_device *ub;
4669: int ret = -EINVAL;
4670:
4671: if (header->len < sizeof(info) || !header->addr)
4672: return -EINVAL;
4673: if (header->queue_id != (u16)-1) {
4674: pr_warn("%s: queue_id is wrong %x\n",
4675: __func__, header->queue_id);
4676: return -EINVAL;
4677: }
4678:
4679: if (copy_from_user(&info, argp, sizeof(info)))
4680: return -EFAULT;
4681:
4682: if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || !info.queue_depth ||
4683: info.nr_hw_queues > UBLK_MAX_NR_QUEUES || !info.nr_hw_queues)
4684: return -EINVAL;
4685:
4686: if (capable(CAP_SYS_ADMIN))
4687: info.flags &= ~UBLK_F_UNPRIVILEGED_DEV;
4688: else if (!(info.flags & UBLK_F_UNPRIVILEGED_DEV))
4689: return -EPERM;
4690:
4691: /* forbid nonsense combinations of recovery flags */
4692: switch (info.flags & UBLK_F_ALL_RECOVERY_FLAGS) {
4693: case 0:
4694: case UBLK_F_USER_RECOVERY:
4695: case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE):
4696: case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_FAIL_IO):
4697: break;
4698: default:
4699: pr_warn("%s: invalid recovery flags %llx\n", __func__,
4700: info.flags & UBLK_F_ALL_RECOVERY_FLAGS);
4701: return -EINVAL;
4702: }
4703:
4704: if ((info.flags & UBLK_F_QUIESCE) && !(info.flags & UBLK_F_USER_RECOVERY)) {
4705: pr_warn("UBLK_F_QUIESCE requires UBLK_F_USER_RECOVERY\n");
4706: return -EINVAL;
4707: }
4708:
4709: /*
4710: * unprivileged device can't be trusted, but RECOVERY and
4711: * RECOVERY_REISSUE still may hang error handling, so can't
4712: * support recovery features for unprivileged ublk now
4713: *
4714: * TODO: provide forward progress for RECOVERY handler, so that
4715: * unprivileged device can benefit from it
4716: */
4717: if (info.flags & UBLK_F_UNPRIVILEGED_DEV) {
4718: info.flags &= ~(UBLK_F_USER_RECOVERY_REISSUE |
4719: UBLK_F_USER_RECOVERY);
4720:
4721: /*
4722: * For USER_COPY, we depends on userspace to fill request
4723: * buffer by pwrite() to ublk char device, which can't be
4724: * used for unprivileged device
4725: *
4726: * Same with zero copy or auto buffer register.
4727: */
4728: if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
4729: UBLK_F_AUTO_BUF_REG))
4730: return -EINVAL;
4731: }
4732:
4733: /* User copy is required to access integrity buffer */
4734: if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
4735: return -EINVAL;
4736:
4737: if (info.flags & UBLK_F_IO_DESC_SIZE) {
4738: if (info.io_desc_size < sizeof(struct ublksrv_io_desc) ||
4739: info.io_desc_size % _Alignof(struct ublksrv_io_desc) ||
4740: info.io_desc_size > UBLK_MAX_IO_DESC_SIZE)
4741: return -EINVAL;
4742: } else {
4743: info.io_desc_size = sizeof(struct ublksrv_io_desc);
4744: }
4745:
4746: /* the created device is always owned by current user */
4747: ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
4748:
4749: if (header->dev_id != info.dev_id) {
4750: pr_warn("%s: dev id not match %u %u\n",
4751: __func__, header->dev_id, info.dev_id);
4752: return -EINVAL;
4753: }
4754:
4755: if (header->dev_id != U32_MAX && header->dev_id >= UBLK_MAX_UBLKS) {
4756: pr_warn("%s: dev id is too large. Max supported is %d\n",
4757: __func__, UBLK_MAX_UBLKS - 1);
4758: return -EINVAL;
4759: }
4760:
4761: ublk_dump_dev_info(&info);
4762:
4763: ret = mutex_lock_killable(&ublk_ctl_mutex);
4764: if (ret)
4765: return ret;
4766:
4767: ret = -EACCES;
4768: if ((info.flags & UBLK_F_UNPRIVILEGED_DEV) &&
4769: unprivileged_ublks_added >= unprivileged_ublks_max)
4770: goto out_unlock;
4771:
4772: ret = -ENOMEM;
4773: ub = kzalloc_flex(*ub, queues, info.nr_hw_queues);
4774: if (!ub)
4775: goto out_unlock;
4776: mutex_init(&ub->mutex);
4777: spin_lock_init(&ub->lock);
4778: mutex_init(&ub->cancel_mutex);
4779: mt_init(&ub->buf_tree);
4780: ida_init(&ub->buf_ida);
4781: INIT_WORK(&ub->partition_scan_work, ublk_partition_scan_work);
4782:
4783: ret = ublk_alloc_dev_number(ub, header->dev_id);
4784: if (ret < 0)
4785: goto out_free_ub;
4786:
4787: memcpy(&ub->dev_info, &info, sizeof(info));
4788:
4789: /* update device id */
4790: ub->dev_info.dev_id = ub->ub_number;
4791:
4792: /*
4793: * ->state and ->ublksrv_pid are owned by the driver and only read back
4794: * by userspace, but they come from the copied-in dev_info, so reset
4795: * them. Otherwise a device added with ->state != DEAD looks live while
4796: * ->ub_disk is still NULL.
4797: */
4798: ub->dev_info.state = UBLK_S_DEV_DEAD;
4799: ub->dev_info.ublksrv_pid = -1;
4800:
4801: /*
4802: * 64bit flags will be copied back to userspace as feature
4803: * negotiation result, so have to clear flags which driver
4804: * doesn't support yet, then userspace can get correct flags
4805: * (features) to handle.
4806: */
4807: ub->dev_info.flags &= UBLK_F_ALL;
4808:
4809: ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE |
4810: UBLK_F_URING_CMD_COMP_IN_TASK |
4811: UBLK_F_PER_IO_DAEMON |
4812: UBLK_F_BUF_REG_OFF_DAEMON |
4813: UBLK_F_SAFE_STOP_DEV;
4814:
4815: /* So far, UBLK_F_PER_IO_DAEMON won't be exposed for BATCH_IO */
4816: if (ublk_dev_support_batch_io(ub))
4817: ub->dev_info.flags &= ~UBLK_F_PER_IO_DAEMON;
4818:
4819: /* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */
4820: if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
4821: UBLK_F_AUTO_BUF_REG))
4822: ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
4823:
4824: /* UBLK_F_BATCH_IO doesn't support GET_DATA */
4825: if (ublk_dev_support_batch_io(ub))
4826: ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
4827:
4828: /*
4829: * Zoned storage support requires reuse `ublksrv_io_cmd->addr` for
4830: * returning write_append_lba, which is only allowed in case of
4831: * user copy or zero copy
4832: */
4833: if (ublk_dev_is_zoned(ub) &&
4834: (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) || !(ub->dev_info.flags &
4835: (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY)))) {
4836: ret = -EINVAL;
4837: goto out_free_dev_number;
4838: }
4839:
4840: ub->dev_info.nr_hw_queues = min_t(unsigned int,
4841: ub->dev_info.nr_hw_queues, nr_cpu_ids);
4842: ublk_align_max_io_size(ub);
4843:
4844: ret = ublk_add_tag_set(ub);
4845: if (ret)
4846: goto out_free_dev_number;
4847:
4848: ret = ublk_init_queues(ub);
4849: if (ret)
4850: goto out_free_tag_set;
4851:
4852: ret = -EFAULT;
4853: if (copy_to_user(argp, &ub->dev_info, sizeof(info)))
4854: goto out_deinit_queues;
4855:
4856: /*
4857: * Add the char dev so that ublksrv daemon can be setup.
4858: * ublk_add_chdev() will cleanup everything if it fails.
4859: */
4860: ret = ublk_add_chdev(ub);
4861: goto out_unlock;
4862:
4863: out_deinit_queues:
4864: ublk_deinit_queues(ub);
4865: out_free_tag_set:
4866: blk_mq_free_tag_set(&ub->tag_set);
4867: out_free_dev_number:
4868: ublk_free_dev_number(ub);
4869: out_free_ub:
4870: mutex_destroy(&ub->mutex);
4871: mutex_destroy(&ub->cancel_mutex);
4872: kfree(ub);
4873: out_unlock:
4874: mutex_unlock(&ublk_ctl_mutex);
4875: return ret;
4876: }
SourceFile:drivers/block/ublk_drv.c]
|
| 60/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 64943
output: 50
thoughts: 912
Thoughts: **Analyzing the ublk_try_buf_match Lockdep Warning**
Okay, so I'm looking at this lockdep warning triggered by `ublk_try_buf_match`, and I need to figure out if it's a real issue. Initially, I thought that `UBLK_F_SHMEM_ZC` was rejected for unprivileged users, but it's not. The rejection only applies to `UBLK_F_USER_COPY`, `UBLK_F_SUPPORT_ZERO_COPY`, and `UBLK_F_AUTO_BUF_REG`.
Then I see the syzbot reproducer uses `UBLK_F_SHMEM_ZC` (0x80000 = `1ULL << 19`) in its flags. But the reproducer *is* running as root ("CPU: 1 UID: 0 PID: 5631 Comm: syz-executor208"). So, it has `CAP_SYS_ADMIN`, `UBLK_F_UNPRIVILEGED_DEV` is cleared, and `UBLK_F_USER_COPY` is allowed. That's why the repro is successful. An unprivileged user could set `UBLK_F_SHMEM_ZC`, since it's *not* in the rejected list, and that *should* trigger it.
However, `ublk_try_buf_match` is only called when `ublk_support_shmem_zc(ubq)` is true. The lockdep warning *could* be triggered by an unprivileged user setting both `UBLK_F_SHMEM_ZC` and `UBLK_F_UNPRIVILEGED_DEV`.
But is this exploitable? After my earlier analysis, I'm pretty sure there's NO actual race condition here, because `blk_mq_freeze_queue` prevents any concurrent modifications to the maple tree while requests are in-flight. `ublk_try_buf_match` only runs for those in-flight requests, so it's fully serialized with any tree modifications. That means it's a *false positive* lockdep warning.
And a false positive is not exploitable and doesn't cause a denial of service (unless `panic_on_warn` is set, which is unlikely in any production environment).
I need to confirm there aren't any other ways to modify the tree concurrently. What if `ublk_ctrl_reg_buf` fails midway through execution? No - it freezes the queue, modifies the tree (if it reaches that point), and unfreezes the queue. What about `ublk_ctrl_unreg_buf`? Same story: freeze, modify, unfreeze. And `ublk_buf_cleanup`? It's called from `ublk_cdev_rel`, which is called when the char device is closed. But `ublk_stop_dev_unlocked` is called first, which calls `del_gendisk` which *also* freezes the queue and waits for in-flight requests to complete. So it's still serialized.
Is there *any* other function that modifies the `ub->buf_tree`? I will need to check all references to the `buf_tree` to be sure.
|
| 61/2 |
2026/09/15 03:19 |
tool |
codesearch-find-references |
0m
Error:
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:buf_tree SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 62/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 66626
output: 55
thoughts: 8
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience:
**Initial Assessment: Navigating the Landscape**
Wait. Okay, hold on a second. This isn't a simple list, a single concept, or a straightforward problem. It's... a field. As an expert, I'm already mentally charting the territory. It's the whole encompassing space where these components interact. This isn't a problem to solve; it's a domain to understand. I need to figure out the boundaries, key players, dominant paradigms, and potential methodologies applicable here.
|
| 63/2 |
2026/09/15 03:19 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_device::buf_tree SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:__ublk_ctrl_reg_buf SourceFile:drivers/block/ublk_drv.c SourceLine:5409] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:__ublk_shmem_remove_ranges SourceFile:drivers/block/ublk_drv.c SourceLine:5544] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ublk_buf_cleanup SourceFile:drivers/block/ublk_drv.c SourceLine:5616] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ublk_buf_erase_ranges SourceFile:drivers/block/ublk_drv.c SourceLine:5370] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ublk_ctrl_add_dev SourceFile:drivers/block/ublk_drv.c SourceLine:4779] map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ublk_try_buf_match SourceFile:drivers/block/ublk_drv.c SourceLine:5637]] TruncatedOutput:false]
|
| 64/2 |
2026/09/15 03:19 |
llm |
expert |
0m
Model:
Tokens:
input: 67196
output: 40
thoughts: 681
Thoughts: **Analyzing a Potential Concurrency Issue in the Ublk Driver**
Okay, let's break this down. The lockdep warning regarding concurrent modification is bugging me, so I need to go through this methodically. I'm going to look at the interactions between these functions: `__ublk_ctrl_reg_buf`, `__ublk_shmem_remove_ranges`, `ublk_buf_cleanup`, and `ublk_buf_erase_ranges`.
Right, `__ublk_ctrl_reg_buf` is called via `ublk_ctrl_reg_buf`, which holds `ublk_lock_buf_tree`. Similarly, `__ublk_shmem_remove_ranges` comes from `ublk_shmem_remove_ranges`, which in turn is called from either `ublk_ctrl_unreg_buf` (also holding `ublk_lock_buf_tree`) or `ublk_buf_cleanup` (which happens *after* the queue is frozen). And `ublk_buf_erase_ranges` is called from `__ublk_ctrl_reg_buf`, protected by `ublk_lock_buf_tree`. Also, `ublk_ctrl_add_dev` initializes the buffer tree.
So, all the modifications seem to be protected by either `ublk_lock_buf_tree` or occur after the queue is frozen and being destroyed. And, `ublk_try_buf_match` is the function that reads the buffer tree. Because `ublk_try_buf_match` is only used for in-flight requests, and `ublk_lock_buf_tree` freezes the queue until all in-flight requests are completed, my initial assessment is that there should be no concurrent modification possible. It really looks like the lockdep warning is a false positive.
But, I need to be *absolutely* sure. The crucial piece is that lock function. Does `ublk_lock_buf_tree` *always* freeze the queue? I see it contains this code:
```c
static unsigned int ublk_lock_buf_tree(struct ublk_device *ub)
{
unsigned int memflags = 0;
mutex_lock(&ub->mutex);
if (ub->ub_disk)
memflags = blk_mq_freeze_queue(ub->ub_disk->queue);
return memflags;
}
```
Okay, if `ub->ub_disk` is NULL, it *doesn't* freeze the queue! So, when is `ub->ub_disk` NULL? It's NULL *before* `ublk_ctrl_start_dev` is called. So, can `ublk_try_buf_match` run before `ublk_ctrl_start_dev`? No, that can't happen. `ublk_try_buf_match` is part of processing block requests, and those requests can only be sent to the block device. That block device is created and added in `ublk_ctrl_start_dev`. So, before `ublk_ctrl_start_dev`, no block device exists, so no block requests can be submitted, and hence `ublk_try_buf_match` can't run.
Alright, what about *after* `ublk_stop_dev_unlocked`? Because that calls `del_gendisk(ub->ub_disk)`. Now *that* freezes the queue and waits for in-flight requests. And then the disk is detached. But does it set `ub->ub_disk = NULL`? I should look into `ublk_detach_disk`.
|
| 65/2 |
2026/09/15 03:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_detach_disk]
Results: map[SourceCode:2979: static struct gendisk *ublk_detach_disk(struct ublk_device *ub)
2980: {
2981: struct gendisk *disk;
2982:
2983: /* Sync with ublk_abort_queue() by holding the lock */
2984: spin_lock(&ub->lock);
2985: disk = ub->ub_disk;
2986: ub->dev_info.state = UBLK_S_DEV_DEAD;
2987: ub->dev_info.ublksrv_pid = -1;
2988: ub->ub_disk = NULL;
2989: spin_unlock(&ub->lock);
2990:
2991: return disk;
2992: }
SourceFile:drivers/block/ublk_drv.c]
|
| 66/2 |
2026/09/15 03:20 |
llm |
expert |
0m
Model:
Tokens:
input: 68952
output: 42
thoughts: 297
Thoughts: **Analyzing the `ub->ub_disk` and `ublk_try_buf_match` Synchronization**
Okay, so I see this sets `ub->ub_disk = NULL`. But, the key is that `del_gendisk` has *already* been called. This is critical because `del_gendisk` effectively freezes the queue and patiently waits for all requests currently in flight to complete. That means any instance of `ublk_try_buf_match` that's running due to an in-flight request will *definitely* finish its work before `del_gendisk` actually returns.
Therefore, after `del_gendisk` hands back control, no new requests can be submitted to the device, right? That means `ublk_try_buf_match` cannot *possibly* run at that point. So, the only times that `ublk_try_buf_match` can execute are when `ub->ub_disk` is *not* NULL, and when the queue hasn't been frozen. Any alterations to the data structure will either see that `ub->ub_disk` isn't NULL and the queue will be frozen – waiting for `ublk_try_buf_match` to finish – or, if `ub->ub_disk` is NULL, then `ublk_try_buf_match` can't possibly be running. Hence, I'm confident that there's no race condition here.
Wait a second, hold on... Is there *any* possibility that `ublk_try_buf_match` gets called from a completely different context? What about when the ublk server sends a `UBLK_U_IO_FETCH_REQ` command and `ublk_dispatch_req` is directly called? I need to check `ublk_ctrl_uring_cmd` to be sure I'm not overlooking something.
|
| 67/2 |
2026/09/15 03:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ctrl_uring_cmd]
Results: map[SourceCode:5767: static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
5768: unsigned int issue_flags)
5769: {
5770: /* May point to userspace-mapped memory */
5771: const struct ublksrv_ctrl_cmd *ub_src = io_uring_sqe128_cmd(cmd->sqe,
5772: struct ublksrv_ctrl_cmd);
5773: struct ublksrv_ctrl_cmd header;
5774: struct ublk_device *ub = NULL;
5775: u32 cmd_op = cmd->cmd_op;
5776: int ret = -EINVAL;
5777:
5778: if (ublk_ctrl_uring_cmd_may_sleep(cmd_op) &&
5779: issue_flags & IO_URING_F_NONBLOCK)
5780: return -EAGAIN;
5781:
5782: if (!(issue_flags & IO_URING_F_SQE128))
5783: return -EINVAL;
5784:
5785: header.dev_id = READ_ONCE(ub_src->dev_id);
5786: header.queue_id = READ_ONCE(ub_src->queue_id);
5787: header.len = READ_ONCE(ub_src->len);
5788: header.addr = READ_ONCE(ub_src->addr);
5789: header.data[0] = READ_ONCE(ub_src->data[0]);
5790: header.dev_path_len = READ_ONCE(ub_src->dev_path_len);
5791: ublk_ctrl_cmd_dump(cmd_op, &header);
5792:
5793: ret = ublk_check_cmd_op(cmd_op);
5794: if (ret)
5795: goto out;
5796:
5797: if (cmd_op == UBLK_U_CMD_GET_FEATURES) {
5798: ret = ublk_ctrl_get_features(&header);
5799: goto out;
5800: }
5801:
5802: if (_IOC_NR(cmd_op) != UBLK_CMD_ADD_DEV) {
5803: ret = -ENODEV;
5804: ub = ublk_get_device_from_id(header.dev_id);
5805: if (!ub)
5806: goto out;
5807:
5808: ret = ublk_ctrl_uring_cmd_permission(ub, cmd_op, &header);
5809: if (ret)
5810: goto put_dev;
5811: }
5812:
5813: switch (_IOC_NR(cmd_op)) {
5814: case UBLK_CMD_START_DEV:
5815: ret = ublk_ctrl_start_dev(ub, &header);
5816: break;
5817: case UBLK_CMD_STOP_DEV:
5818: ublk_ctrl_stop_dev(ub);
5819: ret = 0;
5820: break;
5821: case UBLK_CMD_GET_DEV_INFO:
5822: case UBLK_CMD_GET_DEV_INFO2:
5823: ret = ublk_ctrl_get_dev_info(ub, &header);
5824: break;
5825: case UBLK_CMD_ADD_DEV:
5826: ret = ublk_ctrl_add_dev(&header);
5827: break;
5828: case UBLK_CMD_DEL_DEV:
5829: ret = ublk_ctrl_del_dev(&ub, true);
5830: break;
5831: case UBLK_CMD_DEL_DEV_ASYNC:
5832: ret = ublk_ctrl_del_dev(&ub, false);
5833: break;
5834: case UBLK_CMD_GET_QUEUE_AFFINITY:
5835: ret = ublk_ctrl_get_queue_affinity(ub, &header);
5836: break;
5837: case UBLK_CMD_GET_PARAMS:
5838: ret = ublk_ctrl_get_params(ub, &header);
5839: break;
5840: case UBLK_CMD_SET_PARAMS:
5841: ret = ublk_ctrl_set_params(ub, &header);
5842: break;
5843: case UBLK_CMD_START_USER_RECOVERY:
5844: ret = ublk_ctrl_start_recovery(ub);
5845: break;
5846: case UBLK_CMD_END_USER_RECOVERY:
5847: ret = ublk_ctrl_end_recovery(ub, &header);
5848: break;
5849: case UBLK_CMD_UPDATE_SIZE:
5850: ret = ublk_ctrl_set_size(ub, &header);
5851: break;
5852: case UBLK_CMD_QUIESCE_DEV:
5853: ret = ublk_ctrl_quiesce_dev(ub, &header);
5854: break;
5855: case UBLK_CMD_TRY_STOP_DEV:
5856: ret = ublk_ctrl_try_stop_dev(ub);
5857: break;
5858: case UBLK_CMD_REG_BUF:
5859: ret = ublk_ctrl_reg_buf(ub, &header);
5860: break;
5861: case UBLK_CMD_UNREG_BUF:
5862: ret = ublk_ctrl_unreg_buf(ub, &header);
5863: break;
5864: default:
5865: ret = -EOPNOTSUPP;
5866: break;
5867: }
5868:
5869: put_dev:
5870: if (ub)
5871: ublk_put_device(ub);
5872: out:
5873: pr_devel("%s: cmd done ret %d cmd_op %x, dev id %d qid %d\n",
5874: __func__, ret, cmd_op, header.dev_id, header.queue_id);
5875: return ret;
5876: }
SourceFile:drivers/block/ublk_drv.c]
|
| 68/2 |
2026/09/15 03:20 |
llm |
expert |
0m
Model:
Tokens:
input: 71525
output: 42
thoughts: 36
Thoughts: Here's my take on the thought process, as if it were my own:
**Control Command Handling in ublk_ctrl_uring_cmd and I/O Command Handling in ublk_ch_uring_cmd**
Okay, so I'm looking at the `ublk` code here. Clearly, `ublk_ctrl_uring_cmd` is responsible for processing the control commands. That makes sense; it's right there in the name. But that raises an immediate question: what about the I/O commands? Where are *those* being handled? Ah, okay, I see it now. The I/O commands are going through `ublk_ch_uring_cmd`. Right. So, control commands here, I/O commands over there, both leveraging the io_uring interface. Got it. Now I can see how the architecture is designed. Let's see how I can use this to enhance performance.
|
| 69/2 |
2026/09/15 03:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ch_uring_cmd]
Results: map[SourceCode:3540: static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
3541: {
3542: if (unlikely(issue_flags & IO_URING_F_CANCEL)) {
3543: ublk_uring_cmd_cancel_fn(cmd, issue_flags);
3544: return 0;
3545: }
3546:
3547: /* well-implemented server won't run into unlocked */
3548: if (unlikely(issue_flags & IO_URING_F_UNLOCKED)) {
3549: io_uring_cmd_complete_in_task(cmd, ublk_ch_uring_cmd_cb);
3550: return -EIOCBQUEUED;
3551: }
3552:
3553: return ublk_ch_uring_cmd_local(cmd, issue_flags);
3554: }
SourceFile:drivers/block/ublk_drv.c]
|
| 70/2 |
2026/09/15 03:20 |
llm |
expert |
0m
Model:
Tokens:
input: 72090
output: 43
thoughts: 25
Thoughts: Okay, here's my attempt at summarizing the thought process, as if it were my own, assuming I'm an expert in this field:
**Digging into the ublk Command Flow**
Alright, let's see... `ublk_ch_uring_cmd_local`. I know that function. Right, it's the local command processing part of the ublk character device path. So, `ublk_ch_uring_cmd_local` is the entry point for commands that are handled within the same process. And if I recall correctly from my review of the code, that's where the magic starts to happen before submitting anything to the shared SQ.
The key thing here is that `ublk_ch_uring_cmd_local` seems to be responsible for prepping the local processing. Now, where is it that the real heavy lifting happens? Ah, of course... it's calling `__ublk_ch_uring_cmd`. It makes perfect sense. This means that the real work of dealing with the command, whatever it may be – the actual submission and handling – is handled within the scope of that internal function. We can infer that `__ublk_ch_uring_cmd` then takes care of all the necessary steps: memory mapping, I/O handling, and potentially interaction with the underlying block device through the rings. Right. So, all that local setup in the former function makes sure that the function being called, the latter, has all it needs to be the worker function. This simplifies the former and modularizes the latter. And the local function has some extra parameters that must be relevant. I think I'll go check that out later.
|
| 71/2 |
2026/09/15 03:20 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/block/ublk_drv.c Name:__ublk_ch_uring_cmd]
Results: map[SourceCode: SourceFile:]
|
| 72/2 |
2026/09/15 03:20 |
llm |
expert |
0m
Model:
Tokens:
input: 72531
output: 44
thoughts: 16
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Diving into `ublk_ch_uring_cmd_local`**
Alright, let's take a look at `ublk_ch_uring_cmd_local`. This is a critical function, I need to understand its role in the command processing pipeline. I'm focusing on how it handles local command execution within the ublk context. There are subtleties here in how we handle the submissions. I need to refresh my mental map of how the data structures are laid out; specifically, where this fits within the submission ring and the overall command lifecycle. Is it being handled synchronously, or is there an asynchronous component? I need to understand the implications of the synchronization mechanisms employed. Let's see how efficiently it handles error conditions and how it impacts overall performance. This is all about the low-level mechanics. Gotta make sure I'm solid on these details before I move on.
|
| 73/2 |
2026/09/15 03:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ch_uring_cmd_local]
Results: map[SourceCode:3365: static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
3366: unsigned int issue_flags)
3367: {
3368: /* May point to userspace-mapped memory */
3369: const struct ublksrv_io_cmd *ub_src = io_uring_sqe_cmd(cmd->sqe,
3370: struct ublksrv_io_cmd);
3371: u16 buf_idx = UBLK_INVALID_BUF_IDX;
3372: struct ublk_device *ub = cmd->file->private_data;
3373: struct ublk_queue *ubq;
3374: struct ublk_io *io = NULL;
3375: u32 cmd_op = cmd->cmd_op;
3376: u16 q_id = READ_ONCE(ub_src->q_id);
3377: u16 tag = READ_ONCE(ub_src->tag);
3378: s32 result = READ_ONCE(ub_src->result);
3379: u64 addr = READ_ONCE(ub_src->addr); /* unioned with zone_append_lba */
3380: struct request *req;
3381: int ret;
3382: bool compl;
3383:
3384: WARN_ON_ONCE(issue_flags & IO_URING_F_UNLOCKED);
3385:
3386: pr_devel("%s: received: cmd op %d queue %d tag %d result %d\n",
3387: __func__, cmd->cmd_op, q_id, tag, result);
3388:
3389: ret = ublk_check_cmd_op(cmd_op);
3390: if (ret)
3391: goto out;
3392:
3393: /*
3394: * io_buffer_unregister() doesn't access the ubq or io,
3395: * so no need to validate the q_id, tag, or task
3396: */
3397: if (_IOC_NR(cmd_op) == UBLK_IO_UNREGISTER_IO_BUF)
3398: return ublk_unregister_io_buf(cmd, ub, addr, issue_flags);
3399:
3400: ret = -EINVAL;
3401: if (q_id >= ub->dev_info.nr_hw_queues)
3402: goto out;
3403:
3404: ubq = ublk_get_queue(ub, q_id);
3405:
3406: if (tag >= ub->dev_info.queue_depth)
3407: goto out;
3408:
3409: io = &ubq->ios[tag];
3410: /* UBLK_IO_FETCH_REQ can be handled on any task, which sets io->task */
3411: if (unlikely(_IOC_NR(cmd_op) == UBLK_IO_FETCH_REQ)) {
3412: ret = ublk_check_fetch_buf(ub, addr);
3413: if (ret)
3414: goto out;
3415: ret = ublk_fetch(cmd, ub, io, addr, q_id);
3416: if (ret)
3417: goto out;
3418:
3419: ublk_prep_cancel(cmd, issue_flags, ubq, tag);
3420: return -EIOCBQUEUED;
3421: }
3422:
3423: if (READ_ONCE(io->task) != current) {
3424: /*
3425: * ublk_register_io_buf() accesses only the io's refcount,
3426: * so can be handled on any task
3427: */
3428: if (_IOC_NR(cmd_op) == UBLK_IO_REGISTER_IO_BUF)
3429: return ublk_register_io_buf(cmd, ub, q_id, tag, io,
3430: addr, issue_flags);
3431:
3432: goto out;
3433: }
3434:
3435: /* there is pending io cmd, something must be wrong */
3436: if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)) {
3437: ret = -EBUSY;
3438: goto out;
3439: }
3440:
3441: /*
3442: * ensure that the user issues UBLK_IO_NEED_GET_DATA
3443: * iff the driver have set the UBLK_IO_FLAG_NEED_GET_DATA.
3444: */
3445: if ((!!(io->flags & UBLK_IO_FLAG_NEED_GET_DATA))
3446: ^ (_IOC_NR(cmd_op) == UBLK_IO_NEED_GET_DATA))
3447: goto out;
3448:
3449: switch (_IOC_NR(cmd_op)) {
3450: case UBLK_IO_REGISTER_IO_BUF:
3451: return ublk_daemon_register_io_buf(cmd, ub, q_id, tag, io, addr,
3452: issue_flags);
3453: case UBLK_IO_COMMIT_AND_FETCH_REQ: {
3454: struct ublk_auto_buf_reg auto_buf;
3455:
3456: ret = ublk_check_commit_and_fetch(ub, io, addr);
3457: if (ret)
3458: goto out;
3459: ret = ublk_validate_io_buf(ub, cmd, &auto_buf);
3460: if (ret)
3461: goto out;
3462: io->res = result;
3463: req = ublk_fill_io_cmd(io, cmd);
3464: ublk_apply_io_buf(ub, io, cmd, addr, &auto_buf, &buf_idx);
3465: if (buf_idx != UBLK_INVALID_BUF_IDX)
3466: io_buffer_unregister(cmd, buf_idx, issue_flags);
3467: compl = ublk_need_complete_req(ub, io);
3468:
3469: if (req_op(req) == REQ_OP_ZONE_APPEND)
3470: req->__sector = addr;
3471: if (compl)
3472: __ublk_complete_rq(req, io, ublk_dev_need_map_io(ub), NULL);
3473: break;
3474: }
3475: case UBLK_IO_NEED_GET_DATA:
3476: /*
3477: * ublk_get_data() may fail and fallback to requeue, so keep
3478: * uring_cmd active first and prepare for handling new requeued
3479: * request
3480: */
3481: req = ublk_fill_io_cmd(io, cmd);
3482: io->buf.addr = addr;
3483: if (likely(ublk_get_data(ubq, io, req))) {
3484: __ublk_prep_compl_io_cmd(io, req);
3485: return UBLK_IO_RES_OK;
3486: }
3487: break;
3488: default:
3489: goto out;
3490: }
3491: ublk_prep_cancel(cmd, issue_flags, ubq, tag);
3492: return -EIOCBQUEUED;
3493:
3494: out:
3495: pr_devel("%s: complete: cmd op %d, tag %d ret %x io_flags %x\n",
3496: __func__, cmd_op, tag, ret, io ? io->flags : 0);
3497: return ret;
3498: }
SourceFile:drivers/block/ublk_drv.c]
|
| 74/2 |
2026/09/15 03:20 |
llm |
expert |
0m
Model:
Tokens:
input: 75079
output: 42
thoughts: 1531
Thoughts: **Analysis of the Ublk Lockdep Warning**
Okay, let's break this down. We're looking at a lockdep warning related to `mas_walk` being called from `ublk_try_buf_match`. The key here is understanding how `ublk_try_buf_match` fits into the overall ublk request flow. My understanding is that it's called within the context of an *in-flight* block request, which is crucial.
1. **The Trigger:** We started with a lockdep warning. That raises eyebrows because it indicates a potential synchronization issue. The warning points to `mas_walk` within `ublk_try_buf_match`.
2. **`ublk_try_buf_match` Context:** `ublk_try_buf_match` is called from `ublk_dispatch_req`, and that, in turn, is *only* executed as task work which is queued by `ublk_queue_rq`. That means it's running *asynchronously* but for an active block request.
3. **The Maple Tree:** `ublk_try_buf_match` interacts with `ub->buf_tree`, a maple tree. This is where things can get tricky because this tree is modified by `ublk_ctrl_reg_buf` and `ublk_ctrl_unreg_buf`. The potential conflict is that those are from the control path while the match is from the IO path.
4. **Synchronization Mechanism:** Both modification functions call `ublk_lock_buf_tree`, which crucially calls `blk_mq_freeze_queue`. This is the synchronization point.
5. **`blk_mq_freeze_queue` Behavior:** `blk_mq_freeze_queue` waits *until all in-flight requests* on the block queue are completed. This is the lynchpin of the argument.
6. **The Implication:** Since `ublk_try_buf_match` runs *for an in-flight request*, `blk_mq_freeze_queue` blocks until `ublk_try_buf_match` is done. `blk_mq_freeze_queue` is called before tree modifications. This means that tree modifications wait for the matching to finish.
7. **Conclusion: No Concurrency:** Therefore, `ublk_try_buf_match` *cannot* run concurrently with any tree modifications. The synchronization is effective, and the data race does not exist.
8. **Lockdep's Flaw:** Lockdep is missing this synchronization provided by `blk_mq_freeze_queue`. It's a false positive.
9. **No Impact:** As a consequence, there's no real race condition. There's no memory corruption, no use-after-free, and no exploitability. The lockdep warning is just that: a warning.
10. **Trigger Conditions:** It can be triggered by unprivileged users IF they have access to the ublk control device *and* create a ublk device with `UBLK_F_SHMEM_ZC` and `UBLK_F_UNPRIVILEGED_DEV`.
Okay, so where's the potential for the bug to exist? Is there one? What about unprivileged access? Normally `/dev/ublk-control` is for root, or a specific group. But this reproducer runs as root.
But ublk *allows* unprivileged use. `ublk_ctrl_add_dev` checks `capable(CAP_SYS_ADMIN)` and allows it if `UBLK_F_UNPRIVILEGED_DEV` is set. This means an admin wants the functionality there.
So an unprivileged user can create and use this device, so long as they have access to `/dev/ublk-control`. Maybe by udev rules.
* **User Namespaces:** Can this be triggered from a user namespace? Let's check. `capable(CAP_SYS_ADMIN)` is based on the *initial* namespace, not the current one. So if you're in a user namespace, `CAP_SYS_ADMIN` is usually false. So the user would have to have set `UBLK_F_UNPRIVILEGED_DEV`. So *yes*, it can be triggered from a user namespace, or by an unprivileged user if they have access to `/dev/ublk-control`.
* **No Functional Effect:** However, because the lockdep warning is a false positive, it only generates a warning in `dmesg`. Lockdep is usually disabled in production builds so there's no effect in such builds. Even if it's enabled, it causes only a warning. There's no crash, hang, or resource leak. It is not a denial of service.
|
| 75/2 |
2026/09/15 03:20 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_force_abort_dev]
Results: map[SourceCode:2960: static void ublk_force_abort_dev(struct ublk_device *ub)
2961: {
2962: u16 i;
2963:
2964: pr_devel("%s: force abort ub: dev_id %d state %s\n",
2965: __func__, ub->dev_info.dev_id,
2966: ub->dev_info.state == UBLK_S_DEV_LIVE ?
2967: "LIVE" : "QUIESCED");
2968: blk_mq_quiesce_queue(ub->ub_disk->queue);
2969: if (ub->dev_info.state == UBLK_S_DEV_LIVE)
2970: ublk_wait_tagset_rqs_idle(ub);
2971:
2972: for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
2973: ublk_get_queue(ub, i)->force_abort = true;
2974: blk_mq_unquiesce_queue(ub->ub_disk->queue);
2975: /* We may have requeued some rqs in ublk_quiesce_queue() */
2976: blk_mq_kick_requeue_list(ub->ub_disk->queue);
2977: }
SourceFile:drivers/block/ublk_drv.c]
|
| 76/2 |
2026/09/15 03:20 |
llm |
expert |
0m
Model:
Tokens:
input: 78073
output: 50
thoughts: 1506
Thoughts: **Analyzing the Ublk Maple Tree and Lockdep Warning**
Okay, let's break this down. My initial thought was: the `ublk_force_abort_dev` function sets `force_abort = true` and unquiesces the queue, and then `del_gendisk` is called to destroy the disk. The `del_gendisk` function freezes the queue, and if a request was submitted and its task work is pending, `ublk_dispatch_req` is run. Inside of that `ublk_setup_iod` is called, which then calls `ublk_init_iod`, and that then calls `ublk_try_buf_match`. My immediate concern was that if `force_abort` is true, wouldn't `ublk_start_io` potentially fail after `ublk_setup_iod` has been called? However, `ublk_setup_iod` is called *before* `ublk_start_io`, so `ublk_try_buf_match` would still execute, but the request wouldn't be completed yet, so the queue usage counter `q_usage_counter` would still be elevated, and `del_gendisk` would be waiting in `blk_mq_freeze_queue` since the queue isn't truly frozen. Thus `ub->ub_disk` would still be non-NULL. This means that, even in the abort path, `ublk_try_buf_match` runs while the queue is not completely frozen, it is in the process of freezing (waiting for this request). I hypothesized that `ublk_lock_buf_tree` would then call `blk_mq_freeze_queue` and block, but there is no race.
Now, my next line of thought was about the access to `ub->buf_tree` in `ublk_try_buf_match` *after* `ublk_buf_cleanup` has potentially destroyed it. I observed that `ublk_buf_cleanup` is called from `ublk_cdev_rel`, which is called when the character device is closed. Specifically:
```c
static void ublk_cdev_rel(struct device *dev)
{
struct ublk_device *ub = container_of(dev, struct ublk_device, cdev_dev);
blk_mq_free_tag_set(&ub->tag_set);
ublk_deinit_queues(ub);
ublk_free_dev_number(ub);
mutex_destroy(&ub->mutex);
mutex_destroy(&ub->cancel_mutex);
ublk_buf_cleanup(ub);
kfree(ub);
}
```
This is the release function for the device and is called when the last reference to `ub` is dropped. I determined that the references come from the block device (via `ublk_get_device` and `ublk_detach_disk`) and the char device, and also by in-flight requests. I realized that if `ublk_cdev_rel` is called, then *all* references to `ub` are dropped. The block device cannot be destroyed until all requests are completed (because `del_gendisk` freezes the queue and waits). This meant that `ublk_cdev_rel` cannot be called while there's an in-flight request, thus `ublk_buf_cleanup` cannot be called while `ublk_try_buf_match` is running.
So, from this analysis, it seems perfectly safe. The lockdep warning is likely a false positive. I will classify it as follows:
- Exploitable: False
- DenialOfService: False (it's just a lockdep warning, not a crash in production)
- Unprivileged: True (unprivileged users can trigger if they have access to `/dev/ublk-control`, which is possible with `UBLK_F_UNPRIVILEGED_DEV`)
- UserNamespace: True
- VMGuestTrigger: False
- VMHostTrigger: False
- NetworkTrigger: False
- RemoteTrigger: False
- PeripheralTrigger: False
- FilesystemTrigger: False
I also checked to see if it was possible to initialize `ublk_try_buf_match` when `ublk_support_shmem_zc(ubq)` is true, but the tree was never initialized. `mt_init(&ub->buf_tree)` is called in `ublk_ctrl_add_dev`, so the tree is always initialized when the device is created, so this is safe as well.
I further double-checked if there was any other way to trigger `ublk_try_buf_match` without `q_usage_counter
|
| 77/2 |
2026/09/15 03:21 |
tool |
git-log |
0m
Args: map[CodeRegexp:rcu_read_lock Count:5 PathPrefix:drivers/block/ublk_drv.c Since:1 year]
Results: map[Output:704340f1cd0d Merge tag 'x86_urgent_for_7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
]
|
| 78/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 80719
output: 52
thoughts: 17
Thoughts: Here's my thought process, as if I were considering this search:
**Diving into the ublk_try_buf_match Commit History**
Okay, so I need to find all the commits that touch `ublk_try_buf_match`. This suggests something specific is being tweaked or improved within the Universal Block Layer's buffer matching function. I'm probably looking for changes related to performance optimization, bug fixes, or potentially new features interacting with this matching logic. My experience tells me this function is likely core to efficient data transfer within the Ublk framework.
I'll start with a straightforward search for commits where that specific function name appears. That will be the initial, broad approach. Then, I’ll refine this initial search. I'll need to think about variations in the search query. Maybe I'll need to account for slight name changes, like if it was renamed at some point, or if there's a related function using a similar naming convention, and then search those.
I'll keep an eye out for associated commit messages. Keywords such as "performance," "optimization," "bugfix," "latency," "throughput," "buffer," "Ublk," and relevant hardware references will jump out. If I see a commit referencing a specific storage device, I'll pay close attention; any changes to buffer handling or match logic often indicate platform-specific issues or performance tuning. It might reveal how they are addressing the interaction between the Ublk layer and the underlying block storage.
I'm prepared to dive into the code diffs themselves. The commit messages will guide me to the relevant changes, but I need to fully understand how the changes impact the function’s behavior. I will look for how the matching logic itself might be altered, which might reveal critical performance bottlenecks or improvements.
I want to analyze the context of the changes - the surrounding code and dependencies. I'll want to check if the changes involve mutexes, atomics, or other concurrency-related aspects of the `ublk_try_buf_match` function, and verify its thread safety.
Ultimately, I want to gain a solid understanding of why this function was modified and what impact those modifications had on the Ublk system. That's the goal of this search.
|
| 79/2 |
2026/09/15 03:21 |
tool |
git-log |
0m
Args: map[CodeRegexp:ublk_try_buf_match Count:5 PathPrefix:drivers/block/ublk_drv.c Since:1 year]
Results: map[Output:704340f1cd0d Merge tag 'x86_urgent_for_7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
]
|
| 80/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 81325
output: 44
thoughts: 20
Thoughts: Here's your summary:
**Digging into the Git Log**
Okay, so I was initially looking at the `git log` output, expecting to find the juicy details I needed. But, hold on a second... this is just a merge commit! Nothing substantive here. That's a dead end. Right, no point in wasting time on that approach. Let's pivot and try something more targeted. Let's see if using `SymbolName` gets us any closer to the information I actually require. Perhaps that will reveal the specific commit responsible.
|
| 81/2 |
2026/09/15 03:21 |
tool |
git-log |
0m
Args: map[Count:5 SourcePath:drivers/block/ublk_drv.c SymbolName:ublk_try_buf_match]
Results: map[Output:704340f1cd0d Merge tag 'x86_urgent_for_7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
]
|
| 82/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 81571
output: 31
thoughts: 29
Thoughts: Here's a summary of my immediate thought process:
**Debugging Git Log Issues**
Okay, hold on a second. I suspect there's something wrong with how I'm using `git-log`, or potentially it's returning nothing at all, which is throwing off my workflow. I need to troubleshoot this. Perhaps the filter criteria I'm providing are too restrictive, or maybe the command itself isn't executing as expected. A common pitfall is misunderstanding how `git-log` handles filtering. I need to ensure I'm getting the output I expect, and that the history is being properly parsed. Time to try using `git-log` again, but this time I'll experiment with `MessageRegexps` to see if that helps narrow down or expand the results, allowing me to isolate the problem more effectively.
|
| 83/2 |
2026/09/15 03:21 |
tool |
git-log |
0m
Args: map[Count:5 MessageRegexps:[ublk suspicious RCU usage]]
Results:
|
| 84/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 81819
output: 28
thoughts: 24
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Investigating Potential Performance Bottlenecks with `git log`**
Okay, so I'm trying to track down something specific related to the ublk and rcu subsystems, probably looking at recent changes that might be causing an issue. I know that `git log` is going to be my best friend here. What I need is to filter the log output. Let's see... I'll use `git log` to dig through the history and I'm going to leverage the `MessageRegexps` option. I can feed this argument an array of strings. I'll pass in ["ublk", "rcu"]. With that I can quickly narrow down the commits I'm interested in by filtering for those that include the words "ublk" or "rcu" in the commit message. Hopefully, I'll be able to quickly zero in on the relevant commits. Then, I can start looking at the diffs.
|
| 85/2 |
2026/09/15 03:21 |
tool |
git-log |
0m
Args: map[Count:5 MessageRegexps:[ublk rcu]]
Results:
|
| 86/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 82097
output: 29
thoughts: 35
Thoughts: Okay, let's see...
**Checking the RCU Status on That Ubiquitous Ublk Bug**
Hmm, I'm getting a nagging feeling. It's possible that the bug isn't *truly* fixed yet, or maybe the fix overlooked something critical, especially concerning RCU usage. We've seen these kinds of subtleties before. Best to double-check. Let's dig into the history of `ublk_try_buf_match`. The change logs should tell the tale. I need to understand what's been touched regarding that function and see if RCU implications are mentioned, or even hinted at, within the patches. Need to make sure there are no missed interactions.
|
| 87/2 |
2026/09/15 03:21 |
tool |
git-log |
0m
Args: map[Count:5 MessageRegexps:[ublk_try_buf_match]]
Results:
|
| 88/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 82322
output: 40
thoughts: 45
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in the weeds of kernel development:
**Debugging a Git Issue**
Alright, hang on a second. `git log` isn't giving me anything useful, which is strange. I was expecting a history there. Hmm, okay, maybe there's some kind of filtering I'm missing. No, let's just bypass that rabbit hole for now. I need to understand the history around `ublk_try_buf_match`, specifically within `drivers/block/ublk_drv.c`. Forget `git log` for a moment; let's cut straight to `git blame` on that specific function. I'll get the line-by-line attribution and see who was touching it, when, and what comments they left. That's a more direct route to the relevant changes. Hopefully, it will illuminate the current issue. Time to get my hands dirty.
|
| 89/2 |
2026/09/15 03:21 |
tool |
git-blame |
0m
Args: map[End:5640 File:drivers/block/ublk_drv.c Start:5620]
Results: map[Output:^704340f1cd0d 5620) /* Check if request pages match a registered shared memory buffer */
^704340f1cd0d 5621) static bool ublk_try_buf_match(struct ublk_device *ub,
^704340f1cd0d 5622) struct request *rq,
^704340f1cd0d 5623) u32 *buf_idx, u32 *buf_off)
^704340f1cd0d 5624) {
^704340f1cd0d 5625) struct req_iterator iter;
^704340f1cd0d 5626) struct bio_vec bv;
^704340f1cd0d 5627) int index = -1;
^704340f1cd0d 5628) unsigned long expected_offset = 0;
^704340f1cd0d 5629) bool first = true;
^704340f1cd0d 5630)
^704340f1cd0d 5631) rq_for_each_bvec(bv, rq, iter) {
^704340f1cd0d 5632) unsigned long pfn = page_to_pfn(bv.bv_page);
^704340f1cd0d 5633) unsigned long end_pfn = pfn +
^704340f1cd0d 5634) ((bv.bv_offset + bv.bv_len - 1) >> PAGE_SHIFT);
^704340f1cd0d 5635) struct ublk_buf_range *range;
^704340f1cd0d 5636) unsigned long off;
^704340f1cd0d 5637) MA_STATE(mas, &ub->buf_tree, pfn, pfn);
^704340f1cd0d 5638)
^704340f1cd0d 5639) range = mas_walk(&mas);
^704340f1cd0d 5640) if (!range)
]
|
| 90/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 83253
output: 30
thoughts: 48
Thoughts: Okay, here's my interpretation of that.
**Analyzing a Git Merge Commit**
Alright, let's see, the commit ID in question is `704340f1cd0d`. Hang on a second... that ID looks familiar. Oh yeah, it's a merge commit. So, instead of directly seeing the changes, I'll need to dig into the merge itself. Now, let's see what that merge commit, `704340f1cd0d`, is all about. Time to trace this back.
|
| 91/2 |
2026/09/15 03:21 |
tool |
git-show |
0m
Args: map[Commit:704340f1cd0d Stat:true]
Results: map[Output:commit 704340f1cd0dcef829eb62f5b48ae95a2ce17bdf
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date: Sun Sep 13 17:50:28 2026 -0700
Merge tag 'x86_urgent_for_7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fixes from Dave Hansen:
"The most notable fix is THP not silently losing user data and having
been around for a couple of years. The main explanation I'd have for
its longevity is that it requires a few different things to align at
the same time: MADV_FREE, THP and heavy reclaim.
- Fix user-space data loss with THP
- Fix set_memory oopses
- Fix addition of large constants in mul_u64_add_u64_div_u64()
- Fix FineIBT hash offset in cfi_get_func_hash()
- Fix PCI device reference counting in amd_smn_init()"
* tag 'x86_urgent_for_7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/amd_node: Fix PCI device reference counting in amd_smn_init()
x86/div64: Fix addition of large constants in mul_u64_add_u64_div_u64()
x86/cfi: Fix FineIBT hash offset in cfi_get_func_hash()
x86/mm: Fix user-space data loss with MADV_FREE and THP
x86/mm/pat: Allocate split page tables as kernel page tables
x86/alternatives: Exclude text poking against change_page_attr()
x86/mm/pat: Acquire init_mm read lock on attribute changes to avoid UAF
x86/mm/pat: Acquire init_mm write lock on collapse to avoid UAF
.clang-format | 808 +
.clippy.toml | 21 +
.cocciconfig | 3 +
.editorconfig | 27 +
.get_maintainer.ignore | 9 +
.gitattributes | 5 +
.gitignore | 196 +
.mailmap | 996 +
.pylintrc | 2 +
.rustfmt.toml | 12 +
COPYING | 20 +
CREDITS | 4651 +
Documentation/.gitignore | 3 +
Documentation/.renames.txt | 1185 +
Documentation/ABI/README | 98 +
.../ABI/obsolete/automount-tracefs-debugfs | 20 +
Documentation/ABI/obsolete/o2cb | 11 +
Documentation/ABI/obsolete/procfs-i8k | 10 +
Documentation/ABI/obsolete/sysfs-bus-iio | 174 +
Documentation/ABI/obsolete/sysfs-bus-usb | 31 +
Documentation/ABI/obsolete/sysfs-class-typec | 48 +
Documentation/ABI/obsolete/sysfs-cpuidle | 9 +
.../ABI/obsolete/sysfs-driver-hid-roccat-arvo | 53 +
.../ABI/obsolete/sysfs-driver-hid-roccat-isku | 153 +
.../ABI/obsolete/sysfs-driver-hid-roccat-koneplus | 145 +
.../ABI/obsolete/sysfs-driver-hid-roccat-konepure | 105 +
.../ABI/obsolete/sysfs-driver-hid-roccat-kovaplus | 116 +
.../ABI/obsolete/sysfs-driver-hid-roccat-lua | 7 +
.../ABI/obsolete/sysfs-driver-hid-roccat-pyra | 126 +
.../ABI/obsolete/sysfs-driver-hid-roccat-ryos | 178 +
.../ABI/obsolete/sysfs-driver-hid-roccat-savu | 77 +
.../ABI/obsolete/sysfs-driver-intel_pmc_bxt | 22 +
Documentation/ABI/obsolete/sysfs-driver-ivpu | 30 +
.../ABI/obsolete/sysfs-driver-samsung-laptop | 10 +
Documentation/ABI/obsolete/sysfs-firmware-acpi | 8 +
Documentation/ABI/obsolete/sysfs-gpio | 40 +
.../ABI/obsolete/sysfs-kernel-fadump_enabled | 9 +
.../ABI/obsolete/sysfs-kernel-fadump_registered | 10 +
.../ABI/obsolete/sysfs-kernel-fadump_release_mem | 10 +
.../ABI/obsolete/sysfs-kernel-kexec-kdump | 71 +
.../ABI/obsolete/sysfs-platform-ideapad-laptop | 8 +
Documentation/ABI/removed/devfs | 13 +
Documentation/ABI/removed/dv1394 | 14 +
Documentation/ABI/removed/ip_queue | 9 +
Documentation/ABI/removed/net_dma | 8 +
Documentation/ABI/removed/o2cb | 10 +
Documentation/ABI/removed/raw1394 | 16 +
Documentation/ABI/removed/sysfs-bus-nfit | 17 +
Documentation/ABI/removed/sysfs-class-cxl | 272 +
Documentation/ABI/removed/sysfs-class-rfkill | 13 +
Documentation/ABI/removed/sysfs-firmware-efi-vars | 12 +
.../removed/sysfs-kernel-fadump_release_opalcore | 9 +
Documentation/ABI/removed/sysfs-kernel-uids | 14 +
Documentation/ABI/removed/sysfs-mce | 37 +
.../ABI/removed/sysfs-selinux-checkreqprot | 26 +
Documentation/ABI/removed/sysfs-selinux-disable | 29 +
Documentation/ABI/removed/sysfs-selinux-user | 12 +
Documentation/ABI/removed/video1394 | 17 +
Documentation/ABI/stable/configfs-nvmet | 352 +
Documentation/ABI/stable/firewire-cdev | 111 +
Documentation/ABI/stable/o2cb | 10 +
Documentation/ABI/stable/procfs-audit_loginuid | 27 +
Documentation/ABI/stable/syscalls | 10 +
Documentation/ABI/stable/sysfs-acpi-pmprofile | 28 +
Documentation/ABI/stable/sysfs-block | 941 +
Documentation/ABI/stable/sysfs-bus-firewire | 136 +
Documentation/ABI/stable/sysfs-bus-fsl-mc | 19 +
Documentation/ABI/stable/sysfs-bus-mhi | 44 +
Documentation/ABI/stable/sysfs-bus-nvmem | 50 +
Documentation/ABI/stable/sysfs-bus-usb | 142 +
Documentation/ABI/stable/sysfs-bus-vmbus | 187 +
Documentation/ABI/stable/sysfs-bus-w1 | 12 +
Documentation/ABI/stable/sysfs-bus-xen-backend | 84 +
Documentation/ABI/stable/sysfs-class-backlight | 63 +
Documentation/ABI/stable/sysfs-class-bluetooth | 9 +
Documentation/ABI/stable/sysfs-class-infiniband | 784 +
Documentation/ABI/stable/sysfs-class-rfkill | 93 +
Documentation/ABI/stable/sysfs-class-tpm | 210 +
Documentation/ABI/stable/sysfs-class-ubi | 221 +
Documentation/ABI/stable/sysfs-class-udc | 93 +
Documentation/ABI/stable/sysfs-devices | 32 +
Documentation/ABI/stable/sysfs-devices-node | 238 +
Documentation/ABI/stable/sysfs-devices-system-cpu | 117 +
.../ABI/stable/sysfs-devices-system-xen_memory | 86 +
Documentation/ABI/stable/sysfs-driver-aspeed-vuart | 24 +
Documentation/ABI/stable/sysfs-driver-dma-idxd | 376 +
Documentation/ABI/stable/sysfs-driver-dma-ioatdma | 30 +
.../ABI/stable/sysfs-driver-firmware-zynqmp | 256 +
Documentation/ABI/stable/sysfs-driver-ib_srp | 192 +
Documentation/ABI/stable/sysfs-driver-misc-cp500 | 25 +
Documentation/ABI/stable/sysfs-driver-mlxreg-io | 815 +
Documentation/ABI/stable/sysfs-driver-qaic | 19 +
Documentation/ABI/stable/sysfs-driver-qla2xxx | 8 +
Documentation/ABI/stable/sysfs-driver-speakup | 394 +
Documentation/ABI/stable/sysfs-driver-usb-usbtmc | 27 +
Documentation/ABI/stable/sysfs-driver-w1_ds2438 | 13 +
Documentation/ABI/stable/sysfs-driver-w1_ds28e04 | 15 +
Documentation/ABI/stable/sysfs-driver-w1_ds28ea00 | 6 +
Documentation/ABI/stable/sysfs-firmware-opal-dump | 46 +
Documentation/ABI/stable/sysfs-firmware-opal-elog | 62 +
Documentation/ABI/stable/sysfs-fs-orangefs | 87 +
Documentation/ABI/stable/sysfs-hypervisor-xen | 135 +
Documentation/ABI/stable/sysfs-kernel-notes | 5 +
.../ABI/stable/sysfs-kernel-time-aux-clocks | 5 +
Documentation/ABI/stable/sysfs-module | 47 +
Documentation/ABI/stable/sysfs-nvme | 453 +
Documentation/ABI/stable/sysfs-platform-wmi-bmof | 7 +
Documentation/ABI/stable/sysfs-transport-srp | 58 +
Documentation/ABI/stable/thermal-notification | 4 +
Documentation/ABI/stable/vdso | 37 +
Documentation/ABI/testing/amdpmf-interface | 73 +
Documentation/ABI/testing/configfs-acpi | 52 +
Documentation/ABI/testing/configfs-iio | 34 +
Documentation/ABI/testing/configfs-most | 241 +
Documentation/ABI/testing/configfs-rdma_cm | 34 +
.../ABI/testing/configfs-spear-pcie-gadget | 33 +
Documentation/ABI/testing/configfs-stp-policy | 48 +
.../ABI/testing/configfs-stp-policy-p_sys-t | 41 +
.../ABI/testing/configfs-thunderbolt_stream | 98 +
Documentation/ABI/testing/configfs-tsm-report | 161 +
Documentation/ABI/testing/configfs-usb-gadget | 158 +
Documentation/ABI/testing/configfs-usb-gadget-acm | 15 +
Documentation/ABI/testing/configfs-usb-gadget-ecm | 20 +
Documentation/ABI/testing/configfs-usb-gadget-eem | 16 +
Documentation/ABI/testing/configfs-usb-gadget-ffs | 17 +
Documentation/ABI/testing/configfs-usb-gadget-hid | 13 +
.../ABI/testing/configfs-usb-gadget-loopback | 10 +
.../ABI/testing/configfs-usb-gadget-mass-storage | 41 +
Documentation/ABI/testing/configfs-usb-gadget-midi | 15 +
.../ABI/testing/configfs-usb-gadget-midi2 | 54 +
Documentation/ABI/testing/configfs-usb-gadget-ncm | 15 +
Documentation/ABI/testing/configfs-usb-gadget-obex | 9 +
.../ABI/testing/configfs-usb-gadget-phonet | 8 +
.../ABI/testing/configfs-usb-gadget-printer | 11 +
.../ABI/testing/configfs-usb-gadget-rndis | 19 +
.../ABI/testing/configfs-usb-gadget-serial | 9 +
.../ABI/testing/configfs-usb-gadget-sourcesink | 16 +
.../ABI/testing/configfs-usb-gadget-subset | 16 +
Documentation/ABI/testing/configfs-usb-gadget-tcm | 6 +
Documentation/ABI/testing/configfs-usb-gadget-uac1 | 41 +
.../ABI/testing/configfs-usb-gadget-uac1_legacy | 9 +
Documentation/ABI/testing/configfs-usb-gadget-uac2 | 51 +
Documentation/ABI/testing/configfs-usb-gadget-uvc | 438 +
Documentation/ABI/testing/debugfs-alienware-wmi | 64 +
Documentation/ABI/testing/debugfs-amd-iommu | 131 +
Documentation/ABI/testing/debugfs-cdns-pcie | 5 +
Documentation/ABI/testing/debugfs-cec-error-inj | 40 +
Documentation/ABI/testing/debugfs-cros-ec | 78 +
Documentation/ABI/testing/debugfs-cxl | 157 +
Documentation/ABI/testing/debugfs-dell-wmi-ddv | 34 +
Documentation/ABI/testing/debugfs-driver-dcc | 127 +
Documentation/ABI/testing/debugfs-driver-genwqe | 91 +
.../ABI/testing/debugfs-driver-habanalabs | 323 +
Documentation/ABI/testing/debugfs-driver-qat | 109 +
.../ABI/testing/debugfs-driver-qat_telemetry | 259 +
Documentation/ABI/testing/debugfs-dwc-pcie | 157 +
Documentation/ABI/testing/debugfs-ec | 21 +
Documentation/ABI/testing/debugfs-hisi-hpre | 200 +
Documentation/ABI/testing/debugfs-hisi-migration | 25 +
Documentation/ABI/testing/debugfs-hisi-sec | 173 +
Documentation/ABI/testing/debugfs-hisi-zip | 174 +
Documentation/ABI/testing/debugfs-hyperv | 23 +
Documentation/ABI/testing/debugfs-ideapad | 19 +
Documentation/ABI/testing/debugfs-iio-ad9467 | 39 +
Documentation/ABI/testing/debugfs-iio-backend | 20 +
Documentation/ABI/testing/debugfs-intel-iommu | 276 +
Documentation/ABI/testing/debugfs-moxtet | 35 +
Documentation/ABI/testing/debugfs-msi-wmi-platform | 14 +
Documentation/ABI/testing/debugfs-olpc | 16 +
Documentation/ABI/testing/debugfs-pcie-ptm | 70 +
Documentation/ABI/testing/debugfs-pfo-nx-crypto | 45 +
Documentation/ABI/testing/debugfs-scmi | 70 +
Documentation/ABI/testing/debugfs-scmi-raw | 208 +
Documentation/ABI/testing/debugfs-tpmi | 40 +
Documentation/ABI/testing/debugfs-vfio | 57 +
Documentation/ABI/testing/debugfs-wilco-ec | 45 +
Documentation/ABI/testing/dell-smbios-wmi | 41 +
Documentation/ABI/testing/dev-kmsg | 127 +
Documentation/ABI/testing/devlink-resource-mlxsw | 33 +
Documentation/ABI/testing/evm | 109 +
Documentation/ABI/testing/gpio-cdev | 28 +
Documentation/ABI/testing/ima_policy | 196 +
Documentation/ABI/testing/ppc-memtrace | 46 +
Documentation/ABI/testing/procfs-attr-current | 20 +
Documentation/ABI/testing/procfs-attr-exec | 20 +
Documentation/ABI/testing/procfs-attr-prev | 19 +
Documentation/ABI/testing/procfs-diskstats | 43 +
.../ABI/testing/procfs-nfsd-reply_cache_stats | 38 +
Documentation/ABI/testing/procfs-smaps_rollup | 42 +
Documentation/ABI/testing/pstore | 47 +
Documentation/ABI/testing/rtc-cdev | 50 +
Documentation/ABI/testing/securityfs-secrets-coco | 51 +
Documentation/ABI/testing/sysfs-amd-pmc | 13 +
Documentation/ABI/testing/sysfs-amd-pmf | 13 +
Documentation/ABI/testing/sysfs-ata | 140 +
.../ABI/testing/sysfs-auxdisplay-linedisp | 90 +
Documentation/ABI/testing/sysfs-block-aoe | 45 +
Documentation/ABI/testing/sysfs-block-bcache | 149 +
Documentation/ABI/testing/sysfs-block-device | 119 +
Documentation/ABI/testing/sysfs-block-dm | 47 +
Documentation/ABI/testing/sysfs-block-loop | 50 +
Documentation/ABI/testing/sysfs-block-rnbd | 64 +
Documentation/ABI/testing/sysfs-block-rssd | 5 +
Documentation/ABI/testing/sysfs-block-zram | 166 +
Documentation/ABI/testing/sysfs-bus-acpi | 98 +
Documentation/ABI/testing/sysfs-bus-amba | 20 +
Documentation/ABI/testing/sysfs-bus-auxiliary | 9 +
Documentation/ABI/testing/sysfs-bus-bcma | 31 +
Documentation/ABI/testing/sysfs-bus-cdx | 121 +
Documentation/ABI/testing/sysfs-bus-coreboot | 45 +
.../ABI/testing/sysfs-bus-coresight-devices-cti | 247 +
.../sysfs-bus-coresight-devices-dummy-source | 21 +
.../ABI/testing/sysfs-bus-coresight-devices-etb10 | 88 +
.../ABI/testing/sysfs-bus-coresight-devices-etm3x | 328 +
.../ABI/testing/sysfs-bus-coresight-devices-etm4x | 532 +
.../ABI/testing/sysfs-bus-coresight-devices-funnel | 18 +
.../ABI/testing/sysfs-bus-coresight-devices-stm | 59 +
.../ABI/testing/sysfs-bus-coresight-devices-tmc | 115 +
.../ABI/testing/sysfs-bus-coresight-devices-tpda | 69 +
.../ABI/testing/sysfs-bus-coresight-devices-tpdm | 290 +
.../ABI/testing/sysfs-bus-coresight-devices-trbe | 20 +
.../testing/sysfs-bus-coresight-devices-ultra_smb | 31 +
Documentation/ABI/testing/sysfs-bus-counter | 477 +
Documentation/ABI/testing/sysfs-bus-css | 56 +
Documentation/ABI/testing/sysfs-bus-cxl | 646 +
Documentation/ABI/testing/sysfs-bus-dax | 177 +
Documentation/ABI/testing/sysfs-bus-dfl | 17 +
.../ABI/testing/sysfs-bus-dfl-devices-emif | 25 +
.../ABI/testing/sysfs-bus-dfl-devices-n3000-nios | 47 +
.../ABI/testing/sysfs-bus-event_source-devices | 24 +
.../testing/sysfs-bus-event_source-devices-caps | 24 +
.../testing/sysfs-bus-event_source-devices-dfl_fme | 104 +
.../ABI/testing/sysfs-bus-event_source-devices-dsa | 30 +
.../testing/sysfs-bus-event_source-devices-events | 102 +
.../testing/sysfs-bus-event_source-devices-format | 21 +
.../sysfs-bus-event_source-devices-hisi_ptt | 113 +
.../testing/sysfs-bus-event_source-devices-hv_24x7 | 98 +
.../testing/sysfs-bus-event_source-devices-hv_gpci | 242 +
.../testing/sysfs-bus-event_source-devices-iommu | 37 +
.../testing/sysfs-bus-event_source-devices-rdpmc | 44 +
.../testing/sysfs-bus-event_source-devices-uncore | 13 +
.../testing/sysfs-bus-event_source-devices-vpa-dtl | 25 +
.../testing/sysfs-bus-event_source-devices-vpa-pmu | 25 +
Documentation/ABI/testing/sysfs-bus-fcoe | 138 +
Documentation/ABI/testing/sysfs-bus-fsi | 46 +
.../ABI/testing/sysfs-bus-fsi-devices-sbefifo | 10 +
Documentation/ABI/testing/sysfs-bus-fsl-mc | 25 +
Documentation/ABI/testing/sysfs-bus-hsi | 19 +
.../ABI/testing/sysfs-bus-i2c-devices-bq32k | 7 +
.../ABI/testing/sysfs-bus-i2c-devices-fsa9480 | 27 +
.../ABI/testing/sysfs-bus-i2c-devices-hm6352 | 21 +
.../ABI/testing/sysfs-bus-i2c-devices-lm3533 | 15 +
.../ABI/testing/sysfs-bus-i2c-devices-m24lr | 100 +
.../ABI/testing/sysfs-bus-i2c-devices-pca954x | 23 +
.../testing/sysfs-bus-i2c-devices-turris-omnia-mcu | 113 +
Documentation/ABI/testing/sysfs-bus-i3c | 194 +
Documentation/ABI/testing/sysfs-bus-iio | 2511 +
.../ABI/testing/sysfs-bus-iio-accel-adxl372 | 7 +
.../ABI/testing/sysfs-bus-iio-accel-bmc150 | 7 +
Documentation/ABI/testing/sysfs-bus-iio-ad9739a | 19 +
.../ABI/testing/sysfs-bus-iio-adc-ad-sigma-delta | 23 +
Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130 | 20 +
Documentation/ABI/testing/sysfs-bus-iio-adc-ad7192 | 27 +
.../ABI/testing/sysfs-bus-iio-adc-ad7280a | 13 +
.../testing/sysfs-bus-iio-adc-envelope-detector | 39 +
Documentation/ABI/testing/sysfs-bus-iio-adc-hi8435 | 48 +
.../ABI/testing/sysfs-bus-iio-adc-max11410 | 13 +
.../ABI/testing/sysfs-bus-iio-adc-mcp3564 | 53 +
Documentation/ABI/testing/sysfs-bus-iio-adc-mt6360 | 78 +
.../ABI/testing/sysfs-bus-iio-adc-pac1934 | 9 +
Documentation/ABI/testing/sysfs-bus-iio-adc-stm32 | 21 +
Documentation/ABI/testing/sysfs-bus-iio-bno055 | 81 +
Documentation/ABI/testing/sysfs-bus-iio-cdc-ad7746 | 11 +
.../ABI/testing/sysfs-bus-iio-chemical-sgp40 | 17 +
.../ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 | 38 +
.../ABI/testing/sysfs-bus-iio-chemical-vz89x | 7 +
Documentation/ABI/testing/sysfs-bus-iio-cros-ec | 21 +
Documentation/ABI/testing/sysfs-bus-iio-dac | 61 +
Documentation/ABI/testing/sysfs-bus-iio-dac-ad5766 | 31 +
.../ABI/testing/sysfs-bus-iio-dac-dpot-dac | 8 +
.../ABI/testing/sysfs-bus-iio-dac-ltc2688 | 55 +
.../ABI/testing/sysfs-bus-iio-dfsdm-adc-stm32 | 22 +
.../ABI/testing/sysfs-bus-iio-distance-srf08 | 17 +
Documentation/ABI/testing/sysfs-bus-iio-dma-buffer | 19 +
.../ABI/testing/sysfs-bus-iio-filter-admv8818 | 18 +
Documentation/ABI/testing/sysfs-bus-iio-flow | 21 +
Documentation/ABI/testing/sysfs-bus-iio-frequency | 11 +
.../ABI/testing/sysfs-bus-iio-frequency-ad9523 | 31 +
.../ABI/testing/sysfs-bus-iio-frequency-adf4350 | 11 +
.../ABI/testing/sysfs-bus-iio-frequency-adf4371 | 11 +
.../ABI/testing/sysfs-bus-iio-frequency-admv1013 | 38 +
.../ABI/testing/sysfs-bus-iio-frequency-admv1014 | 23 +
.../ABI/testing/sysfs-bus-iio-gyro-bmg160 | 7 +
.../ABI/testing/sysfs-bus-iio-health-afe440x | 39 +
Documentation/ABI/testing/sysfs-bus-iio-humidity | 10 +
.../sysfs-bus-iio-impedance-analyzer-ad5933 | 35 +
Documentation/ABI/testing/sysfs-bus-iio-ina2xx-adc | 15 +
.../ABI/testing/sysfs-bus-iio-inv_icm42600 | 18 +
Documentation/ABI/testing/sysfs-bus-iio-isl29501 | 47 +
.../ABI/testing/sysfs-bus-iio-light-isl29018 | 21 +
.../ABI/testing/sysfs-bus-iio-light-lm3533-als | 56 +
.../ABI/testing/sysfs-bus-iio-light-si1133 | 22 +
.../ABI/testing/sysfs-bus-iio-light-tsl2583 | 20 +
.../ABI/testing/sysfs-bus-iio-light-tsl2772 | 13 +
.../ABI/testing/sysfs-bus-iio-magnetometer-hmc5843 | 20 +
Documentation/ABI/testing/sysfs-bus-iio-meas-spec | 7 +
Documentation/ABI/testing/sysfs-bus-iio-mpu6050 | 13 +
.../testing/sysfs-bus-iio-potentiometer-mcp4531 | 8 +
Documentation/ABI/testing/sysfs-bus-iio-proximity | 24 +
.../ABI/testing/sysfs-bus-iio-proximity-as3935 | 15 +
.../ABI/testing/sysfs-bus-iio-resolver-ad2s1210 | 27 +
Documentation/ABI/testing/sysfs-bus-iio-sps30 | 28 +
Documentation/ABI/testing/sysfs-bus-iio-sx9310 | 10 +
Documentation/ABI/testing/sysfs-bus-iio-sx9324 | 29 +
.../ABI/testing/sysfs-bus-iio-thermocouple | 18 +
.../ABI/testing/sysfs-bus-iio-timer-stm32 | 143 +
.../ABI/testing/sysfs-bus-iio-trigger-sysfs | 42 +
Documentation/ABI/testing/sysfs-bus-iio-vf610 | 16 +
.../ABI/testing/sysfs-bus-intel_th-devices-gth | 52 +
.../ABI/testing/sysfs-bus-intel_th-devices-msc | 54 +
.../ABI/testing/sysfs-bus-intel_th-devices-pti | 24 +
.../ABI/testing/sysfs-bus-intel_th-output-devices | 15 +
Documentation/ABI/testing/sysfs-bus-mcb | 29 +
Documentation/ABI/testing/sysfs-bus-mdio | 72 +
Documentation/ABI/testing/sysfs-bus-media | 6 +
Documentation/ABI/testing/sysfs-bus-mei | 56 +
Documentation/ABI/testing/sysfs-bus-mmc | 4 +
Documentation/ABI/testing/sysfs-bus-most | 299 +
Documentation/ABI/testing/sysfs-bus-moxtet-devices | 17 +
Documentation/ABI/testing/sysfs-bus-nfit | 233 +
Documentation/ABI/testing/sysfs-bus-nvdimm | 60 +
Documentation/ABI/testing/sysfs-bus-optee-devices | 17 +
Documentation/ABI/testing/sysfs-bus-papr-pmem | 75 +
Documentation/ABI/testing/sysfs-bus-pci | 704 +
.../ABI/testing/sysfs-bus-pci-devices-aer | 163 +
.../ABI/testing/sysfs-bus-pci-devices-avs | 8 +
.../ABI/testing/sysfs-bus-pci-devices-catpt | 17 +
.../ABI/testing/sysfs-bus-pci-devices-cciss | 80 +
.../ABI/testing/sysfs-bus-pci-devices-pvpanic | 26 +
.../ABI/testing/sysfs-bus-pci-drivers-btintel_pcie | 15 +
.../ABI/testing/sysfs-bus-pci-drivers-ehci_hcd | 46 +
.../ABI/testing/sysfs-bus-pci-drivers-janz-cmodio | 8 +
.../ABI/testing/sysfs-bus-pci-drivers-xhci_hcd | 129 +
Documentation/ABI/testing/sysfs-bus-peci | 16 +
Documentation/ABI/testing/sysfs-bus-platform | 56 +
.../sysfs-bus-platform-devices-ampere-smpro | 325 +
.../testing/sysfs-bus-platform-devices-occ-hwmon | 13 +
.../sysfs-bus-platform-drivers-amd_x3d_vcache | 12 +
.../ABI/testing/sysfs-bus-platform-onboard-usb-dev | 9 +
Documentation/ABI/testing/sysfs-bus-rapidio | 201 +
Documentation/ABI/testing/sysfs-bus-rbd | 173 +
Documentation/ABI/testing/sysfs-bus-rpmsg | 95 +
Documentation/ABI/testing/sysfs-bus-siox | 90 +
.../ABI/testing/sysfs-bus-soundwire-master | 23 +
.../ABI/testing/sysfs-bus-soundwire-slave | 109 +
.../ABI/testing/sysfs-bus-spi-devices-spi-nor | 40 +
.../testing/sysfs-bus-surface_aggregator-tabletsw | 57 +
Documentation/ABI/testing/sysfs-bus-thunderbolt | 370 +
Documentation/ABI/testing/sysfs-bus-typec | 51 +
Documentation/ABI/testing/sysfs-bus-usb | 607 +
.../ABI/testing/sysfs-bus-usb-devices-usbsevseg | 46 +
Documentation/ABI/testing/sysfs-bus-usb-lvstest | 60 +
Documentation/ABI/testing/sysfs-bus-vdpa | 57 +
Documentation/ABI/testing/sysfs-bus-vfio-mdev | 113 +
Documentation/ABI/testing/sysfs-bus-vmbus | 21 +
Documentation/ABI/testing/sysfs-bus-wmi | 81 +
Documentation/ABI/testing/sysfs-c2port | 81 +
Documentation/ABI/testing/sysfs-cfq-target-latency | 8 +
Documentation/ABI/testing/sysfs-class | 16 +
Documentation/ABI/testing/sysfs-class-backlight | 186 +
.../testing/sysfs-class-backlight-driver-lm3533 | 54 +
.../ABI/testing/sysfs-class-backlight-lm3639 | 11 +
Documentation/ABI/testing/sysfs-class-bdi | 134 +
Documentation/ABI/testing/sysfs-class-bsr | 25 +
Documentation/ABI/testing/sysfs-class-chromeos | 53 +
.../sysfs-class-chromeos-driver-cros-ec-lightbar | 74 +
.../sysfs-class-chromeos-driver-cros-ec-vbc | 6 +
Documentation/ABI/testing/sysfs-class-devfreq | 143 +
.../ABI/testing/sysfs-class-devfreq-event | 25 +
Documentation/ABI/testing/sysfs-class-devlink | 130 +
Documentation/ABI/testing/sysfs-class-drm | 8 +
Documentation/ABI/testing/sysfs-class-extcon | 101 +
Documentation/ABI/testing/sysfs-class-fc | 27 +
Documentation/ABI/testing/sysfs-class-fc_host | 23 +
.../ABI/testing/sysfs-class-fc_remote_ports | 23 +
Documentation/ABI/testing/sysfs-class-firmware | 77 +
.../ABI/testing/sysfs-class-firmware-attributes | 494 +
Documentation/ABI/testing/sysfs-class-fpga-bridge | 11 +
Documentation/ABI/testing/sysfs-class-fpga-manager | 60 +
Documentation/ABI/testing/sysfs-class-fpga-region | 9 +
Documentation/ABI/testing/sysfs-class-gnss | 17 +
Documentation/ABI/testing/sysfs-class-hwmon | 1089 +
.../ABI/testing/sysfs-class-ieee80211-rtw89 | 24 +
Documentation/ABI/testing/sysfs-class-intel_pmt | 119 +
.../ABI/testing/sysfs-class-intel_pmt-features | 134 +
Documentation/ABI/testing/sysfs-class-iommu | 17 +
.../ABI/testing/sysfs-class-iommu-amd-iommu | 14 +
.../ABI/testing/sysfs-class-iommu-intel-iommu | 32 +
Documentation/ABI/testing/sysfs-class-issei | 73 +
Documentation/ABI/testing/sysfs-class-lcd | 23 +
Documentation/ABI/testing/sysfs-class-led | 89 +
.../ABI/testing/sysfs-class-led-driver-aw200xx | 5 +
.../ABI/testing/sysfs-class-led-driver-lm3533 | 73 +
.../testing/sysfs-class-led-driver-turris-omnia | 28 +
Documentation/ABI/testing/sysfs-class-led-flash | 89 +
.../ABI/testing/sysfs-class-led-multicolor | 37 +
.../ABI/testing/sysfs-class-led-trigger-netdev | 192 +
.../ABI/testing/sysfs-class-led-trigger-oneshot | 36 +
.../ABI/testing/sysfs-class-led-trigger-pattern | 50 +
.../ABI/testing/sysfs-class-led-trigger-tty | 62 +
.../ABI/testing/sysfs-class-led-trigger-usbport | 13 +
Documentation/ABI/testing/sysfs-class-leds-gt683r | 18 +
Documentation/ABI/testing/sysfs-class-mei | 105 +
Documentation/ABI/testing/sysfs-class-mic | 178 +
Documentation/ABI/testing/sysfs-class-mtd | 242 +
Documentation/ABI/testing/sysfs-class-mux | 16 +
Documentation/ABI/testing/sysfs-class-net | 354 +
Documentation/ABI/testing/sysfs-class-net-cdc_ncm | 168 +
Documentation/ABI/testing/sysfs-class-net-dsa | 14 +
Documentation/ABI/testing/sysfs-class-net-grcan | 35 +
.../ABI/testing/sysfs-class-net-janz-ican3 | 19 +
Documentation/ABI/testing/sysfs-class-net-peak_usb | 19 +
Documentation/ABI/testing/sysfs-class-net-phydev | 75 +
Documentation/ABI/testing/sysfs-class-net-qmi | 76 +
Documentation/ABI/testing/sysfs-class-net-queues | 121 +
.../ABI/testing/sysfs-class-net-statistics | 217 +
Documentation/ABI/testing/sysfs-class-ocxl | 51 +
.../ABI/testing/sysfs-class-platform-profile | 50 +
Documentation/ABI/testing/sysfs-class-power | 906 +
.../ABI/testing/sysfs-class-power-bd71828 | 13 +
Documentation/ABI/testing/sysfs-class-power-gaokun | 27 +
.../ABI/testing/sysfs-class-power-ltc4162l | 82 +
.../ABI/testing/sysfs-class-power-max1720x | 32 +
Documentation/ABI/testing/sysfs-class-power-mp2629 | 9 +
Documentation/ABI/testing/sysfs-class-power-rt9467 | 19 +
Documentation/ABI/testing/sysfs-class-power-rt9471 | 32 +
Documentation/ABI/testing/sysfs-class-power-rt9756 | 30 +
.../ABI/testing/sysfs-class-power-surface | 15 +
.../ABI/testing/sysfs-class-power-twl4030 | 33 +
Documentation/ABI/testing/sysfs-class-power-wilco | 40 +
Documentation/ABI/testing/sysfs-class-powercap | 152 +
Documentation/ABI/testing/sysfs-class-pwm | 88 +
Documentation/ABI/testing/sysfs-class-rapidio | 59 +
Documentation/ABI/testing/sysfs-class-rc | 137 +
Documentation/ABI/testing/sysfs-class-rc-nuvoton | 15 +
.../testing/sysfs-class-reboot-mode-reboot_modes | 39 +
Documentation/ABI/testing/sysfs-class-regulator | 453 +
Documentation/ABI/testing/sysfs-class-remoteproc | 104 +
Documentation/ABI/testing/sysfs-class-rnbd-client | 133 +
Documentation/ABI/testing/sysfs-class-rnbd-server | 58 +
Documentation/ABI/testing/sysfs-class-rtc | 91 +
.../sysfs-class-rtc-rtc0-device-rtc_calibration | 14 +
Documentation/ABI/testing/sysfs-class-rtrs-client | 146 +
Documentation/ABI/testing/sysfs-class-rtrs-server | 53 +
Documentation/ABI/testing/sysfs-class-scsi_host | 119 +
Documentation/ABI/testing/sysfs-class-scsi_tape | 109 +
Documentation/ABI/testing/sysfs-class-spi-eeprom | 30 +
Documentation/ABI/testing/sysfs-class-spi-master | 34 +
Documentation/ABI/testing/sysfs-class-stm | 24 +
Documentation/ABI/testing/sysfs-class-stm_source | 11 +
Documentation/ABI/testing/sysfs-class-switchtec | 96 +
Documentation/ABI/testing/sysfs-class-tee | 25 +
Documentation/ABI/testing/sysfs-class-thermal | 259 +
Documentation/ABI/testing/sysfs-class-tsm | 9 +
Documentation/ABI/testing/sysfs-class-typec | 386 +
.../ABI/testing/sysfs-class-usb_power_delivery | 284 +
Documentation/ABI/testing/sysfs-class-usb_role | 27 +
Documentation/ABI/testing/sysfs-class-vduse | 33 +
Documentation/ABI/testing/sysfs-class-wakeup | 76 +
Documentation/ABI/testing/sysfs-class-watchdog | 129 +
Documentation/ABI/testing/sysfs-class-zram | 24 +
Documentation/ABI/testing/sysfs-dev | 21 +
Documentation/ABI/testing/sysfs-devices | 25 +
Documentation/ABI/testing/sysfs-devices-consumer | 9 +
Documentation/ABI/testing/sysfs-devices-coredump | 10 +
Documentation/ABI/testing/sysfs-devices-edac | 157 +
.../ABI/testing/sysfs-devices-faux-tdx-host | 26 +
.../ABI/testing/sysfs-devices-firmware_node | 17 +
Documentation/ABI/testing/sysfs-devices-lpss_ltr | 44 +
Documentation/ABI/testing/sysfs-devices-mapping | 62 +
Documentation/ABI/testing/sysfs-devices-memory | 120 +
Documentation/ABI/testing/sysfs-devices-mmc | 21 +
Documentation/ABI/testing/sysfs-devices-online | 20 +
.../ABI/testing/sysfs-devices-pci-host-bridge | 45 +
.../ABI/testing/sysfs-devices-physical_location | 42 +
.../ABI/testing/sysfs-devices-platform-ACPI-TAD | 117 +
.../testing/sysfs-devices-platform-_UDC_-gadget | 23 +
.../ABI/testing/sysfs-devices-platform-docg3 | 40 +
.../ABI/testing/sysfs-devices-platform-dock | 39 +
.../ABI/testing/sysfs-devices-platform-ipmi | 242 +
.../testing/sysfs-devices-platform-kunpeng_hccs | 126 +
.../sysfs-devices-platform-sh_mobile_lcdc_fb | 44 +
.../ABI/testing/sysfs-devices-platform-soc-ipa | 114 +
.../testing/sysfs-devices-platform-stratix10-rsu | 232 +
.../ABI/testing/sysfs-devices-platform-trackpoint | 115 +
Documentation/ABI/testing/sysfs-devices-power | 307 +
.../ABI/testing/sysfs-devices-power_resources_D0 | 13 +
.../ABI/testing/sysfs-devices-power_resources_D1 | 14 +
.../ABI/testing/sysfs-devices-power_resources_D2 | 14 +
.../testing/sysfs-devices-power_resources_D3hot | 14 +
.../testing/sysfs-devices-power_resources_wakeup | 13 +
.../ABI/testing/sysfs-devices-power_state | 20 +
.../ABI/testing/sysfs-devices-real_power_state | 23 +
Documentation/ABI/testing/sysfs-devices-removable | 20 +
.../ABI/testing/sysfs-devices-resource_in_use | 12 +
Documentation/ABI/testing/sysfs-devices-soc | 95 +
.../ABI/testing/sysfs-devices-software_node | 10 +
.../ABI/testing/sysfs-devices-state_synced | 29 +
Documentation/ABI/testing/sysfs-devices-sun | 14 +
Documentation/ABI/testing/sysfs-devices-supplier | 9 +
Documentation/ABI/testing/sysfs-devices-system-cpu | 808 +
.../ABI/testing/sysfs-devices-system-ibm-rtl | 24 +
.../ABI/testing/sysfs-devices-system-xen_cpu | 20 +
Documentation/ABI/testing/sysfs-devices-vfio-dev | 8 +
.../testing/sysfs-devices-virtual-misc-tdx_guest | 63 +
.../ABI/testing/sysfs-devices-waiting_for_supplier | 17 +
Documentation/ABI/testing/sysfs-devices-xenbus | 41 +
Documentation/ABI/testing/sysfs-driver-altera-cvp | 8 +
Documentation/ABI/testing/sysfs-driver-amd-sfh | 13 +
.../ABI/testing/sysfs-driver-aspeed-uart-routing | 27 +
.../ABI/testing/sysfs-driver-bd9571mwv-regulator | 31 +
Documentation/ABI/testing/sysfs-driver-ccp | 120 +
.../ABI/testing/sysfs-driver-chromeos-acpi | 152 +
.../ABI/testing/sysfs-driver-dell-dw5826e-reset | 9 +
Documentation/ABI/testing/sysfs-driver-eud | 9 +
.../ABI/testing/sysfs-driver-framer-pef2256 | 8 +
.../ABI/testing/sysfs-driver-fsi-master-gpio | 10 +
Documentation/ABI/testing/sysfs-driver-ge-achc | 15 +
Documentation/ABI/testing/sysfs-driver-genwqe | 78 +
Documentation/ABI/testing/sysfs-driver-habanalabs | 265 +
Documentation/ABI/testing/sysfs-driver-hid | 20 +
.../ABI/testing/sysfs-driver-hid-appletb-kbd | 13 +
Documentation/ABI/testing/sysfs-driver-hid-corsair | 15 +
.../ABI/testing/sysfs-driver-hid-corsair-void | 38 +
Documentation/ABI/testing/sysfs-driver-hid-lenovo | 60 +
.../ABI/testing/sysfs-driver-hid-lenovo-go | 724 +
.../ABI/testing/sysfs-driver-hid-lenovo-go-s | 304 +
.../ABI/testing/sysfs-driver-hid-logitech-hidpp | 19 +
.../ABI/testing/sysfs-driver-hid-logitech-lg4ff | 75 +
.../ABI/testing/sysfs-driver-hid-multitouch | 9 +
Documentation/ABI/testing/sysfs-driver-hid-ntrig | 73 +
Documentation/ABI/testing/sysfs-driver-hid-picolcd | 43 +
.../ABI/testing/sysfs-driver-hid-prodikeys | 29 +
.../ABI/testing/sysfs-driver-hid-roccat-kone | 125 +
Documentation/ABI/testing/sysfs-driver-hid-srws1 | 21 +
Documentation/ABI/testing/sysfs-driver-hid-wiimote | 85 +
.../ABI/testing/sysfs-driver-input-axp-pek | 11 +
.../ABI/testing/sysfs-driver-input-cros-ec-keyb | 6 +
.../ABI/testing/sysfs-driver-input-exc3000 | 26 +
Documentation/ABI/testing/sysfs-driver-intc_sar | 54 +
.../ABI/testing/sysfs-driver-intel-i915-hwmon | 93 +
.../ABI/testing/sysfs-driver-intel-m10-bmc | 36 +
.../testing/sysfs-driver-intel-m10-bmc-sec-update | 61 +
.../ABI/testing/sysfs-driver-intel-rapid-start | 21 +
.../ABI/testing/sysfs-driver-intel-xe-hwmon | 315 +
.../ABI/testing/sysfs-driver-intel-xe-ras | 30 +
.../ABI/testing/sysfs-driver-intel-xe-sriov | 190 +
Documentation/ABI/testing/sysfs-driver-intel_sdsi | 90 +
Documentation/ABI/testing/sysfs-driver-ivpu | 65 +
.../ABI/testing/sysfs-driver-jz4780-efuse | 20 +
.../ABI/testing/sysfs-driver-lenovo-yb9-kbdock | 19 +
.../ABI/testing/sysfs-driver-panfrost-profiling | 10 +
.../ABI/testing/sysfs-driver-panthor-profiling | 10 +
Documentation/ABI/testing/sysfs-driver-pciback | 28 +
Documentation/ABI/testing/sysfs-driver-ppi | 75 +
Documentation/ABI/testing/sysfs-driver-qaic | 18 +
Documentation/ABI/testing/sysfs-driver-qat | 167 +
Documentation/ABI/testing/sysfs-driver-qat_kpt | 97 +
Documentation/ABI/testing/sysfs-driver-qat_ras | 41 +
Documentation/ABI/testing/sysfs-driver-qat_rl | 226 +
Documentation/ABI/testing/sysfs-driver-qat_svn | 114 +
.../ABI/testing/sysfs-driver-samsung-laptop | 37 +
Documentation/ABI/testing/sysfs-driver-spi-intel | 20 +
Documentation/ABI/testing/sysfs-driver-st | 12 +
Documentation/ABI/testing/sysfs-driver-tegra-fuse | 11 +
.../ABI/testing/sysfs-driver-toshiba_acpi | 223 +
.../ABI/testing/sysfs-driver-toshiba_haps | 22 +
.../ABI/testing/sysfs-driver-typec-displayport | 74 +
Documentation/ABI/testing/sysfs-driver-uacce | 57 +
Documentation/ABI/testing/sysfs-driver-ucsi-ccg | 6 +
Documentation/ABI/testing/sysfs-driver-ufs | 1793 +
.../ABI/testing/sysfs-driver-uio_pci_sva-pasid | 29 +
.../ABI/testing/sysfs-driver-uniwill-laptop | 105 +
Documentation/ABI/testing/sysfs-driver-w1_ds28e17 | 24 +
Documentation/ABI/testing/sysfs-driver-w1_therm | 190 +
Documentation/ABI/testing/sysfs-driver-wacom | 105 +
Documentation/ABI/testing/sysfs-driver-xdata | 49 +
Documentation/ABI/testing/sysfs-driver-xen-blkback | 46 +
.../ABI/testing/sysfs-driver-xen-blkfront | 19 +
.../ABI/testing/sysfs-driver-xilinx-tmr-manager | 16 +
Documentation/ABI/testing/sysfs-driver-zynqmp-fpga | 73 +
Documentation/ABI/testing/sysfs-edac-ecs | 74 +
Documentation/ABI/testing/sysfs-edac-memory-repair | 206 +
Documentation/ABI/testing/sysfs-edac-scrub | 85 +
Documentation/ABI/testing/sysfs-firmware-acpi | 277 +
.../ABI/testing/sysfs-firmware-dmi-entries | 114 +
.../ABI/testing/sysfs-firmware-dmi-tables | 22 +
Documentation/ABI/testing/sysfs-firmware-efi | 45 +
Documentation/ABI/testing/sysfs-firmware-efi-esrt | 87 +
.../ABI/testing/sysfs-firmware-efi-runtime-map | 36 +
Documentation/ABI/testing/sysfs-firmware-gsmi | 58 +
Documentation/ABI/testing/sysfs-firmware-initrd | 8 +
.../ABI/testing/sysfs-firmware-lefi-boardinfo | 35 +
Documentation/ABI/testing/sysfs-firmware-log | 7 +
Documentation/ABI/testing/sysfs-firmware-memmap | 75 +
Documentation/ABI/testing/sysfs-firmware-ofw | 52 +
.../ABI/testing/sysfs-firmware-opal-powercap | 31 +
Documentation/ABI/testing/sysfs-firmware-opal-psr | 18 +
.../ABI/testing/sysfs-firmware-opal-sensor-groups | 21 +
.../testing/sysfs-firmware-papr-energy-scale-info | 29 +
Documentation/ABI/testing/sysfs-firmware-plpks | 58 +
.../ABI/testing/sysfs-firmware-qemu_fw_cfg | 103 +
.../ABI/testing/sysfs-firmware-sev-vulnerabilities | 19 +
Documentation/ABI/testing/sysfs-firmware-sgi_uv | 159 +
.../ABI/testing/sysfs-firmware-turris-mox-rwtm | 28 +
Documentation/ABI/testing/sysfs-fs-erofs | 49 +
Documentation/ABI/testing/sysfs-fs-ext4 | 118 +
Documentation/ABI/testing/sysfs-fs-f2fs | 1022 +
Documentation/ABI/testing/sysfs-fs-nilfs2 | 269 +
Documentation/ABI/testing/sysfs-fs-ubifs | 35 +
Documentation/ABI/testing/sysfs-fs-virtiofs | 11 +
Documentation/ABI/testing/sysfs-fs-xfs | 37 +
Documentation/ABI/testing/sysfs-hypervisor-xen | 46 +
Documentation/ABI/testing/sysfs-ibft | 33 +
.../ABI/testing/sysfs-kernel-address_bits | 10 +
Documentation/ABI/testing/sysfs-kernel-boot_params | 39 +
Documentation/ABI/testing/sysfs-kernel-btf | 25 +
.../ABI/testing/sysfs-kernel-cpu_byteorder | 12 +
Documentation/ABI/testing/sysfs-kernel-fadump | 59 +
Documentation/ABI/testing/sysfs-kernel-fscaps | 8 +
.../ABI/testing/sysfs-kernel-hardlockup_count | 7 +
.../ABI/testing/sysfs-kernel-iommu_groups | 64 +
Documentation/ABI/testing/sysfs-kernel-irq | 60 +
Documentation/ABI/testing/sysfs-kernel-kexec-kdump | 61 +
Documentation/ABI/testing/sysfs-kernel-livepatch | 95 +
Documentation/ABI/testing/sysfs-kernel-mm | 6 +
Documentation/ABI/testing/sysfs-kernel-mm-cma | 44 +
Documentation/ABI/testing/sysfs-kernel-mm-damon | 664 +
.../ABI/testing/sysfs-kernel-mm-hugepages | 17 +
Documentation/ABI/testing/sysfs-kernel-mm-ksm | 61 +
.../ABI/testing/sysfs-kernel-mm-memory-tiers | 25 +
.../ABI/testing/sysfs-kernel-mm-mempolicy | 4 +
.../sysfs-kernel-mm-mempolicy-weighted-interleave | 54 +
Documentation/ABI/testing/sysfs-kernel-mm-numa | 28 +
Documentation/ABI/testing/sysfs-kernel-mm-swap | 16 +
.../testing/sysfs-kernel-mm-transparent-hugepage | 18 +
Documentation/ABI/testing/sysfs-kernel-oops_count | 6 +
.../ABI/testing/sysfs-kernel-rcu_stall_count | 6 +
Documentation/ABI/testing/sysfs-kernel-reboot | 40 +
Documentation/ABI/testing/sysfs-kernel-slab | 526 +
.../ABI/testing/sysfs-kernel-softlockup_count | 7 +
Documentation/ABI/testing/sysfs-kernel-vmcoreinfo | 14 +
Documentation/ABI/testing/sysfs-kernel-warn_count | 6 +
Documentation/ABI/testing/sysfs-mce | 97 +
.../ABI/testing/sysfs-memory-page-offline | 44 +
Documentation/ABI/testing/sysfs-module | 84 +
Documentation/ABI/testing/sysfs-nvmem-cells | 21 +
Documentation/ABI/testing/sysfs-ocfs2 | 89 +
.../ABI/testing/sysfs-platform-alienware-wmi | 14 +
.../ABI/testing/sysfs-platform-asus-laptop | 71 +
Documentation/ABI/testing/sysfs-platform-asus-wmi | 240 +
Documentation/ABI/testing/sysfs-platform-at91 | 27 +
Documentation/ABI/testing/sysfs-platform-ayaneo-ec | 19 +
.../ABI/testing/sysfs-platform-brcmstb-gisb-arb | 8 +
.../ABI/testing/sysfs-platform-brcmstb-memc | 15 +
.../ABI/testing/sysfs-platform-chipidea-usb-otg | 56 +
.../ABI/testing/sysfs-platform-chipidea-usb2 | 9 +
.../ABI/testing/sysfs-platform-dell-laptop | 73 +
.../ABI/testing/sysfs-platform-dell-privacy-wmi | 71 +
.../ABI/testing/sysfs-platform-dell-smbios | 21 +
.../ABI/testing/sysfs-platform-dell-wmi-ddv | 9 +
Documentation/ABI/testing/sysfs-platform-dfl-fme | 246 +
Documentation/ABI/testing/sysfs-platform-dfl-port | 101 +
Documentation/ABI/testing/sysfs-platform-dptf | 157 +
.../ABI/testing/sysfs-platform-eeepc-laptop | 56 +
Documentation/ABI/testing/sysfs-platform-hidma | 9 +
.../ABI/testing/sysfs-platform-hidma-mgmt | 97 +
.../ABI/testing/sysfs-platform-i2c-demux-pinctrl | 18 +
.../ABI/testing/sysfs-platform-ideapad-laptop | 52 +
Documentation/ABI/testing/sysfs-platform-intel-ifs | 52 +
Documentation/ABI/testing/sysfs-platform-intel-pmc | 22 +
.../testing/sysfs-platform-intel-wmi-sbl-fw-update | 13 +
.../testing/sysfs-platform-intel-wmi-thunderbolt | 12 +
Documentation/ABI/testing/sysfs-platform-kim | 49 +
Documentation/ABI/testing/sysfs-platform-lg-laptop | 36 +
.../ABI/testing/sysfs-platform-mellanox-bootctl | 162 +
.../ABI/testing/sysfs-platform-mellanox-pmc | 64 +
.../ABI/testing/sysfs-platform-msi-laptop | 83 +
Documentation/ABI/testing/sysfs-platform-oxp | 25 +
.../ABI/testing/sysfs-platform-phy-rcar-gen3-usb2 | 17 +
.../ABI/testing/sysfs-platform-power-on-reason | 12 +
.../ABI/testing/sysfs-platform-renesas_usb3 | 17 +
Documentation/ABI/testing/sysfs-platform-silicom | 30 +
Documentation/ABI/testing/sysfs-platform-sst-atom | 26 +
Documentation/ABI/testing/sysfs-platform-tahvo-usb | 16 +
Documentation/ABI/testing/sysfs-platform-ts5500 | 54 +
.../ABI/testing/sysfs-platform-twl4030-usb | 8 +
.../ABI/testing/sysfs-platform-usbip-vudc | 38 +
Documentation/ABI/testing/sysfs-platform-wilco-ec | 58 +
Documentation/ABI/testing/sysfs-platform_profile | 40 +
Documentation/ABI/testing/sysfs-power | 472 +
Documentation/ABI/testing/sysfs-pps | 73 +
Documentation/ABI/testing/sysfs-pps-gen | 43 +
Documentation/ABI/testing/sysfs-pps-gen-tio | 6 +
Documentation/ABI/testing/sysfs-profiling | 13 +
Documentation/ABI/testing/sysfs-ptp | 142 +
Documentation/ABI/testing/sysfs-secvar | 65 +
Documentation/ABI/testing/sysfs-timecard | 293 +
Documentation/ABI/testing/sysfs-tty | 166 +
Documentation/ABI/testing/sysfs-uevent | 51 +
Documentation/ABI/testing/usb-charger-uevent | 54 +
Documentation/ABI/testing/usb-uevent | 27 +
Documentation/Changes | 1 +
Documentation/CodingStyle | 1 +
Documentation/Kconfig | 28 +
Documentation/Makefile | 115 +
Documentation/PCI/acpi-info.rst | 192 +
Documentation/PCI/boot-interrupts.rst | 159 +
Documentation/PCI/controller/index.rst | 11 +
.../PCI/controller/pci-controller-drivers.rst | 402 +
.../PCI/controller/rcar-pcie-firmware.rst | 32 +
.../PCI/endpoint/function/binding/pci-ntb.rst | 38 +
.../PCI/endpoint/function/binding/pci-test.rst | 26 +
Documentation/PCI/endpoint/index.rst | 21 +
Documentation/PCI/endpoint/pci-endpoint-cfs.rst | 138 +
Documentation/PCI/endpoint/pci-endpoint.rst | 283 +
Documentation/PCI/endpoint/pci-ntb-function.rst | 348 +
Documentation/PCI/endpoint/pci-ntb-howto.rst | 158 +
Documentation/PCI/endpoint/pci-nvme-function.rst | 13 +
Documentation/PCI/endpoint/pci-test-function.rst | 103 +
Documentation/PCI/endpoint/pci-test-howto.rst | 239 +
Documentation/PCI/endpoint/pci-vntb-function.rst | 129 +
Documentation/PCI/endpoint/pci-vntb-howto.rst | 169 +
Documentation/PCI/index.rst | 22 +
Documentation/PCI/msi-howto.rst | 300 +
Documentation/PCI/pci-error-recovery.rst | 471 +
Documentation/PCI/pci-iov-howto.rst | 171 +
Documentation/PCI/pci.rst | 578 +
Documentation/PCI/pcieaer-howto.rst | 267 +
Documentation/PCI/pciebus-howto.rst | 228 +
Documentation/PCI/sysfs-pci.rst | 138 +
Documentation/PCI/tph.rst | 132 +
.../Design/Data-Structures/BigTreeClassicRCU.svg | 474 +
.../BigTreePreemptRCUBHdyntickCB.svg | 662 +
.../RCU/Design/Data-Structures/Data-Structures.rst | 1196 +
.../Design/Data-Structures/HugeTreeClassicRCU.svg | 939 +
.../RCU/Design/Data-Structures/TreeLevel.svg | 828 +
.../RCU/Design/Data-Structures/TreeMapping.svg | 305 +
.../Design/Data-Structures/TreeMappingLevel.svg | 380 +
.../RCU/Design/Data-Structures/blkd_task.svg | 631 +
.../RCU/Design/Data-Structures/nxtlist.svg | 386 +
.../Design/Expedited-Grace-Periods/ExpRCUFlow.svg | 830 +
.../Expedited-Grace-Periods/ExpSchedFlow.svg | 830 +
.../Expedited-Grace-Periods.rst | 521 +
.../RCU/Design/Expedited-Grace-Periods/Funnel0.svg | 275 +
.../RCU/Design/Expedited-Grace-Periods/Funnel1.svg | 275 +
.../RCU/Design/Expedited-Grace-Periods/Funnel2.svg | 287 +
.../RCU/Design/Expedited-Grace-Periods/Funnel3.svg | 323 +
.../RCU/Design/Expedited-Grace-Periods/Funnel4.svg | 323 +
.../RCU/Design/Expedited-Grace-Periods/Funnel5.svg | 335 +
.../RCU/Design/Expedited-Grace-Periods/Funnel6.svg | 335 +
.../RCU/Design/Expedited-Grace-Periods/Funnel7.svg | 347 +
.../RCU/Design/Expedited-Grace-Periods/Funnel8.svg | 311 +
.../Memory-Ordering/Tree-RCU-Memory-Ordering.rst | 648 +
.../TreeRCU-callback-invocation.svg | 486 +
.../Memory-Ordering/TreeRCU-callback-registry.svg | 646 +
.../RCU/Design/Memory-Ordering/TreeRCU-dyntick.svg | 700 +
.../Design/Memory-Ordering/TreeRCU-gp-cleanup.svg | 1133 +
.../RCU/Design/Memory-Ordering/TreeRCU-gp-fqs.svg | 1309 +
.../Design/Memory-Ordering/TreeRCU-gp-init-1.svg | 658 +
.../Design/Memory-Ordering/TreeRCU-gp-init-2.svg | 656 +
.../Design/Memory-Ordering/TreeRCU-gp-init-3.svg | 636 +
.../RCU/Design/Memory-Ordering/TreeRCU-gp.svg | 5135 +
.../RCU/Design/Memory-Ordering/TreeRCU-hotplug.svg | 775 +
.../RCU/Design/Memory-Ordering/TreeRCU-qs.svg | 1095 +
.../RCU/Design/Memory-Ordering/rcu_node-lock.svg | 229 +
.../Design/Requirements/GPpartitionReaders1.svg | 374 +
.../Design/Requirements/ReadersPartitionGP1.svg | 639 +
.../RCU/Design/Requirements/Requirements.rst | 2873 +
Documentation/RCU/NMI-RCU.rst | 123 +
Documentation/RCU/RTFP.txt | 2812 +
Documentation/RCU/UP.rst | 152 +
Documentation/RCU/checklist.rst | 556 +
Documentation/RCU/index.rst | 30 +
Documentation/RCU/listRCU.rst | 511 +
Documentation/RCU/lockdep-splat.rst | 115 +
Documentation/RCU/lockdep.rst | 119 +
Documentation/RCU/rcu.rst | 93 +
Documentation/RCU/rcu_dereference.rst | 502 +
Documentation/RCU/rcubarrier.rst | 377 +
Documentation/RCU/rculist_nulls.rst | 215 +
Documentation/RCU/rcuref.rst | 158 +
Documentation/RCU/stallwarn.rst | 491 +
Documentation/RCU/torture.rst | 374 +
Documentation/RCU/whatisRCU.rst | 1415 +
Documentation/SubmittingPatches | 1 +
Documentation/accel/amdxdna/amdnpu.rst | 306 +
Documentation/accel/amdxdna/index.rst | 11 +
Documentation/accel/index.rst | 13 +
Documentation/accel/introduction.rst | 110 +
Documentation/accel/qaic/aic080.rst | 14 +
Documentation/accel/qaic/aic100.rst | 538 +
Documentation/accel/qaic/index.rst | 14 +
Documentation/accel/qaic/qaic.rst | 209 +
Documentation/accel/rocket/index.rst | 19 +
Documentation/accounting/cgroupstats.rst | 31 +
Documentation/accounting/delay-accounting.rst | 214 +
Documentation/accounting/index.rst | 14 +
Documentation/accounting/psi.rst | 188 +
Documentation/accounting/taskstats-struct.rst | 199 +
Documentation/accounting/taskstats.rst | 182 +
Documentation/admin-guide/LSM/LoadPin.rst | 31 +
Documentation/admin-guide/LSM/SELinux.rst | 44 +
Documentation/admin-guide/LSM/SafeSetID.rst | 118 +
Documentation/admin-guide/LSM/Smack.rst | 871 +
Documentation/admin-guide/LSM/Yama.rst | 75 +
Documentation/admin-guide/LSM/apparmor.rst | 54 +
Documentation/admin-guide/LSM/index.rst | 51 +
Documentation/admin-guide/LSM/ipe.rst | 835 +
Documentation/admin-guide/LSM/landlock.rst | 298 +
Documentation/admin-guide/LSM/tomoyo.rst | 52 +
.../admin-guide/RAS/address-translation.rst | 24 +
Documentation/admin-guide/RAS/error-decoding.rst | 21 +
Documentation/admin-guide/RAS/index.rst | 7 +
Documentation/admin-guide/RAS/main.rst | 1087 +
Documentation/admin-guide/README.rst | 360 +
Documentation/admin-guide/abi-obsolete-files.rst | 7 +
Documentation/admin-guide/abi-obsolete.rst | 13 +
Documentation/admin-guide/abi-removed-files.rst | 7 +
Documentation/admin-guide/abi-removed.rst | 7 +
Documentation/admin-guide/abi-stable-files.rst | 7 +
Documentation/admin-guide/abi-stable.rst | 16 +
Documentation/admin-guide/abi-testing-files.rst | 7 +
Documentation/admin-guide/abi-testing.rst | 22 +
Documentation/admin-guide/abi.rst | 29 +
Documentation/admin-guide/acpi/cppc_sysfs.rst | 78 +
.../admin-guide/acpi/fan_performance_states.rst | 90 +
Documentation/admin-guide/acpi/index.rst | 14 +
.../admin-guide/acpi/initrd_table_override.rst | 115 +
Documentation/admin-guide/acpi/ssdt-overlays.rst | 181 +
Documentation/admin-guide/aoe/aoe.rst | 150 +
Documentation/admin-guide/aoe/autoload.sh | 17 +
Documentation/admin-guide/aoe/examples.rst | 23 +
Documentation/admin-guide/aoe/index.rst | 10 +
Documentation/admin-guide/aoe/status.sh | 30 +
Documentation/admin-guide/aoe/todo.rst | 17 +
Documentation/admin-guide/aoe/udev-install.sh | 33 +
Documentation/admin-guide/aoe/udev.txt | 26 +
.../admin-guide/auxdisplay/cfag12864b.rst | 98 +
Documentation/admin-guide/auxdisplay/index.rst | 9 +
Documentation/admin-guide/auxdisplay/ks0108.rst | 50 +
Documentation/admin-guide/bcache.rst | 644 +
Documentation/admin-guide/binderfs.rst | 87 +
Documentation/admin-guide/binfmt-misc.rst | 391 +
.../blockdev/drbd/DRBD-8.3-data-packets.svg | 588 +
.../blockdev/drbd/DRBD-data-packets.svg | 459 +
.../admin-guide/blockdev/drbd/conn-states-8.dot | 18 +
.../blockdev/drbd/data-structure-v9.rst | 42 +
.../admin-guide/blockdev/drbd/disk-states-8.dot | 16 +
.../drbd/drbd-connection-state-overview.dot | 85 +
.../admin-guide/blockdev/drbd/figures.rst | 30 +
Documentation/admin-guide/blockdev/drbd/index.rst | 19 +
.../admin-guide/blockdev/drbd/peer-states-8.dot | 8 +
Documentation/admin-guide/blockdev/floppy.rst | 255 +
Documentation/admin-guide/blockdev/index.rst | 17 +
Documentation/admin-guide/blockdev/nbd.rst | 31 +
Documentation/admin-guide/blockdev/paride.rst | 207 +
Documentation/admin-guide/blockdev/ramdisk.rst | 153 +
Documentation/admin-guide/blockdev/zoned_loop.rst | 191 +
Documentation/admin-guide/blockdev/zram.rst | 610 +
Documentation/admin-guide/bootconfig.rst | 416 +
Documentation/admin-guide/braille-console.rst | 38 +
Documentation/admin-guide/btmrvl.rst | 124 +
Documentation/admin-guide/bug-bisect.rst | 165 +
Documentation/admin-guide/bug-hunting.rst | 370 +
.../admin-guide/cgroup-v1/blkio-controller.rst | 301 +
Documentation/admin-guide/cgroup-v1/cgroups.rst | 697 +
Documentation/admin-guide/cgroup-v1/cpuacct.rst | 50 +
Documentation/admin-guide/cgroup-v1/cpusets.rst | 874 +
Documentation/admin-guide/cgroup-v1/devices.rst | 132 +
.../admin-guide/cgroup-v1/freezer-subsystem.rst | 131 +
Documentation/admin-guide/cgroup-v1/hugetlb.rst | 139 +
Documentation/admin-guide/cgroup-v1/index.rst | 24 +
Documentation/admin-guide/cgroup-v1/memcg_test.rst | 342 +
Documentation/admin-guide/cgroup-v1/memory.rst | 926 +
Documentation/admin-guide/cgroup-v1/misc.rst | 4 +
Documentation/admin-guide/cgroup-v1/net_cls.rst | 44 +
Documentation/admin-guide/cgroup-v1/net_prio.rst | 57 +
Documentation/admin-guide/cgroup-v1/pids.rst | 93 +
Documentation/admin-guide/cgroup-v1/rdma.rst | 191 +
Documentation/admin-guide/cgroup-v2.rst | 3581 +
Documentation/admin-guide/cifs/authors.rst | 69 +
Documentation/admin-guide/cifs/changes.rst | 9 +
Documentation/admin-guide/cifs/index.rst | 14 +
Documentation/admin-guide/cifs/introduction.rst | 53 +
Documentation/admin-guide/cifs/todo.rst | 133 +
Documentation/admin-guide/cifs/usage.rst | 864 +
Documentation/admin-guide/cifs/winucase_convert.pl | 62 +
Documentation/admin-guide/clearing-warn-once.rst | 9 +
Documentation/admin-guide/cpu-isolation.rst | 357 +
Documentation/admin-guide/cpu-load.rst | 117 +
Documentation/admin-guide/cputopology.rst | 101 +
Documentation/admin-guide/dell_rbu.rst | 128 +
.../admin-guide/device-mapper/cache-policies.rst | 131 +
Documentation/admin-guide/device-mapper/cache.rst | 337 +
Documentation/admin-guide/device-mapper/delay.rst | 54 +
.../admin-guide/device-mapper/dm-clone.rst | 333 +
.../admin-guide/device-mapper/dm-crypt.rst | 212 +
.../admin-guide/device-mapper/dm-dust.rst | 305 +
Documentation/admin-guide/device-mapper/dm-ebs.rst | 51 +
.../admin-guide/device-mapper/dm-flakey.rst | 88 +
Documentation/admin-guide/device-mapper/dm-ima.rst | 718 +
.../admin-guide/device-mapper/dm-init.rst | 133 +
.../admin-guide/device-mapper/dm-inlinecrypt.rst | 129 +
.../admin-guide/device-mapper/dm-integrity.rst | 321 +
Documentation/admin-guide/device-mapper/dm-io.rst | 75 +
Documentation/admin-guide/device-mapper/dm-log.rst | 57 +
.../admin-guide/device-mapper/dm-pcache.rst | 202 +
.../admin-guide/device-mapper/dm-queue-length.rst | 48 +
.../admin-guide/device-mapper/dm-raid.rst | 489 +
.../admin-guide/device-mapper/dm-service-time.rst | 101 +
.../admin-guide/device-mapper/dm-uevent.rst | 110 +
.../admin-guide/device-mapper/dm-zoned.rst | 194 +
Documentation/admin-guide/device-mapper/era.rst | 116 +
Documentation/admin-guide/device-mapper/index.rst | 43 +
Documentation/admin-guide/device-mapper/kcopyd.rst | 47 +
Documentation/admin-guide/device-mapper/linear.rst | 63 +
.../admin-guide/device-mapper/log-writes.rst | 145 +
.../admin-guide/device-mapper/persistent-data.rst | 88 +
.../admin-guide/device-mapper/snapshot.rst | 196 +
.../admin-guide/device-mapper/statistics.rst | 225 +
.../admin-guide/device-mapper/striped.rst | 61 +
Documentation/admin-guide/device-mapper/switch.rst | 141 +
.../device-mapper/thin-provisioning.rst | 427 +
.../admin-guide/device-mapper/unstriped.rst | 135 +
.../admin-guide/device-mapper/vdo-design.rst | 633 +
Documentation/admin-guide/device-mapper/vdo.rst | 413 +
Documentation/admin-guide/device-mapper/verity.rst | 349 +
.../admin-guide/device-mapper/writecache.rst | 114 +
Documentation/admin-guide/device-mapper/zero.rst | 37 +
Documentation/admin-guide/devices.rst | 270 +
Documentation/admin-guide/devices.txt | 3081 +
Documentation/admin-guide/dynamic-debug-howto.rst | 392 +
Documentation/admin-guide/edid.rst | 27 +
Documentation/admin-guide/efi-stub.rst | 103 +
Documentation/admin-guide/ext4.rst | 611 +
Documentation/admin-guide/features.rst | 3 +
.../admin-guide/filesystem-monitoring.rst | 78 +
Documentation/admin-guide/gpio/gpio-aggregator.rst | 218 +
Documentation/admin-guide/gpio/gpio-mockup.rst | 59 +
Documentation/admin-guide/gpio/gpio-sim.rst | 137 +
Documentation/admin-guide/gpio/gpio-virtuser.rst | 177 +
Documentation/admin-guide/gpio/index.rst | 14 +
Documentation/admin-guide/gpio/obsolete.rst | 13 +
.../admin-guide/hw-vuln/attack_vector_controls.rst | 236 +
.../admin-guide/hw-vuln/core-scheduling.rst | 226 +
.../admin-guide/hw-vuln/cross-thread-rsb.rst | 91 +
.../admin-guide/hw-vuln/gather_data_sampling.rst | 109 +
Documentation/admin-guide/hw-vuln/index.rst | 29 +
.../hw-vuln/indirect-target-selection.rst | 168 +
Documentation/admin-guide/hw-vuln/l1d_flush.rst | 69 +
Documentation/admin-guide/hw-vuln/l1tf.rst | 615 +
Documentation/admin-guide/hw-vuln/mds.rst | 303 +
Documentation/admin-guide/hw-vuln/multihit.rst | 167 +
.../admin-guide/hw-vuln/old_microcode.rst | 21 +
.../hw-vuln/processor_mmio_stale_data.rst | 269 +
.../admin-guide/hw-vuln/reg-file-data-sampling.rst | 96 +
Documentation/admin-guide/hw-vuln/rsb.rst | 268 +
[Output truncated: showing 1000 of 96068 lines]]
|
| 92/2 |
2026/09/15 03:21 |
llm |
expert |
0m
Model:
Tokens:
input: 105175
output: 45
thoughts: 53
Thoughts: **Analyzing the Commit and Potential Impacts**
Okay, so `704340f1cd0d`... that's a merge commit. Nothing immediately insightful there, just a junction point. My next move is to dig deeper. I'm going to check the `git log -p` of `drivers/block/ublk_drv.c` and specifically search for `ublk_try_buf_match`. That function likely represents a critical piece of the ublk driver's buffer management logic, and any changes around that function within the context of the merge are what I need to understand. I need to see what's changed, the context, and any potential side effects this merge might have on ublk's buffer handling. This approach should help me quickly identify the significance of the merge, any bugfixes, or potentially new functionality related to ublk's IO handling.
|
| 93/2 |
2026/09/15 03:22 |
tool |
git-log |
0m
Args: map[CodeRegexp:ublk_try_buf_match Count:1 PathPrefix:drivers/block/ublk_drv.c]
Results: map[Output:704340f1cd0d Merge tag 'x86_urgent_for_7.3-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
]
|
| 94/2 |
2026/09/15 03:22 |
llm |
expert-compressor |
0m
Model:
Reply: ### Investigation Summary
#### 1. Bug Report & Trace Analysis
- **Warning**: `WARNING: suspicious RCU usage` at `lib/maple_tree.c:720` (`mas_root()`).
- **Call Trace**:
```
mas_root lib/maple_tree.c:720 [inline]
mas_start+0x3cc/0x640 lib/maple_tree.c:1250
mas_state_walk lib/maple_tree.c:3006 [inline]
mas_walk+0x1db/0x490 lib/maple_tree.c:4458
ublk_try_buf_match drivers/block/ublk_drv.c:5639 [inline]
ublk_init_iod+0x5d2/0x1120 drivers/block/ublk_drv.c:521
ublk_dispatch_req+0x97/0x9c0 drivers/block/ublk_drv.c:1776
tctx_task_work_run+0x73d/0xa80 io_uring/tw.c:96
tctx_task_work+0x3f/0x60 io_uring/tw.c:133
```
- **Context**: In `ublk_try_buf_match()`:
```c
MA_STATE(mas, &ub->buf_tree, pfn, pfn);
range = mas_walk(&mas);
```
`mas_walk(&mas)` expects either RCU read lock (`rcu_read_lock()`) or the maple tree lock (`mt_locked()`) to be held. Neither is held during task work execution (`tctx_task_work_run`), which triggers `lockdep_rcu_suspicious()`.
#### 2. Approaches Tried & Findings
- **Examined `ublk_try_buf_match()`**:
- Located at `drivers/block/ublk_drv.c:5621`.
- Walks `ub->buf_tree` per `bvec` in a `struct request *rq`.
- Called from `ublk_init_iod()` (line 521), which is called from `ublk_setup_iod()` (line 1536), called from `ublk_dispatch_req()` (line 1776).
- **Examined tree writers & synchronization**:
- Looked at all references to `ublk_device::buf_tree`:
- `ublk_ctrl_add_dev()`: initializes with `mt_init(&ub->buf_tree)`.
- `__ublk_ctrl_reg_buf()` / `ublk_buf_erase_ranges()`: called under `ublk_lock_buf_tree(ub)`.
- `__ublk_shmem_remove_ranges()`: called under `ublk_lock_buf_tree(ub)` or `ublk_buf_cleanup()`.
- `ublk_buf_cleanup()`: destroys tree via `mtree_destroy()`, called in `ublk_cdev_rel()` upon device destruction.
- Checked `ublk_lock_buf_tree(ub)`:
```c
mutex_lock(&ub->mutex);
if (ub->ub_disk)
memflags = blk_mq_freeze_queue(ub->ub_disk->queue);
return memflags;
```
- Analyzed synchronization mechanism:
- Modifications to `buf_tree` while device is started acquire `ub->mutex` and freeze the request queue (`blk_mq_freeze_queue`).
- `blk_mq_freeze_queue` waits for `q->q_usage_counter` to drop to 0 (waits for all in-flight block requests).
- `ublk_try_buf_match` runs during the dispatching of an in-flight block request (queued via `ublk_queue_rq()` -> `io_uring_cmd_complete_in_task()` -> `ublk_cmd_tw_cb` -> `ublk_dispatch_req`).
- While `ublk_try_buf_match` is running, `q_usage_counter` is non-zero. Hence, `blk_mq_freeze_queue` in `ublk_lock_buf_tree()` will block until the request completes.
- If `ub->ub_disk` is NULL (device not yet started or already stopped), no block requests can be in flight to execute `ublk_try_buf_match()`.
- When device stops (`ublk_stop_dev_unlocked`), `del_gendisk` is called, which also freezes the queue and drains in-flight requests before `ublk_detach_disk` sets `ub->ub_disk = NULL`.
- `ublk_cdev_rel()` only runs when all references to `ub` (including char device and block device) are released.
- **Result**: `ublk_try_buf_match()` cannot execute concurrently with any write modifications to `ub->buf_tree`. The lockdep warning is a false positive caused by missing an explicit `rcu_read_lock()` / maple tree lock wrapper, even though access is serialized via `blk_mq_freeze_queue`.
- **Examined Accessibility & Privilege**:
- `ublk_ctrl_add_dev`:
- Checks `capable(CAP_SYS_ADMIN)`:
```c
if (capable(CAP_SYS_ADMIN))
info.flags &= ~UBLK_F_UNPRIVILEGED_DEV;
else if (!(info.flags & UBLK_F_UNPRIVILEGED_DEV))
return -EPERM;
```
- For unprivileged device (`info.flags & UBLK_F_UNPRIVILEGED_DEV`):
`UBLK_F_USER_COPY`, `UBLK_F_SUPPORT_ZERO_COPY`, `UBLK_F_AUTO_BUF_REG` are disallowed (`return -EINVAL`).
- However, `UBLK_F_SHMEM_ZC` (`1ULL << 19`) is *not* forbidden for `UBLK_F_UNPRIVILEGED_DEV`.
- If `/dev/ublk-control` is accessible to unprivileged users (or controlled via ACL/permissions/group), an unprivileged process can open it, configure an unprivileged device with `UBLK_F_SHMEM_ZC`, start it, submit I/O, and reach `ublk_try_buf_match()`.
- In user namespaces: `capable(CAP_SYS_ADMIN)` checks init namespace, not user namespace (`ns_capable`). So inside a user namespace without init `CAP_SYS_ADMIN`, the user must still use `UBLK_F_UNPRIVILEGED_DEV`.
- **Dead-ends & Tool loops noted**:
- Attempting `git-log` with various regexes and file paths returned empty or merge commits (`704340f1cd0d`) due to git history boundary/shallow clone; `git-blame` showed lines committed at initial merge. No upstream fix commit was located in the local log.
#### 3. Current Hypotheses & Assessment
- **Exploitable**: **NO**.
- No concurrent write to `ub->buf_tree` occurs; writes are synchronized by queue freezing (`blk_mq_freeze_queue`).
- Read access in `ublk_try_buf_match` is consistent and cannot corrupt memory or read freed memory.
- In production builds without lockdep/RCU debug, the warning does not fire and no memory corruption, privilege escalation, or info leak occurs.
- **Denial of Service**: **NO**.
- In production builds without debug options, `WARNING: suspicious RCU usage` is purely a lockdep diagnostic check.
- The function operates normally, does not crash, deadlock, or panic (no `BUG_ON`, no unhandled failure).
- **Accessible from Unprivileged**:
- Depending on device node permissions for `/dev/ublk-control`, unprivileged users can configure `UBLK_F_UNPRIVILEGED_DEV` with `UBLK_F_SHMEM_ZC`. By default, standard Unix distributions restrict `/dev/ublk-control` to root/disk group, but the kernel code explicitly permits unprivileged creation if node permissions permit.
- **Remote / Guest / Peripheral / Filesystem triggers**:
- None applicable; requires local io_uring / char device interaction with ublk.
#### 4. Critical Files and Functions
- `drivers/block/ublk_drv.c`:
- `ublk_try_buf_match(struct ublk_device *ub, struct request *rq, u32 *buf_idx, u32 *buf_off)`: line 5621. Calls `mas_walk(&mas)`. Missing `rcu_read_lock()` / `rcu_read_unlock()` pair around the walk.
- `ublk_lock_buf_tree(struct ublk_device *ub)`: lines 5349-5358. Freezes queue on write.
- `ublk_ctrl_add_dev()`: lines 4664-4876. Handles `UBLK_F_UNPRIVILEGED_DEV` and `UBLK_F_SHMEM_ZC`.
- `ublk_init_iod()`: line 506. Invokes `ublk_try_buf_match()`.
Tokens:
input: 95560
output: 2183
thoughts: 0
|
| 95/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 28909
output: 42
thoughts: 49
Thoughts: Okay, here's my interpretation of that.
**Analyzing a Lockdep Warning**
Alright, a lockdep warning, specifically flagging a suspicious RCU usage within `mas_walk()` being called from `ublk_try_buf_match()`. Hmm, that narrows down the issue significantly. My immediate next step is to dive right into the `ublk_try_buf_match()` source code. I need to understand the context of the call to `mas_walk()` within that function. I'll be looking specifically for any areas where RCU reads might be involved, and where that interaction might be leading to this lockdep warning. I need to understand the data access patterns to see where things could be going wrong, or even to look for obvious violations of RCU rules.
|
| 96/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_try_buf_match]
Results: map[SourceCode:5620: /* Check if request pages match a registered shared memory buffer */
5621: static bool ublk_try_buf_match(struct ublk_device *ub,
5622: struct request *rq,
5623: u32 *buf_idx, u32 *buf_off)
5624: {
5625: struct req_iterator iter;
5626: struct bio_vec bv;
5627: int index = -1;
5628: unsigned long expected_offset = 0;
5629: bool first = true;
5630:
5631: rq_for_each_bvec(bv, rq, iter) {
5632: unsigned long pfn = page_to_pfn(bv.bv_page);
5633: unsigned long end_pfn = pfn +
5634: ((bv.bv_offset + bv.bv_len - 1) >> PAGE_SHIFT);
5635: struct ublk_buf_range *range;
5636: unsigned long off;
5637: MA_STATE(mas, &ub->buf_tree, pfn, pfn);
5638:
5639: range = mas_walk(&mas);
5640: if (!range)
5641: return false;
5642:
5643: /* verify all pages in this bvec fall within the range */
5644: if (end_pfn > mas.last)
5645: return false;
5646:
5647: off = range->base_offset +
5648: (pfn - mas.index) * PAGE_SIZE + bv.bv_offset;
5649:
5650: if (first) {
5651: /* Read-only buffer can't serve READ (kernel writes) */
5652: if ((range->flags & UBLK_SHMEM_BUF_READ_ONLY) &&
5653: req_op(rq) != REQ_OP_WRITE)
5654: return false;
5655: index = range->buf_index;
5656: expected_offset = off;
5657: *buf_off = off;
5658: first = false;
5659: } else {
5660: if (range->buf_index != index)
5661: return false;
5662: if (off != expected_offset)
5663: return false;
5664: }
5665: expected_offset += bv.bv_len;
5666: }
5667:
5668: if (first)
5669: return false;
5670:
5671: *buf_idx = index;
5672: return true;
5673: }
SourceFile:drivers/block/ublk_drv.c]
|
| 97/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 29968
output: 40
thoughts: 69
Thoughts: **Investigating a Potential Race Condition in the Ublk Driver**
Okay, so the warning popped up, and it's pointing the finger at a potential problem: `mas_walk(&mas)` being called without the proper locks. Specifically, it seems to be accessing the maple tree, `ub->buf_tree`, in this unlocked context. This is a classic recipe for disaster. We need to figure out where this unlocked access is happening.
The code suggests that `ublk_init_iod()` is the origin. It looks like `ublk_try_buf_match()` is called within that function, and that's the likely location where the unlocked access to `ub->buf_tree` is occurring. My immediate task is to scrutinize `ublk_try_buf_match()` and see precisely how it interacts with the maple tree and the locks. I'll need to trace the call paths and verify the lock acquisition strategy to see if we have a true race, and if so, understand the implications. This feels like it could be a subtle bug that might manifest as corruption or inconsistent data, depending on the concurrency pattern. Time to dive into the code!
|
| 98/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_init_iod]
Results: map[SourceCode: 506: static void ublk_init_iod(struct ublk_queue *ubq, struct request *req,
507: uint8_t ublk_op, uint32_t nr_sectors,
508: uint64_t start_sector)
509: {
510: struct ublksrv_io_desc *iod = ublk_get_iod(ubq, req->tag);
511: struct ublk_io *io = &ubq->ios[req->tag];
512:
513: iod->op_flags = ublk_op | ublk_req_build_flags(req);
514: iod->nr_sectors = nr_sectors;
515: iod->start_sector = start_sector;
516:
517: /* Try shmem zero-copy match before setting addr */
518: if (ublk_support_shmem_zc(ubq) && blk_rq_has_data(req)) {
519: u32 buf_idx, buf_off;
520:
521: if (ublk_try_buf_match(ubq->dev, req, &buf_idx, &buf_off)) {
522: iod->op_flags |= UBLK_IO_F_SHMEM_ZC;
523: iod->addr = ublk_shmem_zc_addr(buf_idx, buf_off);
524: return;
525: }
526: }
527:
528: iod->addr = io->buf.addr;
529: }
SourceFile:drivers/block/ublk_drv.c]
|
| 99/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 30796
output: 42
thoughts: 184
Thoughts: **Analyzing the Concurrency Issue in `ublk_try_buf_match()`**
Okay, so the summary indicates a lockdep warning in `ublk_try_buf_match()`. I need to dig into this. The core of the problem seems to be that `ublk_try_buf_match()` *should* be protected by `rcu_read_lock()` when accessing `ub->buf_tree`. I say *should*, because the summary implies we're missing an explicit wrapper. So, the question is, *can* this lack of explicit locking be exploited?
My initial thought is, if `ublk_try_buf_match()` is called without that RCU read lock, that's almost guaranteed to trigger a lockdep warning, which is bad, but is it *dangerous*? Well, if the underlying maple tree is being modified concurrently, then we could potentially have a classic use-after-free or, at best, an out-of-bounds read. That's my main concern.
However, the summary throws a wrench in the works: it mentions that access is serialized via `blk_mq_freeze_queue`. Okay, so if the queue is frozen, then there should be no concurrent modifications. That's the crux of it. The key is in verifying that serialization. I need to understand *how* `blk_mq_freeze_queue` interacts with the mapletree operations. That brings me to the next function to examine: `ublk_lock_buf_tree()`. I'm going to have to look at the locking logic in that function to see if it is in fact sufficient.
|
| 100/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_lock_buf_tree]
Results: map[SourceCode:5341: /*
5342: * Lock for maple tree modification: acquire ub->mutex, then freeze queue
5343: * if device is started. If device is not yet started, only mutex is
5344: * needed since no I/O path can access the tree.
5345: *
5346: * This ordering (mutex -> freeze) is safe because ublk_stop_dev_unlocked()
5347: * already holds ub->mutex when calling del_gendisk() which freezes the queue.
5348: */
5349: static unsigned int ublk_lock_buf_tree(struct ublk_device *ub)
5350: {
5351: unsigned int memflags = 0;
5352:
5353: mutex_lock(&ub->mutex);
5354: if (ub->ub_disk)
5355: memflags = blk_mq_freeze_queue(ub->ub_disk->queue);
5356:
5357: return memflags;
5358: }
SourceFile:drivers/block/ublk_drv.c]
|
| 101/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 31685
output: 42
thoughts: 285
Thoughts: **My Analysis of the ublk Lockdep Warning**
Okay, so `ublk_lock_buf_tree()` definitely freezes the queue, right? And `blk_mq_freeze_queue()` waits for *all* in-flight requests to complete. That's key. Since we're calling `ublk_try_buf_match()` *during* request processing, it's inherently an in-flight request. So, `blk_mq_freeze_queue()` is going to wait for `ublk_try_buf_match()` to wrap up, making sure nothing's getting concurrently modified in that maple tree. So, realistically, the lockdep warning is a false alarm. It's not pointing to a real data race or memory corruption issue. It's just a missing `rcu_read_lock()` annotation, which makes the static analysis unhappy.
Now, the question is, could this become a Denial of Service? A lockdep warning alone isn't going to crash the kernel in a production build – it just generates a warning. So, that's a no on the DoS.
Next, is this accessible from unprivileged processes? The user summary indicates it could be. It says depending on device node permissions for `/dev/ublk-control`, unprivileged users *can* configure `UBLK_F_UNPRIVILEGED_DEV` with `UBLK_F_SHMEM_ZC`. Now, standard Unix distributions lock down `/dev/ublk-control` to root/disk, but the kernel *explicitly* allows unprivileged creation if those node permissions are right. I think I'll go check `ublk_ctrl_add_dev()` to confirm how this is implemented.
|
| 102/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_ctrl_add_dev]
Results: map[SourceCode:4664: static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
4665: {
4666: void __user *argp = (void __user *)(unsigned long)header->addr;
4667: struct ublksrv_ctrl_dev_info info;
4668: struct ublk_device *ub;
4669: int ret = -EINVAL;
4670:
4671: if (header->len < sizeof(info) || !header->addr)
4672: return -EINVAL;
4673: if (header->queue_id != (u16)-1) {
4674: pr_warn("%s: queue_id is wrong %x\n",
4675: __func__, header->queue_id);
4676: return -EINVAL;
4677: }
4678:
4679: if (copy_from_user(&info, argp, sizeof(info)))
4680: return -EFAULT;
4681:
4682: if (info.queue_depth > UBLK_MAX_QUEUE_DEPTH || !info.queue_depth ||
4683: info.nr_hw_queues > UBLK_MAX_NR_QUEUES || !info.nr_hw_queues)
4684: return -EINVAL;
4685:
4686: if (capable(CAP_SYS_ADMIN))
4687: info.flags &= ~UBLK_F_UNPRIVILEGED_DEV;
4688: else if (!(info.flags & UBLK_F_UNPRIVILEGED_DEV))
4689: return -EPERM;
4690:
4691: /* forbid nonsense combinations of recovery flags */
4692: switch (info.flags & UBLK_F_ALL_RECOVERY_FLAGS) {
4693: case 0:
4694: case UBLK_F_USER_RECOVERY:
4695: case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_REISSUE):
4696: case (UBLK_F_USER_RECOVERY | UBLK_F_USER_RECOVERY_FAIL_IO):
4697: break;
4698: default:
4699: pr_warn("%s: invalid recovery flags %llx\n", __func__,
4700: info.flags & UBLK_F_ALL_RECOVERY_FLAGS);
4701: return -EINVAL;
4702: }
4703:
4704: if ((info.flags & UBLK_F_QUIESCE) && !(info.flags & UBLK_F_USER_RECOVERY)) {
4705: pr_warn("UBLK_F_QUIESCE requires UBLK_F_USER_RECOVERY\n");
4706: return -EINVAL;
4707: }
4708:
4709: /*
4710: * unprivileged device can't be trusted, but RECOVERY and
4711: * RECOVERY_REISSUE still may hang error handling, so can't
4712: * support recovery features for unprivileged ublk now
4713: *
4714: * TODO: provide forward progress for RECOVERY handler, so that
4715: * unprivileged device can benefit from it
4716: */
4717: if (info.flags & UBLK_F_UNPRIVILEGED_DEV) {
4718: info.flags &= ~(UBLK_F_USER_RECOVERY_REISSUE |
4719: UBLK_F_USER_RECOVERY);
4720:
4721: /*
4722: * For USER_COPY, we depends on userspace to fill request
4723: * buffer by pwrite() to ublk char device, which can't be
4724: * used for unprivileged device
4725: *
4726: * Same with zero copy or auto buffer register.
4727: */
4728: if (info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
4729: UBLK_F_AUTO_BUF_REG))
4730: return -EINVAL;
4731: }
4732:
4733: /* User copy is required to access integrity buffer */
4734: if (info.flags & UBLK_F_INTEGRITY && !(info.flags & UBLK_F_USER_COPY))
4735: return -EINVAL;
4736:
4737: if (info.flags & UBLK_F_IO_DESC_SIZE) {
4738: if (info.io_desc_size < sizeof(struct ublksrv_io_desc) ||
4739: info.io_desc_size % _Alignof(struct ublksrv_io_desc) ||
4740: info.io_desc_size > UBLK_MAX_IO_DESC_SIZE)
4741: return -EINVAL;
4742: } else {
4743: info.io_desc_size = sizeof(struct ublksrv_io_desc);
4744: }
4745:
4746: /* the created device is always owned by current user */
4747: ublk_store_owner_uid_gid(&info.owner_uid, &info.owner_gid);
4748:
4749: if (header->dev_id != info.dev_id) {
4750: pr_warn("%s: dev id not match %u %u\n",
4751: __func__, header->dev_id, info.dev_id);
4752: return -EINVAL;
4753: }
4754:
4755: if (header->dev_id != U32_MAX && header->dev_id >= UBLK_MAX_UBLKS) {
4756: pr_warn("%s: dev id is too large. Max supported is %d\n",
4757: __func__, UBLK_MAX_UBLKS - 1);
4758: return -EINVAL;
4759: }
4760:
4761: ublk_dump_dev_info(&info);
4762:
4763: ret = mutex_lock_killable(&ublk_ctl_mutex);
4764: if (ret)
4765: return ret;
4766:
4767: ret = -EACCES;
4768: if ((info.flags & UBLK_F_UNPRIVILEGED_DEV) &&
4769: unprivileged_ublks_added >= unprivileged_ublks_max)
4770: goto out_unlock;
4771:
4772: ret = -ENOMEM;
4773: ub = kzalloc_flex(*ub, queues, info.nr_hw_queues);
4774: if (!ub)
4775: goto out_unlock;
4776: mutex_init(&ub->mutex);
4777: spin_lock_init(&ub->lock);
4778: mutex_init(&ub->cancel_mutex);
4779: mt_init(&ub->buf_tree);
4780: ida_init(&ub->buf_ida);
4781: INIT_WORK(&ub->partition_scan_work, ublk_partition_scan_work);
4782:
4783: ret = ublk_alloc_dev_number(ub, header->dev_id);
4784: if (ret < 0)
4785: goto out_free_ub;
4786:
4787: memcpy(&ub->dev_info, &info, sizeof(info));
4788:
4789: /* update device id */
4790: ub->dev_info.dev_id = ub->ub_number;
4791:
4792: /*
4793: * ->state and ->ublksrv_pid are owned by the driver and only read back
4794: * by userspace, but they come from the copied-in dev_info, so reset
4795: * them. Otherwise a device added with ->state != DEAD looks live while
4796: * ->ub_disk is still NULL.
4797: */
4798: ub->dev_info.state = UBLK_S_DEV_DEAD;
4799: ub->dev_info.ublksrv_pid = -1;
4800:
4801: /*
4802: * 64bit flags will be copied back to userspace as feature
4803: * negotiation result, so have to clear flags which driver
4804: * doesn't support yet, then userspace can get correct flags
4805: * (features) to handle.
4806: */
4807: ub->dev_info.flags &= UBLK_F_ALL;
4808:
4809: ub->dev_info.flags |= UBLK_F_CMD_IOCTL_ENCODE |
4810: UBLK_F_URING_CMD_COMP_IN_TASK |
4811: UBLK_F_PER_IO_DAEMON |
4812: UBLK_F_BUF_REG_OFF_DAEMON |
4813: UBLK_F_SAFE_STOP_DEV;
4814:
4815: /* So far, UBLK_F_PER_IO_DAEMON won't be exposed for BATCH_IO */
4816: if (ublk_dev_support_batch_io(ub))
4817: ub->dev_info.flags &= ~UBLK_F_PER_IO_DAEMON;
4818:
4819: /* GET_DATA isn't needed any more with USER_COPY or ZERO COPY */
4820: if (ub->dev_info.flags & (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY |
4821: UBLK_F_AUTO_BUF_REG))
4822: ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
4823:
4824: /* UBLK_F_BATCH_IO doesn't support GET_DATA */
4825: if (ublk_dev_support_batch_io(ub))
4826: ub->dev_info.flags &= ~UBLK_F_NEED_GET_DATA;
4827:
4828: /*
4829: * Zoned storage support requires reuse `ublksrv_io_cmd->addr` for
4830: * returning write_append_lba, which is only allowed in case of
4831: * user copy or zero copy
4832: */
4833: if (ublk_dev_is_zoned(ub) &&
4834: (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) || !(ub->dev_info.flags &
4835: (UBLK_F_USER_COPY | UBLK_F_SUPPORT_ZERO_COPY)))) {
4836: ret = -EINVAL;
4837: goto out_free_dev_number;
4838: }
4839:
4840: ub->dev_info.nr_hw_queues = min_t(unsigned int,
4841: ub->dev_info.nr_hw_queues, nr_cpu_ids);
4842: ublk_align_max_io_size(ub);
4843:
4844: ret = ublk_add_tag_set(ub);
4845: if (ret)
4846: goto out_free_dev_number;
4847:
4848: ret = ublk_init_queues(ub);
4849: if (ret)
4850: goto out_free_tag_set;
4851:
4852: ret = -EFAULT;
4853: if (copy_to_user(argp, &ub->dev_info, sizeof(info)))
4854: goto out_deinit_queues;
4855:
4856: /*
4857: * Add the char dev so that ublksrv daemon can be setup.
4858: * ublk_add_chdev() will cleanup everything if it fails.
4859: */
4860: ret = ublk_add_chdev(ub);
4861: goto out_unlock;
4862:
4863: out_deinit_queues:
4864: ublk_deinit_queues(ub);
4865: out_free_tag_set:
4866: blk_mq_free_tag_set(&ub->tag_set);
4867: out_free_dev_number:
4868: ublk_free_dev_number(ub);
4869: out_free_ub:
4870: mutex_destroy(&ub->mutex);
4871: mutex_destroy(&ub->cancel_mutex);
4872: kfree(ub);
4873: out_unlock:
4874: mutex_unlock(&ublk_ctl_mutex);
4875: return ret;
4876: }
SourceFile:drivers/block/ublk_drv.c]
|
| 103/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 36010
output: 40
thoughts: 586
Thoughts: **My Assessment of the UBLK Unprivileged Device Control Concern**
Okay, let's break this down. My initial thought is that unprivileged users *shouldn't* be able to configure `UBLK_F_UNPRIVILEGED_DEV` with `UBLK_F_SHMEM_ZC` through `/dev/ublk-control`. The text states that this is possible depending on the file node's permissions. By default, Unix distros lock down `/dev/ublk-control` to the `root/disk` group, which is a good thing. The critical piece here is the kernel code's *explicit* allowance for unprivileged creation if the file permissions permit it.
The question then becomes: is this a vulnerability that's actually exploitable in *most* real-world scenarios? Typically, I'd consider something exploitable if the default permissions allow for unprivileged access, *or* if it's a very common, easily-enabled configuration that exposes this behavior. If `/dev/ublk-control` is correctly restricted, the bug is not accessible to unprivileged processes.
Now, considering this is a lockdep warning, I'd say that lockdep warnings are generally not exploitable and *don't* cause any Denial of Service in production builds. So, exploitable: False, Denial of Service: False. This gives me a reason to pause here, but I always want to make sure I'm not missing something.
Now, let's dig a bit deeper. I need to make sure I don't miss anything. If I understand correctly, `ublk_try_buf_match()` calls `mas_walk(&mas)`. Since `mas_walk()` doesn't hold any locks, I need to know if the `ub->buf_tree` is modified concurrently. The code shows the function `ublk_lock_buf_tree()` freezes the queue. Specifically, it locks the mutex and then freezes the queue which should be good enough.
```c
static unsigned int ublk_lock_buf_tree(struct ublk_device *ub)
{
unsigned int memflags = 0;
mutex_lock(&ub->mutex);
if (ub->ub_disk)
memflags = blk_mq_freeze_queue(ub->ub_disk->queue);
return memflags;
}
```
`blk_mq_freeze_queue()` waits for all in-flight requests to complete. This is the critical point. From what I understand, `ublk_try_buf_match()` is called from `ublk_init_iod()`, which is called from `ublk_setup_iod()`, which is called from `ublk_dispatch_req()`. `ublk_dispatch_req()` is in turn called from `ublk_cmd_tw_cb()`, which is a task work callback.
Wait, `ublk_dispatch_req()` is called when a request is dispatched! So, when is the request considered "in-flight"? Right, `blk_mq_start_request()` is called *before* or during dispatch. That confirms my expectation of the flow. Now I need to check `ublk_dispatch_req()` to make sure I'm not missing anything.
|
| 104/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_dispatch_req]
Results: map[SourceCode:1770: static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req)
1771: {
1772: unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
1773: u16 tag = req->tag;
1774: struct ublk_io *io = &ubq->ios[tag];
1775:
1776: ublk_setup_iod(ubq, req);
1777: pr_devel("%s: complete: qid %d tag %d io_flags %x addr %llx\n",
1778: __func__, ubq->q_id, req->tag, io->flags,
1779: ublk_get_iod(ubq, req->tag)->addr);
1780:
1781: /*
1782: * Task is exiting if either:
1783: *
1784: * (1) current != io->task.
1785: * io_uring_cmd_complete_in_task() tries to run task_work
1786: * in a workqueue if cmd's task is PF_EXITING.
1787: *
1788: * (2) current->flags & PF_EXITING.
1789: */
1790: if (unlikely(current != io->task || current->flags & PF_EXITING)) {
1791: __ublk_abort_rq(ubq, req);
1792: return;
1793: }
1794:
1795: if (ublk_need_get_data(ubq) && ublk_need_map_req(req)) {
1796: /*
1797: * We have not handled UBLK_IO_NEED_GET_DATA command yet,
1798: * so immediately pass UBLK_IO_RES_NEED_GET_DATA to ublksrv
1799: * and notify it.
1800: */
1801: io->flags |= UBLK_IO_FLAG_NEED_GET_DATA;
1802: pr_devel("%s: need get data. qid %d tag %d io_flags %x\n",
1803: __func__, ubq->q_id, req->tag, io->flags);
1804: ublk_complete_io_cmd(io, req, UBLK_IO_RES_NEED_GET_DATA,
1805: issue_flags);
1806: return;
1807: }
1808:
1809: if (!ublk_start_io(ubq, req, io))
1810: return;
1811:
1812: if (ublk_support_auto_buf_reg(ubq) && blk_rq_has_data(req)) {
1813: ublk_auto_buf_dispatch(ubq, req, io, io->cmd, issue_flags);
1814: } else {
1815: ublk_init_req_ref(ubq, io);
1816: ublk_complete_io_cmd(io, req, UBLK_IO_RES_OK, issue_flags);
1817: }
1818: }
SourceFile:drivers/block/ublk_drv.c]
|
| 105/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 38247
output: 55
thoughts: 60
Thoughts: Here's my take on the thought process, as requested:
**Digging into the ublk Request Dispatch**
Okay, so I'm tracing down this `ublk_dispatch_req()` function call. It's coming from `ublk_cmd_tw_cb()`, which is interesting. Wait a minute... `ublk_cmd_tw_cb()`... that's a task work callback, isn't it? Right. So, this dispatch is happening asynchronously. Now I need to see *when* and *why* this task work callback, `ublk_cmd_tw_cb()`, is being scheduled. That's the critical piece of the puzzle here – understanding the timing of the dispatch itself. This likely has implications for latency or potential contention, depending on the scheduler's behavior. Time to delve further into the scheduling mechanisms.
|
| 106/2 |
2026/09/15 03:22 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_cmd_tw_cb SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:takes-address-of ReferencingEntityKind:function ReferencingEntityName:ublk_queue_cmd SourceFile:drivers/block/ublk_drv.c SourceLine:2091]] TruncatedOutput:false]
|
| 107/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 38622
output: 40
thoughts: 13
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into `ublk_queue_cmd()`**
Okay, time to dig into the guts of the Ublk implementation. I'm going to focus on `ublk_queue_cmd()` specifically. I need to understand its role in command submission. From my knowledge of block device drivers, I expect this to be the core function where I'm going to find the details of how the Ublk handles command queuing. I'll be looking for the interplay between the submission path, the actual data transfer setup, and the potential interactions with hardware queues or software rings, depending on the Ublk backend. The critical aspects will be how it manages the command metadata, how it ensures command ordering (especially in a multi-queue scenario), and how it interfaces with the underlying storage fabric. I need to see how the completion path is tied back to this submission function, and how errors are handled at this stage. I'll pay close attention to the lock usage and synchronization primitives to see where potential contention might arise.
|
| 108/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_queue_cmd]
Results: map[SourceCode:2085: static void ublk_queue_cmd(struct ublk_queue *ubq, struct request *rq)
2086: {
2087: struct io_uring_cmd *cmd = ubq->ios[rq->tag].cmd;
2088: struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
2089:
2090: pdu->req = rq;
2091: io_uring_cmd_complete_in_task(cmd, ublk_cmd_tw_cb);
2092: }
SourceFile:drivers/block/ublk_drv.c]
|
| 109/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 39094
output: 40
thoughts: 20
Thoughts: Here's my understanding of the situation, given my expertise:
**My Perspective on the `ublk_queue_cmd()` Call**
Okay, so I see that `ublk_queue_cmd()` is getting invoked from `ublk_queue_rq()`. That's the key relationship I need to focus on. Based on the naming, `ublk_queue_rq()` is likely the primary function involved in enqueuing a request (`rq`). So `ublk_queue_cmd()` must be dealing with the actual *submission* or perhaps the detailed processing of that queued command.
Given the "ublk" prefix, this is almost certainly related to the user-space block driver framework. I'll need to dig deeper into the code to figure out precisely what `ublk_queue_cmd()` is doing within that submission process. Is it preparing the command for the hardware? Is it handling any necessary translations or data copies? Or possibly adding it to a command queue data structure? The fact that it's called *from* the queue function suggests it's a critical, low-level piece. I’ll need to understand how the parameters passed to `ublk_queue_rq()` are used within `ublk_queue_cmd()` to build a complete picture of the request flow and what the `ublk_queue_cmd()` call really signifies. My understanding of the framework will tell me what the likely scenarios are.
|
| 110/2 |
2026/09/15 03:22 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c Name:ublk_queue_rq]
Results: map[SourceCode:2203: static blk_status_t ublk_queue_rq(struct blk_mq_hw_ctx *hctx,
2204: const struct blk_mq_queue_data *bd)
2205: {
2206: struct ublk_queue *ubq = hctx->driver_data;
2207: struct request *rq = bd->rq;
2208: bool should_queue;
2209: blk_status_t res;
2210:
2211: res = __ublk_queue_rq_common(ubq, rq, &should_queue);
2212: if (!should_queue)
2213: return res;
2214:
2215: ublk_queue_cmd(ubq, rq);
2216: return BLK_STS_OK;
2217: }
SourceFile:drivers/block/ublk_drv.c]
|
| 111/2 |
2026/09/15 03:22 |
llm |
expert |
0m
Model:
Tokens:
input: 39725
output: 55
thoughts: 979
Thoughts: **Analysis of the UBLK Lockdep Warning**
Okay, let's break this down systematically. My initial thought is that the lockdep warning triggered by `lib/maple_tree.c:720` during a call to `ublk_try_buf_match()` is likely a false positive. We know that `ublk_queue_rq()` is the block layer callback, and when it's called, the request is already in-flight. Importantly, `blk_mq_freeze_queue()` within `ublk_lock_buf_tree()` will definitively wait for the completion of that request. This means that, at that point, there *cannot* be concurrent modification of `ub->buf_tree` while `ublk_try_buf_match()` is running. Hence, the lockdep warning is a red herring.
Now, let's explore if there are any *other* ways to trigger this warning. Remember, this is a *lockdep* warning specifically – it won't manifest in production builds because lockdep is disabled there. So, the system *won't* crash due to this. Therefore, Denial of Service is False. And because it's a lockdep warning, not a memory corruption, it is not directly exploitable. So, Exploitable is also False.
Next, I need to figure out if unprivileged processes can trigger this. The user summary mentions `/dev/ublk-control` and the flag `UBLK_F_UNPRIVILEGED_DEV`. By default, standard Unix distributions restrict access to `/dev/ublk-control` to root/disk group, implying it's *not* accessible to unprivileged users. However, the kernel code permits unprivileged creation *if* device node permissions allow it. Let's revisit `ublk_ctrl_add_dev()`. I see that unprivileged users *must* set `UBLK_F_UNPRIVILEGED_DEV` if they are not CAP_SYS_ADMIN. But they still need to be able to *open* `/dev/ublk-control` in the first place. Kernel-created char devices generally have `0600` permissions. Unless a udev rule intervenes, this means root-only access. Do systemd/udev typically have a rule for `ublk-control`? Usually, no.
A key point is that the syzkaller reproducer runs as an unprivileged user (or in a user namespace). However, `capable(CAP_SYS_ADMIN)` checks the *init* namespace. Whereas `ns_capable()` checks the user namespace. So, in a user namespace, `capable(CAP_SYS_ADMIN)` will be false.
The reproducer attempts to open `/dev/ublk-control`. Syzkaller runs as root in its test environment, or it may have special access to `/dev/ublk-control` due to its setup. Standard Linux systems have `/dev/ublk-control` as `crw------- 1 root root`. It's not accessible to an unprivileged user. Even within a user namespace, they still *can't* access it unless it's bind-mounted or permissions are modified externally. So, Unprivileged is False, and UserNamespace is also False.
Let's do one last check. Is there *any* other possible trigger? The root of the issue is still a lockdep warning. Since `blk_mq_freeze_queue()` guarantees no concurrent modifications during execution of `ublk_try_buf_match()`, I am certain there is no chance for memory corruption. Therefore, Exploitable and DenialOfService are False.
The conclusions are as follows:
Exploitable: False.
DenialOfService: False.
Unprivileged: False.
UserNamespace: False.
|
| 112/2 |
2026/09/15 03:23 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/block/ublk_drv.c IncludeSnippetLines:0 Name:ublk_try_buf_match SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:ublk_init_iod SourceFile:drivers/block/ublk_drv.c SourceLine:521]] TruncatedOutput:false]
|
| 113/2 |
2026/09/15 03:23 |
llm |
expert |
0m
Model:
Tokens:
input: 41640
output: 33
thoughts: 41
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Investigating `ublk_buf_tree` Accesses**
Right, `ublk_try_buf_match()` calls `ublk_init_iod()`. Got it. That's a key starting point. But, my immediate concern is: where *else* is `ub->buf_tree` being touched within the Ublk subsystem? Knowing the scope of its usage is critical. Understanding all the access points to `ub->buf_tree` is essential for debugging and optimization. I need to be sure I've identified every potential point of contention or unexpected interaction with this structure. Let me see where else in the codebase this structure is used.
|
| 114/2 |
2026/09/15 03:23 |
tool |
grepper |
0m
Args: map[Expression:buf_tree PathPrefix:drivers/block/ublk_drv.c]
Results: map[Output:drivers/block/ublk_drv.c=311=struct ublk_device {
--
drivers/block/ublk_drv.c-344- /* shared memory zero copy */
drivers/block/ublk_drv.c:345: struct maple_tree buf_tree;
drivers/block/ublk_drv.c-346- struct ida buf_ida;
--
drivers/block/ublk_drv.c=4664=static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
--
drivers/block/ublk_drv.c-4778- mutex_init(&ub->cancel_mutex);
drivers/block/ublk_drv.c:4779: mt_init(&ub->buf_tree);
drivers/block/ublk_drv.c-4780- ida_init(&ub->buf_ida);
--
drivers/block/ublk_drv.c=5315=static int ublk_char_dev_permission(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5348-*/
drivers/block/ublk_drv.c:5349:static unsigned int ublk_lock_buf_tree(struct ublk_device *ub)
drivers/block/ublk_drv.c-5350-{
--
drivers/block/ublk_drv.c-5359-
drivers/block/ublk_drv.c:5360:static void ublk_unlock_buf_tree(struct ublk_device *ub, unsigned int memflags)
drivers/block/ublk_drv.c-5361-{
--
drivers/block/ublk_drv.c=5368=static void ublk_buf_erase_ranges(struct ublk_device *ub, int buf_index)
drivers/block/ublk_drv.c-5369-{
drivers/block/ublk_drv.c:5370: MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
drivers/block/ublk_drv.c-5371- struct ublk_buf_range *range;
--
drivers/block/ublk_drv.c=5383=static int __ublk_ctrl_reg_buf(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5408-
drivers/block/ublk_drv.c:5409: ret = mtree_insert_range(&ub->buf_tree, pfn,
drivers/block/ublk_drv.c-5410- pfn + (i - start),
--
drivers/block/ublk_drv.c=5429=static int ublk_ctrl_reg_buf(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5480-
drivers/block/ublk_drv.c:5481: memflags = ublk_lock_buf_tree(ub);
drivers/block/ublk_drv.c-5482-
--
drivers/block/ublk_drv.c-5494-
drivers/block/ublk_drv.c:5495: ublk_unlock_buf_tree(ub, memflags);
drivers/block/ublk_drv.c-5496- kvfree(pages);
--
drivers/block/ublk_drv.c-5499-err_unlock:
drivers/block/ublk_drv.c:5500: ublk_unlock_buf_tree(ub, memflags);
drivers/block/ublk_drv.c-5501-err_unpin:
--
drivers/block/ublk_drv.c=5541=static bool __ublk_shmem_remove_ranges(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5543-{
drivers/block/ublk_drv.c:5544: MA_STATE(mas, &ub->buf_tree, 0, ULONG_MAX);
drivers/block/ublk_drv.c-5545- struct ublk_buf_range *range;
--
drivers/block/ublk_drv.c=5590=static int ublk_ctrl_unreg_buf(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5602-
drivers/block/ublk_drv.c:5603: memflags = ublk_lock_buf_tree(ub);
drivers/block/ublk_drv.c-5604-
--
drivers/block/ublk_drv.c-5608-
drivers/block/ublk_drv.c:5609: ublk_unlock_buf_tree(ub, memflags);
drivers/block/ublk_drv.c-5610- return ret;
--
drivers/block/ublk_drv.c=5613=static void ublk_buf_cleanup(struct ublk_device *ub)
--
drivers/block/ublk_drv.c-5615- ublk_shmem_remove_ranges(ub, -1);
drivers/block/ublk_drv.c:5616: mtree_destroy(&ub->buf_tree);
drivers/block/ublk_drv.c-5617- ida_destroy(&ub->buf_ida);
--
drivers/block/ublk_drv.c=5621=static bool ublk_try_buf_match(struct ublk_device *ub,
--
drivers/block/ublk_drv.c-5636- unsigned long off;
drivers/block/ublk_drv.c:5637: MA_STATE(mas, &ub->buf_tree, pfn, pfn);
drivers/block/ublk_drv.c-5638-
]
|
| 115/2 |
2026/09/15 03:23 |
llm |
expert |
0m
Model:
Tokens:
input: 43350
output: 66
thoughts: 741
Thoughts: **My Assessment of the ublk Lockdep Warning**
Okay, so I've dug into this lockdep warning concerning `ublk_try_buf_match()`. It's pretty clear that this function is the only one that reads the maple tree (`ub->buf_tree`) without holding a specific lock, specifically RCU or the maple tree lock, but it also appears that the request queue is frozen when `ublk_lock_buf_tree()` is called. That should prevent concurrent modifications. So, the lockdep warning is likely a false positive. We're getting tripped up by a lack of an `rcu_read_lock()` annotation.
Let's trace this warning back. It's triggered within `ublk_try_buf_match()`, which is called from `ublk_init_iod()`, which is called from `ublk_setup_iod()`, and that in turn is called from `ublk_dispatch_req()`. The whole chain starts with `ublk_cmd_tw_cb()`, which is a task work callback, and this is where it gets interesting because those callbacks execute without an RCU read lock by default. That's why `mas_walk(&mas)` in `ublk_try_buf_match()` triggers the lockdep.
Now, the important part: is this exploitable? No. Lockdep warnings are just that: warnings. They don't crash the kernel in production builds. So, no security impact, no Denial of Service. Exploitable: False. DenialOfService: False.
Could an unprivileged user trigger this? `/dev/ublk-control` is usually root-restricted. Therefore, the issue isn't accessible to unprivileged processes by default. Unprivileged: False. UserNamespace: False. It's not a VM guest, network, remote, or peripheral trigger either. VMHostTrigger: False, NetworkTrigger: False, RemoteTrigger: False, PeripheralTrigger: False, FilesystemTrigger: False.
I've double-checked: even if an unprivileged user somehow *could* access `/dev/ublk-control`, it still wouldn't matter. The bug is just a lockdep warning, so no exploit, no DoS.
To summarize, the issue is a lockdep warning related to RCU usage in `ublk_try_buf_match()`, specifically `mas_walk()` without proper locking. However, concurrent modifications are actually prevented by queue freezing. The warning is a false positive because of the missing annotation and it's not exploitable or a DoS risk, and the control device is usually restricted to root. I'll summarize using `set-results`.
|
| 116/2 |
2026/09/15 03:23 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 117/2 |
2026/09/15 03:23 |
llm |
expert |
0m
Model:
Tokens:
input: 44789
output: 537
thoughts: 13
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert user, and presented in the first person:
**Finalizing the Response**
Alright, the data's in, the analysis is complete, and the simulations have run. Now it's time to craft the definitive conclusion. No more refinements, no more iterations on the methodology – the groundwork is solid. My responsibility now is to present the findings concisely and accurately, ensuring that the final response effectively communicates the implications of this work. It's about synthesizing all the information into a coherent narrative. Let's make this response count.
|
| 118/1 |
2026/09/15 03:23 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the bug report and the kernel source code, here is the
assessment of the security impact:
**1. Bug Mechanism & Exploitability:**
The bug is a lockdep warning (`WARNING: suspicious RCU usage`) triggered in
`ublk_try_buf_match()` when it calls `mas_walk(&mas)` to traverse the
`ub->buf_tree` maple tree. The warning occurs because the function does not
explicitly hold the RCU read lock (`rcu_read_lock()`) or the maple tree lock
during the traversal.
However, this is a false positive in terms of actual memory safety. Write
modifications to `ub->buf_tree` (such as adding or removing buffers) are
protected by `ublk_lock_buf_tree()`, which calls `blk_mq_freeze_queue()`.
Freezing the queue ensures that all in-flight block requests are fully drained
before any modifications can proceed. Since `ublk_try_buf_match()` is executed
as part of dispatching an in-flight request, it is guaranteed that no concurrent
writes to the maple tree can occur while it is running.
Because there is no actual data race or memory corruption, the bug is **not
exploitable**.
**2. Denial of Service (DoS):**
In production kernel builds, debug options like `CONFIG_PROVE_RCU` (lockdep) are
disabled. Therefore, this warning will not even be evaluated or printed, and it
will not cause a kernel panic, hang, or resource leak. The system will continue
to operate normally. Thus, it does not pose a Denial of Service risk.
**3. Accessibility:**
To trigger this code path, a user must interact with the `/dev/ublk-control`
character device to set up a userspace block device and configure it with the
`UBLK_F_SHMEM_ZC` flag. By default, standard Linux distributions restrict access
to `/dev/ublk-control` to the `root` user (or a privileged group like `disk`).
While the kernel code does have provisions for unprivileged device creation
(`UBLK_F_UNPRIVILEGED_DEV`), it still fundamentally relies on the user having
permission to open the device node in the first place. Therefore, it is
typically **not accessible from unprivileged processes or user namespaces**.
**Conclusion:**
The bug is a benign missing lockdep annotation (missing `rcu_read_lock()`) that
does not lead to any memory corruption or system instability. It has no security
impact on production environments.]
|