All of lore.kernel.org
 help / color / mirror / Atom feed
From: Boqun Feng <boqun.feng@gmail.com>
To: Benno Lossin <lossin@kernel.org>
Cc: "Viresh Kumar" <viresh.kumar@linaro.org>,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	"Yury Norov" <yury.norov@gmail.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Vincent Guittot" <vincent.guittot@linaro.org>,
	linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org,
	rust-for-linux@vger.kernel.org
Subject: Re: [PATCH] rust: cpumask: Validate CPU number in set() and clear()
Date: Fri, 6 Jun 2025 06:34:56 -0700	[thread overview]
Message-ID: <aELugDefiviXZjx6@Mac.home> (raw)
In-Reply-To: <DAFAR5SUQSU9.OSLB2UAXE9DY@kernel.org>

On Fri, Jun 06, 2025 at 10:11:13AM +0200, Benno Lossin wrote:
> On Fri Jun 6, 2025 at 6:37 AM CEST, Boqun Feng wrote:
> > On Fri, Jun 06, 2025 at 09:47:28AM +0530, Viresh Kumar wrote:
> >> The C `cpumask_{set|clear}_cpu()` APIs emit a warning when given an
> >> invalid CPU number - but only if `CONFIG_DEBUG_PER_CPU_MAPS=y` is set.
> >> 
> >> Meanwhile, `cpumask_weight()` only considers CPUs up to `nr_cpu_ids`,
> >> which can cause inconsistencies: a CPU number greater than `nr_cpu_ids`
> >> may be set in the mask, yet the weight calculation won't reflect it.
> >> 
> >> This leads to doctest failures when `nr_cpu_ids < 4`, as the test tries
> >> to set CPUs 2 and 3:
> >> 
> >>   rust_doctest_kernel_cpumask_rs_0.location: rust/kernel/cpumask.rs:180
> >>   rust_doctest_kernel_cpumask_rs_0: ASSERTION FAILED at rust/kernel/cpumask.rs:190
> >> 
> >> Fix this by validating the CPU number in the Rust `set()` and `clear()`
> >> methods to prevent out-of-bounds modifications.
> >> 
> >
> > Thanks for the quick fix!
> >
> > While this can fix the current problem, but it's not a good solution for
> > the long run. Because outside a test, we should never use an arbitrary
> > i32 as a cpu number (we usually get it from smp_processor_id(), or
> > something else). So the `< nr_cpu_ids` testing is not necessary in
> > normal use cases.
> >
> > We should instead provide a wrapper for cpu id:
> >
> >     /// # Invariants
> >     ///
> >     /// The number is always in [0..nr_cpu_ids) range.
> >     pub struct CpuId(i32);
> >
> > and
> >
> >     impl CpuId {
> >         /// # Safety
> > 	/// Callers must ensure `i` is a valid cpu id (i.e. 0 <= i <
> > 	/// nr_cpu_ids).
> >         pub unsafe fn from_i32_unchecked(i: i32) -> Self {
> > 	    // INVARIANT: The function safety guarantees `i` is a valid
> > 	    // cpu id.
> > 	    CpuId(id);
> > 	}
> >
> > 	pub fn from_i32(i: i32) -> Option<Self> {
> > 	    if i < 0 || i >= nr_cpu_ids {
> > 	        None
> > 	    } else {
> > 	        // SAFETY: `i` has just been checked as a valid cpu id.
> > 	        Some(unsafe { Self::from_i32_unchecked(i) })
> > 	    }
> > 	}
> >
> > 	pub fn current() -> Self {
> > 	    // SAFETY: smp_processor_id() always return valid cpu id.
> > 	    unsafe { Self::from_i32_unchecked(smp_processor_id()) }
> > 	}
> >     }
> >
> > All `Cpumask` functions then take `CpuId` instead of `i32` as the
> > parameter. Needless to say if we were to have a cpumask_next() wrapper,
> > the return value will be `CpuId` (or `Option<CpuId>`), i.e. if a bit was
> > set in a cpumask, then it must represent a correct cpu id.
> >
> > Make sense?
> 
> Just to make sure, the `nr_cpu_ids` stays constant, right?
> 

Sort of. I believe the value won't be changed once the kernel boots, in
most cases (modulo NR_CPUS=1 or CONFIG_FORCE_NR_CPUS=y), it's a
read-mostly variable not a constant, and the value gets set by either a
kernel command line or how many CPUs the kernel actually detect at boot
time. See:

https://github.com/Rust-for-Linux/linux/blob/rust-next/kernel/smp.c#L995:w

> >> @@ -101,10 +108,16 @@ pub fn set(&mut self, cpu: u32) {
> >>      /// This mismatches kernel naming convention and corresponds to the C
> >>      /// function `__cpumask_clear_cpu()`.
> >>      #[inline]
> >> -    pub fn clear(&mut self, cpu: i32) {
> >> +    pub fn clear(&mut self, cpu: i32) -> Result {
> >> +        // SAFETY: It is safe to read `nr_cpu_ids`.
> >> +        if unsafe { cpu as u32 >= bindings::nr_cpu_ids } {
> >
> > You probably want to check whether `bindings::nr_cpu_ids` can be
> > accessible if NR_CPUS == 1 or CONFIG_FORCE_NR_CPUS=y, because then
> > nr_cpu_ids is a macro definition.
> 
> Just define a helper function?
> 

Maybe, but it is then "a variable read" vs "a FFI function call" if we
want to check every time in clear()/set(), of course if we only check it
in CpuId::from_i32() mentioned above, the performance impact shouldn't
be observable, because we won't call that method often.

Either, I was just pointing out the current fix may cause build errors.

Regards,
Boqun

> ---
> Cheers,
> Benno

  reply	other threads:[~2025-06-06 13:35 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-06  4:17 [PATCH] rust: cpumask: Validate CPU number in set() and clear() Viresh Kumar
2025-06-06  4:37 ` Boqun Feng
2025-06-06  8:11   ` Benno Lossin
2025-06-06 13:34     ` Boqun Feng [this message]
2025-06-06 19:34       ` Benno Lossin
2025-06-06 19:40         ` Boqun Feng
2025-06-09 11:17   ` Viresh Kumar
2025-06-06 10:11 ` Miguel Ojeda
2025-06-09  2:36   ` Viresh Kumar
2025-06-06 23:23 ` kernel test robot

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=aELugDefiviXZjx6@Mac.home \
    --to=boqun.feng@gmail.com \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rafael@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=vincent.guittot@linaro.org \
    --cc=viresh.kumar@linaro.org \
    --cc=yury.norov@gmail.com \
    /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.