public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
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

  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