From: Christian Schrefl <chrisi.schrefl@gmail.com>
To: Alice Ryhl <aliceryhl@google.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Arnd Bergmann" <arnd@arndb.de>, "Lee Jones" <lee@kernel.org>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/3] rust: miscdevice: Add additional data to MiscDeviceRegistration
Date: Thu, 23 Jan 2025 11:02:56 +0100 [thread overview]
Message-ID: <bc096b1c-2e33-4234-a0ac-081b05d8cd7f@gmail.com> (raw)
In-Reply-To: <CAH5fLggZ-7Rb=98JF+KkieY_H13mRVLB+T1umOrQib3ah78M-Q@mail.gmail.com>
On 22.01.25 2:06 PM, Alice Ryhl wrote:
> On Wed, Jan 22, 2025 at 1:40 PM Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
>>
>> On Wed, Jan 22, 2025 at 11:11:07AM +0100, Alice Ryhl wrote:
>>> On Wed, Jan 22, 2025 at 10:28 AM Greg Kroah-Hartman
>>> <gregkh@linuxfoundation.org> wrote:
>>>>
>>>> On Sun, Jan 19, 2025 at 11:11:14PM +0100, Christian Schrefl wrote:
>>>>> When using the Rust miscdevice bindings, you generally embed the
>>>>> MiscDeviceRegistration within another struct:
>>>>>
>>>>> struct MyDriverData {
>>>>> data: SomeOtherData,
>>>>> misc: MiscDeviceRegistration<MyMiscFile>
>>>>> }
>>>>>
>>>>> In the `fops->open` callback of the miscdevice, you are given a
>>>>> reference to the registration, which allows you to access its fields.
>>>>> For example, as of commit 284ae0be4dca ("rust: miscdevice: Provide
>>>>> accessor to pull out miscdevice::this_device") you can access the
>>>>> internal `struct device`. However, there is still no way to access the
>>>>> `data` field in the above example, because you only have a reference to
>>>>> the registration.
>>>>
>>>> What's wrong with the driver_data pointer in the misc device structure?
>>>> Shouldn't you be in control of that as you are a misc driver owner? Or
>>>> does the misc core handle this I can't recall at the moment, sorry.
>>>
>>> There's no such pointer in struct miscdevice?
>>
>> struct miscdevice->struct device->driver_data;
>>
>> But in digging, this might not be "allowed" for a misc driver to do, I
>> can't remember anymore, sorry.
>>
>>>>> Using container_of is also not possible to do safely. For example, if
>>>>> the destructor of `MyDriverData` runs, then the destructor of `data`
>>>>> would run before the miscdevice is deregistered, so using container_of
>>>>> to access `data` from `fops->open` could result in a UAF. A similar
>>>>> problem can happen on initialization if `misc` is not the last field to
>>>>> be initialized.
>>>>>
>>>>> To provide a safe way to access user-defined data stored next to the
>>>>> `struct miscdevice`, make `MiscDeviceRegistration` into a container that
>>>>> can store a user-provided piece of data. This way, `fops->open` can
>>>>> access that data via the registration, since the data is stored inside
>>>>> the registration.
>>>>
>>>> "next to" feels odd, that's what a container_of is for, but be careful
>>>> as to who owns the lifecycle of the object you are trying to get to.
>>>> You can't have multiple objects with different lifecycles in the same
>>>> structure (i.e. don't mix a misc device and a platform device together).
>>>
>>> Ultimately, this is intended to solve the problem that container_of
>>> solves in C. Actually using container_of runs into the challenge that
>>> you have no way of knowing if those other fields are still valid.
>>
>> You "know" they are valid if you have a pointer to your structure,
>> right? Someone sent that to you, so by virtue of that it has to be
>> valid.
>>
>>> If
>>> the destructor of the struct starts running, then it might have
>>> destroyed some of the fields, but not yet have destroyed the
>>> miscdevice field. Since you can't know if the rest of the struct is
>>> still valid, it's not safe to use container_of.
>>
>> That's a different issue, that's a lifetime issue of "can I look at
>> these other fields at this point in time and trust them", it's not a
>> "these are not valid thing to look at".
>
> The memory containing the field can't have been deallocated yet, but
> if we provide a safe way to get access to those fields after their
> destructor runs, then that's still a problem. If there's a field of
> type `KBox<MyType>`, i.e. a pointer to a kmalloc allocation, then the
> destructor will have freed that allocation. We cannot let you access
> the field that holds the KBox after that happens because the pointer
> will be dangling.
>
>>> Storing those other fields within the registration solves this issue.
>>> The registration ensures that the miscdevice is deregistered before
>>> the `data` field is destroyed. This means that if it's safe to access
>>> the miscdevice field, then it's also safe to access the `data` field,
>>> and that's the guarantee we need.
>>
>> I'm confused. A misc device should be embedded inside of something
>> else. And the misc device controls the lifespan of that "something
>> else" structure, right? So by virtue of the misc device being alive,
>> everything else in there should be ok.
>
> This patch proposes that the way you write
>
> struct MyDriverData {
> // lifetime of these fields is controlled by misc
> field1: Foo,
> field2: Bar,
> misc: MiscDeviceRegistration<MyMiscFile>
> }
>
> is
>
> struct MyDriverData {
> misc: MiscDeviceRegistration<MyMiscFile, Inner>
> }
> struct Inner {
> // lifetime of these fields is controlled by misc
> field1: Foo,
> field2: Bar,
> }
>
> in memory all three fields gets laid out next to each other.
>
>> Now individual misc devices might want to do different things but if
>> they are being torn down, that means all references are dropped so any
>> "container_of()" like cast-back should not even be possible as there
>> should not be a pointer that you can cast-back from here.
>>
>> Or am I confused? I think a real example would be good to see here.
>
> Christian, do you mind sharing the example you showed to me?
Sure
#[pin_data(PinnedDrop)]
struct LedPwmDriver {
pdev: platform::Device,
mapping: Devres<IoMem<MAPPING_SIZE, true>>,
#[pin]
miscdev: MiscDeviceRegistration<RustMiscDevice>,
}
impl platform::Driver for LedPwmDriver {
type IdInfo = ();
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
fn probe(pdev: &mut platform::Device, info: Option<&Self::IdInfo>) -> Result<Pin<KBox<Self>>> {
dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
if info.is_some() {
dev_info!(pdev.as_ref(), "Probed with info\n");
}
let mapping = pdev
.ioremap_resource_sized::<MAPPING_SIZE, true>(pdev.resource(0).ok_or(ENXIO)?)
.map_err(|_| ENXIO)?;
// Initialize the HW
mapping.try_access().ok_or(ENXIO)?.writel(0xFF, 0x0);
let options = MiscDeviceOptions { name: c_str!("led0") };
let drvdata = KBox::try_pin_init(
try_pin_init!(Self {
pdev: pdev.clone(),
mapping,
miscdev <- MiscDeviceRegistration::register(options),
}),
GFP_KERNEL,
)?;
Ok(drvdata)
}
}
#[pinned_drop]
impl PinnedDrop for LedPwmDriver {
fn drop(self: Pin<&mut Self>) {
dev_info!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
if let Some(res) = self.mapping.try_access() {
res.writel(0x00, 0x0);
}
}
}
#[pin_data(PinnedDrop)]
struct RustMiscDevice {
dev: ARef<Device>,
mapping: &'static Devres<IoMem<MAPPING_SIZE, true>>,
}
#[vtable]
impl MiscDevice for RustMiscDevice {
type Ptr = Pin<KBox<Self>>;
fn open(_file: &File, misc: &MiscDeviceRegistration<Self>) -> Result<Pin<KBox<Self>>> {
let dev = ARef::from(misc.device());
// SAFETY:
// Stuff?
let ledpwm = unsafe { &*container_of!(misc, LedPwmDriver, miscdev) };
dev_info!(dev, "Opening Rust Misc Device Sample\n");
let res = ledpwm.mapping.try_access().ok_or(ENXIO)?;
res.writel(0x80, 0x0);
KBox::try_pin_init(
try_pin_init! {
RustMiscDevice {
dev: dev,
mapping: &ledpwm.mapping
}
},
GFP_KERNEL,
)
}
}
#[pinned_drop]
impl PinnedDrop for RustMiscDevice {
fn drop(self: Pin<&mut Self>) {
dev_info!(self.dev, "Exiting the Rust Misc Device Sample\n");
if let Some(res) = self.mapping.try_access() {
res.writel(0x10, 0x0);
}
}
}
I removed or simplified many irrelevant parts, the full driver can be found here:
https://github.com/onestacked/linux/blob/c9e2ef858e4537d33625cdf2b5d20ef4acfa9424/drivers/misc/ledpwm.rs
This driver was only an attempt to the assignment of a University course in Rust mostly to see if was
possible or which abstractions are missing.
The driver using the patch can be found here:
https://github.com/onestacked/linux/blob/rust_ledpwm_driver/drivers/misc/ledpwm.rs
Cheers
Christian
next prev parent reply other threads:[~2025-01-23 10:02 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-01-19 22:11 [PATCH 0/3] rust: miscdevice: Add additional data to MiscDeviceRegistration Christian Schrefl
2025-01-19 22:11 ` [PATCH 1/3] rust: add Aliased type Christian Schrefl
2025-01-19 23:04 ` Miguel Ojeda
2025-01-20 0:27 ` Christian Schrefl
2025-01-20 17:24 ` Boqun Feng
2025-01-23 10:21 ` Christian Schrefl
2025-01-23 17:56 ` Boqun Feng
2025-01-23 18:04 ` Christian Schrefl
2025-01-23 18:25 ` Boqun Feng
2025-01-23 20:18 ` Christian Schrefl
2025-01-23 20:24 ` Boqun Feng
2025-01-23 20:27 ` Christian Schrefl
2025-01-19 22:11 ` [PATCH 2/3] rust: miscdevice: Add additional data to MiscDeviceRegistration Christian Schrefl
2025-01-20 0:27 ` Christian Schrefl
2025-01-21 10:53 ` kernel test robot
2025-01-22 9:28 ` Greg Kroah-Hartman
2025-01-22 10:11 ` Alice Ryhl
2025-01-22 12:40 ` Greg Kroah-Hartman
2025-01-22 13:06 ` Alice Ryhl
2025-01-23 10:02 ` Christian Schrefl [this message]
2025-01-23 15:52 ` Christian Schrefl
2025-01-23 16:00 ` Greg Kroah-Hartman
2025-01-23 16:04 ` Christian Schrefl
2025-01-23 23:26 ` Christian Schrefl
2025-01-27 10:27 ` Alice Ryhl
2025-01-27 13:27 ` Christian Schrefl
2025-01-27 13:33 ` Alice Ryhl
2025-01-27 13:35 ` Christian Schrefl
2025-01-27 13:42 ` Miguel Ojeda
2025-01-19 22:11 ` [PATCH 3/3] rust: miscdevice: adjust the rust_misc_device sample to use RegistrationData Christian Schrefl
2025-01-21 15:40 ` Alice Ryhl
2025-01-23 17:57 ` Christian Schrefl
2025-01-24 7:29 ` Alice Ryhl
2025-01-24 8:06 ` Greg Kroah-Hartman
2025-01-24 9:42 ` Alice Ryhl
2025-01-24 10:34 ` Greg Kroah-Hartman
2025-01-24 10:39 ` Alice Ryhl
2025-01-24 11:22 ` Greg Kroah-Hartman
2025-01-24 11:37 ` Alice Ryhl
2025-01-24 11:42 ` Christian Schrefl
2025-01-20 5:46 ` [PATCH 0/3] rust: miscdevice: Add additional data to MiscDeviceRegistration Greg Kroah-Hartman
2025-01-21 10:29 ` Christian Schrefl
2025-01-22 9:22 ` Greg Kroah-Hartman
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=bc096b1c-2e33-4234-a0ac-081b05d8cd7f@gmail.com \
--to=chrisi.schrefl@gmail.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=arnd@arndb.de \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=lee@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/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