From nobody Mon Jun 8 07:23:00 2026 Received: from mail-qk1-f174.google.com (mail-qk1-f174.google.com [209.85.222.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F84E339847 for ; Sun, 31 May 2026 14:23:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780237398; cv=none; b=Jm5J3L9NwTpX0HoF9TRNHM/RwC9p9ljr3gY8/8uzIyJl12sMIOzrnTA+0WaQOv46l4qgcleMZhG5cGcdm/aQHImJVh7l2YI+LyDrkOvPKAwcH73hZVZxjveNkuXweWPsYp5U9d5h/shbG5MAoCiz7NDBpUwYQ/4BpY3un2mh/xQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780237398; c=relaxed/simple; bh=9z1lDO7WVRwOEqS8leOsAWQHA9GkNdjVjksm5OmCH2U=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=N+Td78jVXh+NwknI2K4X99o1vrtPDh+BTjraXMt9uqUZ3TsgCuHwynVGbB8zccOIG4QQBVt6aMKesfhFfd+DYKOQ8p6BNQScgdXkwDT6yjuOy93cleNUzzCrIYywUWuL1uC8/gl0KZHINzFCQLCCdLMob2z4d8QHeX60OSwOsfI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=evm6D9YD; arc=none smtp.client-ip=209.85.222.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="evm6D9YD" Received: by mail-qk1-f174.google.com with SMTP id af79cd13be357-914b5f85129so804056785a.3 for ; Sun, 31 May 2026 07:23:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780237395; x=1780842195; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=qDDIR+RflfCxQnybr+lZETyzlWzPcM4iBOlWecmvA2w=; b=evm6D9YDcOxEMYOJT6+jJr9WxBaMYoXykx5xcHyzNYaau1b1F//f0CC4h3NPVVbD+C WBeCTD92tq1jwMVS7rrKbPpRWOSTCyMHY8c8HqyAe6B36YcKoK8R0pCHFMcofqEbwAhK kJOjtvybHQAPUNkAIj6y8TlI4X931twpv6SfaP5MeMx2isrdiOpJ0XVI6dw+zP7gL47J IB9K/tLaw2qA2r6YggCdATWoUSdGDT7zLqDVrhF+1m1Mdo3xdm8pFK9yT/vsKRhzaTxT 66J09hbm/bPeOXgWn1ets7qzq1ptN9GDHSPg9VurW40Po6az0ItkxW6zVruuQR7iUa+R txyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780237395; x=1780842195; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=qDDIR+RflfCxQnybr+lZETyzlWzPcM4iBOlWecmvA2w=; b=BFTo6fwdgfezxY2083mEeG47OJKPf6UM1iF/SsKGoZjP/lnUZ7ntFRBRXjoRyOGkOd fkpCPLBEiLBiWsKryQBN9JPlvXJudLQ+zbQltBvCyw5SBKysf0+ciOozmloD6OXJWhiV kmDTg/G0HgkwI0Q+2EwzjyXV9g2kOTHG15aC1NXuVrJmivpka4eVUTdM4UGYd757KPEi ikXAn3QilRwArb1xvkN/oh94Kw2FhLZLIogr5Cmxm7DRsmBN1Y2QSEdpv9XVoc1F6yOf y/3gtfYpPq/JMRKQ3FlefZkS7Bsr9sXtauGzYYNRQILcOUDXhahfOo5zlnG+mrujqOIF /cJQ== X-Forwarded-Encrypted: i=1; AFNElJ/jDGyh4EnH9rSblvA5tKNkeik7xBH6nrpTisddQh2RxMoy97jKpqPWZy9Y8TMO2K3Z7TTBHCuZlimgYSA=@vger.kernel.org X-Gm-Message-State: AOJu0Yynf/fR858eRBaZIvsvV6lbAbuBa1kdnzeEuX+SSTDYu5KmwFdz ADPIgg/9aAs4YnaLPRMySzwnmRdZr1mbGz9rk9vpNMrkZDkUyjY5dNZo X-Gm-Gg: Acq92OHIN6Z/d3a9plVq/Q/zmPAwai1e1nRCN108E2cpuSh+gvqt2+QAmIpwZOJf58o FceVFAXA66AN/0ckERCoLVu0zLSN3LPkXZGw2sSQNdMRICDoK7aG05B3UU8nBXE6W1uqmngc2Br e8CgnvKxPNtX2HrAOceRhgLj6HE4a2oyynaN23dWzh6fSA+K2CqDaPA41WiVTCSReYf3cuFyY4e VmNEayfS/KnGJFmrwDm+8ZBOS8tfIN0QV/E2zwaEfv990OCx2XtarF6/i9xF+wZsfRlA+DaSO3M 4MU2Z0pIcKw+bj8ONR4U7N1ymsDISpiSb5cjTYhk0WRCpcWwDFOGLqrmMFbcs1Cze7u45oT/OeF frSOfnbfYuS7O8wpPMb36HSxfMQq9ey78xeIZ6C8IP2BQzadMibXtZQu+SLvR+/Y0wbsqqsPwrn w6enKRv5BwTVKSQ00IzY4q038tEPpl5BwlkGN9WAa+JQVTsPBLbyC568kE7KRVqhX6SSXEs9m+7 jE49c/kvJ5PLLdNTNpxWnnVFONh5iE= X-Received: by 2002:a05:620a:c4d:b0:910:db3a:7bdb with SMTP id af79cd13be357-9153d9fa2c3mr1132560685a.32.1780237395122; Sun, 31 May 2026 07:23:15 -0700 (PDT) Received: from server0 (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id af79cd13be357-9153249370csm769221285a.19.2026.05.31.07.23.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 31 May 2026 07:23:14 -0700 (PDT) From: Michael Bommarito To: Olivia Mackall , Herbert Xu , linux-crypto@vger.kernel.org Cc: "Michael S . Tsirkin" , Jason Wang , Kees Cook , Christian Borntraeger , virtualization@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH v3] hwrng: virtio: clamp device-reported used.len at copy_data() Date: Sun, 31 May 2026 10:22:51 -0400 Message-ID: <20260531142251.2792061-1-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" random_recv_done() stores the device-reported used.len directly into vi->data_avail. copy_data() then indexes vi->data[] using vi->data_idx (advanced by previous copy_data() calls) and issues a memcpy() without re-validating either value against the posted buffer size sizeof(vi->data) (SMP_CACHE_BYTES bytes, typically 32 or 64). A malicious or buggy virtio-rng backend can set used.len beyond sizeof(vi->data), steering the memcpy() past the end of the inline array into adjacent kmalloc-1k slab bytes. hwrng_fillfn() mixes those bytes into the guest RNG, and guest root can also observe them directly via /dev/hwrng. Concrete impact is inside the guest: - Memory-safety / hardening: any virtio-rng backend that over-reports used.len causes the driver to read past vi->data into unrelated slab contents. hwrng_fillfn() is a kernel thread that runs as soon as the device is probed; no guest userspace interaction is required to first-trigger the OOB. - Cross-boundary leak (confidential-compute threat model): a malicious hypervisor cooperating with a malicious or compromised guest root userspace can use /dev/hwrng as a leak channel for guest-kernel heap data. The host sets a large used.len, guest root reads /dev/hwrng, and the returned bytes contain guest kernel slab contents that were adjacent to vi->data. In practice, confidential-compute guests (SEV-SNP, TDX) usually disable virtio-rng entirely, so this path is narrow, but the fix is still worth carrying because the underlying memory-safety bug contaminates the guest RNG on any host. KASAN confirms the OOB on a 7.1-rc4 guest whose virtio-rng backend has been patched to report used.len =3D 0x10000: BUG: KASAN: slab-out-of-bounds in virtio_read+0x394/0x5d0 Read of size 64 at addr ffff88800ae0ba20 by task hwrng/52 Call Trace: __asan_memcpy+0x23/0x60 virtio_read+0x394/0x5d0 hwrng_fillfn+0xb2/0x470 kthread+0x2cc/0x3a0 Allocated by task 1: probe_common+0xa5/0x660 virtio_dev_probe+0x549/0xbc0 The buggy address belongs to the object at ffff88800ae0b800 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 0 bytes to the right of allocated 544-byte region [ffff88800ae0b800, ffff88800ae0ba20) Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"), which hardened usb9pfs_rx_complete() against unchecked device-reported length in the USB 9p transport. With the clamp at point of use and array_index_nospec() in place, the same harness boots cleanly: copy_data() returns zero for the bogus report, the device-supplied bytes after data_idx are discarded, and the driver issues a fresh request. Fixes: f7f510ec1957 ("virtio: An entropy device, as suggested by hpa.") Cc: stable@vger.kernel.org Suggested-by: Michael S. Tsirkin Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-8 --- Changes in v3: - No functional change from v2. Reposting the v2 clamp after the v2 thread went quiet on linux-crypto. Michael S. Tsirkin reconfirmed off-list that clamping the device-reported used.len at sizeof(vi->data) addresses his earlier concern, so this resends that fix unchanged. - Rebased onto v7.1-rc4. copy_data() is unchanged since 2023, so the clamp applies as-is, and the KASAN reproduction above was re-run on v7.1-rc4 (stock splats, patched boots clean). Changes in v2 (Michael S. Tsirkin review): - move the bound check from random_recv_done() into copy_data(), so the clamp sits immediately next to the memcpy() it protects. - clamp to sizeof(vi->data) rather than substituting len =3D 0, so a previously-working but buggy device that occasionally over-reports used.len does not start returning zero-length reads. - add array_index_nospec() on vi->data_idx to defeat a speculative out-of-bounds read given the malicious-backend threat model. - expand the commit message with the /dev/hwrng observation path and the hypervisor plus guest-root cooperation scenario. v1: https://lore.kernel.org/all/20260418000020.1847122-1-michael.bommarito@= gmail.com/ v2: https://lore.kernel.org/all/20260418150613.3522589-1-michael.bommarito@= gmail.com/ drivers/char/hw_random/virtio-rng.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/v= irtio-rng.c index 0ce02d7e5048e..5e83ffa105e41 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -69,8 +70,26 @@ static void request_entropy(struct virtrng_info *vi) static unsigned int copy_data(struct virtrng_info *vi, void *buf, unsigned int size) { - size =3D min_t(unsigned int, size, vi->data_avail); - memcpy(buf, vi->data + vi->data_idx, size); + unsigned int idx, avail; + + /* + * vi->data_avail was set from the device-reported used.len and + * vi->data_idx was advanced by previous copy_data() calls. A + * malicious or buggy virtio-rng backend can drive either past + * sizeof(vi->data). Clamp at point of use and harden the index + * with array_index_nospec() so the memcpy() below cannot be + * steered into adjacent slab memory, including under + * speculation. + */ + avail =3D min_t(unsigned int, vi->data_avail, sizeof(vi->data)); + if (vi->data_idx >=3D avail) { + vi->data_avail =3D 0; + request_entropy(vi); + return 0; + } + size =3D min_t(unsigned int, size, avail - vi->data_idx); + idx =3D array_index_nospec(vi->data_idx, sizeof(vi->data)); + memcpy(buf, vi->data + idx, size); vi->data_idx +=3D size; vi->data_avail -=3D size; if (vi->data_avail =3D=3D 0) base-commit: a1f173eb51db0dc78536334729ef832c62d6c65a --=20 2.53.0