Kernel KVM virtualization development
 help / color / mirror / Atom feed
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

  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