From: Amit Machhiwal <amachhiw@linux.ibm.com>
To: sashiko-reviews@lists.linux.dev
Cc: Amit Machhiwal <amachhiw@linux.ibm.com>, kvm@vger.kernel.org
Subject: Re: [PATCH v6 1/4] KVM: PPC: Introduce KVM_CAP_PPC_COMPAT_CAPS and wire up ioctl
Date: Thu, 6 Aug 2026 21:37:19 +0530 [thread overview]
Message-ID: <20260806211428.e98e021d-5c-amachhiw@linux.ibm.com> (raw)
In-Reply-To: <20260804181937.2EF2A1F000E9@smtp.kernel.org>
On 2026/08/04 06:19 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] The ioctl artificially inflates the userspace struct's `size` field on success, creating a memory corruption trap if userspace reuses the struct.
> - [High] The ioctl unconditionally rejects larger structs with -E2BIG, bypassing standard UAPI zero-checking and encouraging silent input truncation.
> - [Medium] The KVM_PPC_GET_COMPAT_CAPS ioctl availability is decoupled from its KVM_CAP_PPC_COMPAT_CAPS capability, breaking capability negotiation and bisectability.
> --
>
> --- Patch [1]: [PATCH v6 1/4] KVM: PPC: Introduce KVM_CAP_PPC_COMPAT_CAPS and wire up ioctl ---
> commit bcf86bb5d3079ca2dbb4bf09693ba6e6a24b06c5
> Author: Amit Machhiwal <amachhiw@linux.ibm.com>
>
> KVM: PPC: Introduce KVM_CAP_PPC_COMPAT_CAPS and wire up ioctl
>
> This commit introduces a new capability and ioctl to expose CPU compatibility
> modes supported by the host processor for nested guests.
>
> > diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
> > index 00302399fc37b..8d6632c64ca5f 100644
> > --- a/arch/powerpc/kvm/powerpc.c
> > +++ b/arch/powerpc/kvm/powerpc.c
> > @@ -697,6 +697,13 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> > }
> > }
> > break;
> > +#if defined(CONFIG_KVM_BOOK3S_HV_POSSIBLE)
> > + case KVM_CAP_PPC_COMPAT_CAPS:
> > + r = 0;
> > + if (kvmhv_on_pseries())
> > + r = 1;
> > + break;
>
> [Severity: Medium]
> Is this capability check fully aligned with the ioctl's availability?
>
> This check returns 1 on a pseries host using PR KVM (since it misses the
> hv_enabled check), but the ioctl might fail with -ENOTTY if the backend
> is missing.
Looking closely, it looks like a good catch. PPC kernels can be built
both KVM-PR and as well KVM-HV config enabled. Though at one point in
time only one of kvm-pr and kvm-hv can be loaded, a PR KVM guest on a
pseries system can see cap=1 but then get -ENOTTY from the ioctl
(thought the documentation explicitly covers this case: "ENOTTY if the
backend does not implement the get_compat_caps operation (e.g., on
non-HV KVM implementations). But anyway, I'll fix in v7:
case KVM_CAP_PPC_COMPAT_CAPS:
r = 0;
if (hv_enabled && kvmhv_on_pseries())
r = 1;
break;
>
> Later in the ioctl execution, it succeeds on a bare-metal host using HV KVM
> (missing the kvmhv_on_pseries() check) even though this capability check
> would return 0.
On bare-metal, kvmhv_on_pseries() is false (CPU_FTR_HVMODE is set), so
the cap correctly returns 0 there regardless of hv_enabled. The ioctl is
only meaningful on pseries (PAPR guests) where KVM acts as L1 under a
hypervisor, which is why both conditions are required
>
> Could this decoupling between the capability and the ioctl availability break
> capability negotiation for userspace?
>
> > +#endif /* CONFIG_KVM_BOOK3S_HV_POSSIBLE */
> > default:
> > r = 0;
> > break;
>
> [ ... ]
>
> > @@ -2463,6 +2470,70 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
> > r = kvm->arch.kvm_ops->svm_off(kvm);
> > break;
> > }
> > + case KVM_PPC_GET_COMPAT_CAPS: {
> > + struct kvm_ppc_compat_caps host_caps = {};
> > + u64 usize;
> > +
> > + /*
> > + * Read the size field first to drive copy_struct_from_user.
> > + * size must be the first field of the struct.
> > + */
> > + r = -EFAULT;
> > + if (get_user(usize, (__u64 __user *)argp))
> > + goto out;
> > +
> > + /*
> > + * Enforce a minimum: reject buffers smaller than the initial
> > + * struct version (VER0). This allows old userspace compiled
> > + * against the original struct to still work on a newer kernel
> > + * that has grown the struct with appended fields.
> > + */
> > + r = -EINVAL;
> > + if (usize < KVM_PPC_COMPAT_CAPS_SIZE_VER0)
> > + goto out;
> > +
> > + /*
> > + * New userspace with a larger struct called an older kernel.
> > + * Write back ksize in host_caps.size so userspace knows which
> > + * older struct to retry with, then fail with -E2BIG.
> > + */
> > + if (usize > sizeof(host_caps)) {
> > + host_caps.size = sizeof(host_caps);
> > + r = -EFAULT;
> > + if (put_user(host_caps.size, (__u64 __user *)argp))
> > + goto out;
> > + r = -E2BIG;
> > + goto out;
> > + }
>
> [Severity: High]
> Does explicitly rejecting larger structs here bypass the standard UAPI
> zero-checking?
The pre-check `usize > sizeof(host_caps)` is required, not a bypass. The
-E2BIG path writes back ksize so userspace can discover the kernel's
size and retry explicitly. Userspace that retries with the smaller size
is making a deliberate choice to use the older interface; the kernel
cannot have seen the truncated extra fields, so there is nothing
silently ignored. copy_struct_from_user() itself would also return
-E2BIG for non-zero trailing bytes — but it cannot be called safely with
an arbitrary usize against a fixed-size kernel struct.
>
> By catching usize > sizeof(host_caps) early and returning -E2BIG, the code
> bypasses copy_struct_from_user()'s built-in validation which would safely
> reject non-zero extended fields.
>
> If userspace receives the smaller kernel size and retries by truncating its
> buffer size, the kernel will only read the truncated portion, silently
> ignoring any non-zero extended fields that userspace originally provided.
>
> Should this rely on copy_struct_from_user() to safely check zeroed trailing
> bytes instead?
>
> > +
> > + /*
> > + * copy_struct_from_user() handles forward/backward compat:
> > + * usize == ksize: verbatim copy
> > + * usize < ksize: zero-pad trailing (old userspace, new kernel)
> > + */
> > + r = copy_struct_from_user(&host_caps, sizeof(host_caps),
> > + argp, usize);
> > + if (r)
> > + goto out;
> > +
> > + /* Reserved fields must be zero */
> > + r = -EINVAL;
> > + if (host_caps.flags)
> > + goto out;
> > +
> > + r = -ENOTTY;
> > + if (!kvm->arch.kvm_ops->get_compat_caps)
> > + goto out;
> > +
> > + r = kvm->arch.kvm_ops->get_compat_caps(&host_caps);
> > + if (r)
> > + goto out;
> > +
> > + host_caps.size = sizeof(host_caps);
> > + r = copy_struct_to_user(argp, usize, &host_caps,
> > + sizeof(host_caps), NULL);
>
> [Severity: High]
> Can this artificial size inflation cause an out-of-bounds read/write if
> userspace reuses the struct?
>
> If an older userspace passes a smaller struct to a newer kernel (where
> usize < sizeof(host_caps)), the kernel sets host_caps.size to its own larger
> size. It then copies the smaller usize bytes back, overwriting the userspace
> size field with the kernel's larger size.
>
> If userspace reuses this struct for a subsequent ioctl call, it will now pass
> the larger size. The kernel would then read past the end of the originally
> allocated userspace buffer, and if successful, write back past the end
> as well.
Not a bug. `copy_struct_to_user(argp, usize, &host_caps,
sizeof(host_caps), NULL)` copies min(usize, ksize) bytes back to
userspace. Today usize == ksize == 24, so exactly 24 bytes are written.
In a future kernel where ksize grows, the function correctly copies only
usize bytes (old userspace case), meaning userspace sees its own size
field reflected back — not the kernel's larger size. The kernel sets
host_caps.size = sizeof(host_caps) in the kernel-side copy so that the
output `size` field documents the kernel's struct version when it is
within the copied range, which is the designed semantics for versioned
structs. No out-of-bounds access occurs.
>
> > + break;
> > + }
> > default: {
> > struct kvm *kvm = filp->private_data;
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260804180705.59160-1-amachhiw@linux.ibm.com?part=1
next prev parent reply other threads:[~2026-08-06 16:06 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-04 18:07 [PATCH v6 0/4] KVM: PPC: Expose CPU compatibility modes for nested guests Amit Machhiwal
2026-08-04 18:07 ` [PATCH v6 1/4] KVM: PPC: Introduce KVM_CAP_PPC_COMPAT_CAPS and wire up ioctl Amit Machhiwal
2026-08-04 18:19 ` sashiko-bot
2026-08-06 16:07 ` Amit Machhiwal [this message]
2026-08-04 18:07 ` [PATCH v6 2/4] KVM: PPC: Book3S HV: Implement compat CPU capability retrieval for KVM on PowerVM Amit Machhiwal
2026-08-04 18:21 ` sashiko-bot
2026-08-06 16:09 ` Amit Machhiwal
2026-08-04 18:07 ` [PATCH v6 3/4] KVM: PPC: Book3S HV: Add support for compat CPU capabilities for KVM on PowerNV Amit Machhiwal
2026-08-04 18:27 ` sashiko-bot
2026-08-06 16:13 ` Amit Machhiwal
2026-08-04 18:07 ` [PATCH v6 4/4] KVM: PPC: Document KVM_PPC_GET_COMPAT_CAPS ioctl Amit Machhiwal
2026-08-04 18:27 ` sashiko-bot
2026-08-06 16:15 ` Amit Machhiwal
2026-08-05 4:32 ` [PATCH v6 0/4] KVM: PPC: Expose CPU compatibility modes for nested guests Anushree Mathur
2026-08-05 18:39 ` Ritesh Harjani
2026-08-06 5:33 ` Amit Machhiwal
2026-08-06 13:05 ` Ritesh Harjani
2026-08-06 14:58 ` Amit Machhiwal
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=20260806211428.e98e021d-5c-amachhiw@linux.ibm.com \
--to=amachhiw@linux.ibm.com \
--cc=kvm@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox