diff mbox series

[v7,7/7] rust: enable `clippy::ref_as_ptr` lint

Message ID 20250325-ptr-as-ptr-v7-7-87ab452147b9@gmail.com (mailing list archive)
State New
Headers show
Series rust: reduce `as` casts, enable related lints | expand

Commit Message

Tamir Duberstein March 25, 2025, 8:07 p.m. UTC
In Rust 1.78.0, Clippy introduced the `ref_as_ptr` lint [1]:

> Using `as` casts may result in silently changing mutability or type.

While this doesn't eliminate unchecked `as` conversions, it makes such
conversions easier to scrutinize.  It also has the slight benefit of
removing a degree of freedom on which to bikeshed. Thus apply the
changes and enable the lint -- no functional change intended.

Link: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr [1]
Suggested-by: Benno Lossin <benno.lossin@proton.me>
Link: https://lore.kernel.org/all/D8PGG7NTWB6U.3SS3A5LN4XWMN@proton.me/
Signed-off-by: Tamir Duberstein <tamird@gmail.com>
---
 Makefile                 | 1 +
 rust/bindings/lib.rs     | 1 +
 rust/kernel/device_id.rs | 3 ++-
 rust/kernel/fs/file.rs   | 3 ++-
 rust/kernel/str.rs       | 4 ++--
 rust/kernel/uaccess.rs   | 5 +++--
 rust/uapi/lib.rs         | 1 +
 7 files changed, 12 insertions(+), 6 deletions(-)

Comments

Benno Lossin March 25, 2025, 10:11 p.m. UTC | #1
On Tue Mar 25, 2025 at 9:07 PM CET, Tamir Duberstein wrote:
> diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> index 40034f77fc2f..6233af50bab7 100644
> --- a/rust/kernel/str.rs
> +++ b/rust/kernel/str.rs
> @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool {
>      #[inline]
>      pub const fn from_bytes(bytes: &[u8]) -> &Self {
>          // SAFETY: `BStr` is transparent to `[u8]`.
> -        unsafe { &*(bytes as *const [u8] as *const BStr) }
> +        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }

Hmm I'm not sure about using `transmute` here. Yes the types are
transparent, but I don't think that we should use it here.

>      }
>  
>      /// Strip a prefix from `self`. Delegates to [`slice::strip_prefix`].
> @@ -290,7 +290,7 @@ pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError
>      #[inline]
>      pub unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut CStr {
>          // SAFETY: Properties of `bytes` guaranteed by the safety precondition.
> -        unsafe { &mut *(bytes as *mut [u8] as *mut CStr) }
> +        unsafe { &mut *(core::mem::transmute::<*mut [u8], *mut Self>(bytes)) }
>      }
>  
>      /// Returns a C pointer to the string.
> diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
> index 80a9782b1c6e..c042b1fe499e 100644
> --- a/rust/kernel/uaccess.rs
> +++ b/rust/kernel/uaccess.rs
> @@ -242,7 +242,7 @@ pub fn read_raw(&mut self, out: &mut [MaybeUninit<u8>]) -> Result {
>      pub fn read_slice(&mut self, out: &mut [u8]) -> Result {
>          // SAFETY: The types are compatible and `read_raw` doesn't write uninitialized bytes to
>          // `out`.
> -        let out = unsafe { &mut *(out as *mut [u8] as *mut [MaybeUninit<u8>]) };
> +        let out = unsafe { &mut *(core::mem::transmute::<*mut [u8], *mut [MaybeUninit<u8>]>(out)) };

I have a patch that adds a `cast_slice_mut` method that could be used
here, so I can fix it in that series. But let's not use `transmute` here
either.

---
Cheers,
Benno

>          self.read_raw(out)
>      }
>
Tamir Duberstein March 25, 2025, 10:33 p.m. UTC | #2
On Tue, Mar 25, 2025 at 6:11 PM Benno Lossin <benno.lossin@proton.me> wrote:
>
> On Tue Mar 25, 2025 at 9:07 PM CET, Tamir Duberstein wrote:
> > diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> > index 40034f77fc2f..6233af50bab7 100644
> > --- a/rust/kernel/str.rs
> > +++ b/rust/kernel/str.rs
> > @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool {
> >      #[inline]
> >      pub const fn from_bytes(bytes: &[u8]) -> &Self {
> >          // SAFETY: `BStr` is transparent to `[u8]`.
> > -        unsafe { &*(bytes as *const [u8] as *const BStr) }
> > +        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }
>
> Hmm I'm not sure about using `transmute` here. Yes the types are
> transparent, but I don't think that we should use it here.

What's your suggestion? I initially tried

let bytes: *const [u8] = bytes;
unsafe { &*bytes.cast() }

but that doesn't compile because of the implicit Sized bound on pointer::cast.

>
> >      }
> >
> >      /// Strip a prefix from `self`. Delegates to [`slice::strip_prefix`].
> > @@ -290,7 +290,7 @@ pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError
> >      #[inline]
> >      pub unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut CStr {
> >          // SAFETY: Properties of `bytes` guaranteed by the safety precondition.
> > -        unsafe { &mut *(bytes as *mut [u8] as *mut CStr) }
> > +        unsafe { &mut *(core::mem::transmute::<*mut [u8], *mut Self>(bytes)) }
> >      }
> >
> >      /// Returns a C pointer to the string.
> > diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
> > index 80a9782b1c6e..c042b1fe499e 100644
> > --- a/rust/kernel/uaccess.rs
> > +++ b/rust/kernel/uaccess.rs
> > @@ -242,7 +242,7 @@ pub fn read_raw(&mut self, out: &mut [MaybeUninit<u8>]) -> Result {
> >      pub fn read_slice(&mut self, out: &mut [u8]) -> Result {
> >          // SAFETY: The types are compatible and `read_raw` doesn't write uninitialized bytes to
> >          // `out`.
> > -        let out = unsafe { &mut *(out as *mut [u8] as *mut [MaybeUninit<u8>]) };
> > +        let out = unsafe { &mut *(core::mem::transmute::<*mut [u8], *mut [MaybeUninit<u8>]>(out)) };
>
> I have a patch that adds a `cast_slice_mut` method that could be used
> here, so I can fix it in that series. But let's not use `transmute` here
> either.

See above - I don't know what else I could write here.
Benno Lossin March 25, 2025, 10:40 p.m. UTC | #3
On Tue Mar 25, 2025 at 11:33 PM CET, Tamir Duberstein wrote:
> On Tue, Mar 25, 2025 at 6:11 PM Benno Lossin <benno.lossin@proton.me> wrote:
>>
>> On Tue Mar 25, 2025 at 9:07 PM CET, Tamir Duberstein wrote:
>> > diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
>> > index 40034f77fc2f..6233af50bab7 100644
>> > --- a/rust/kernel/str.rs
>> > +++ b/rust/kernel/str.rs
>> > @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool {
>> >      #[inline]
>> >      pub const fn from_bytes(bytes: &[u8]) -> &Self {
>> >          // SAFETY: `BStr` is transparent to `[u8]`.
>> > -        unsafe { &*(bytes as *const [u8] as *const BStr) }
>> > +        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }
>>
>> Hmm I'm not sure about using `transmute` here. Yes the types are
>> transparent, but I don't think that we should use it here.
>
> What's your suggestion? I initially tried
>
> let bytes: *const [u8] = bytes;
> unsafe { &*bytes.cast() }
>
> but that doesn't compile because of the implicit Sized bound on pointer::cast.

This is AFAIK one of the only places where we cannot get rid of the `as`
cast. So:

    let bytes: *const [u8] = bytes;
    // CAST: `BStr` transparently wraps `[u8]`.
    let bytes = bytes as *const BStr;
    // SAFETY: `bytes` is derived from a reference.
    unsafe { &*bytes }

IMO a `transmute` is worse than an `as` cast :)

---
Cheers,
Benno
Tamir Duberstein March 25, 2025, 11:54 p.m. UTC | #4
On Tue, Mar 25, 2025 at 6:40 PM Benno Lossin <benno.lossin@proton.me> wrote:
>
> On Tue Mar 25, 2025 at 11:33 PM CET, Tamir Duberstein wrote:
> > On Tue, Mar 25, 2025 at 6:11 PM Benno Lossin <benno.lossin@proton.me> wrote:
> >>
> >> On Tue Mar 25, 2025 at 9:07 PM CET, Tamir Duberstein wrote:
> >> > diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> >> > index 40034f77fc2f..6233af50bab7 100644
> >> > --- a/rust/kernel/str.rs
> >> > +++ b/rust/kernel/str.rs
> >> > @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool {
> >> >      #[inline]
> >> >      pub const fn from_bytes(bytes: &[u8]) -> &Self {
> >> >          // SAFETY: `BStr` is transparent to `[u8]`.
> >> > -        unsafe { &*(bytes as *const [u8] as *const BStr) }
> >> > +        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }
> >>
> >> Hmm I'm not sure about using `transmute` here. Yes the types are
> >> transparent, but I don't think that we should use it here.
> >
> > What's your suggestion? I initially tried
> >
> > let bytes: *const [u8] = bytes;
> > unsafe { &*bytes.cast() }
> >
> > but that doesn't compile because of the implicit Sized bound on pointer::cast.
>
> This is AFAIK one of the only places where we cannot get rid of the `as`
> cast. So:
>
>     let bytes: *const [u8] = bytes;
>     // CAST: `BStr` transparently wraps `[u8]`.
>     let bytes = bytes as *const BStr;
>     // SAFETY: `bytes` is derived from a reference.
>     unsafe { &*bytes }
>
> IMO a `transmute` is worse than an `as` cast :)

Hmm, looking at this again we can just transmute ref-to-ref and avoid
pointers entirely. We're already doing that in
`CStr::from_bytes_with_nul_unchecked`

Why is transmute worse than an `as` cast?
Benno Lossin March 26, 2025, 10:30 a.m. UTC | #5
On Wed Mar 26, 2025 at 12:54 AM CET, Tamir Duberstein wrote:
> On Tue, Mar 25, 2025 at 6:40 PM Benno Lossin <benno.lossin@proton.me> wrote:
>> On Tue Mar 25, 2025 at 11:33 PM CET, Tamir Duberstein wrote:
>> > On Tue, Mar 25, 2025 at 6:11 PM Benno Lossin <benno.lossin@proton.me> wrote:
>> >> On Tue Mar 25, 2025 at 9:07 PM CET, Tamir Duberstein wrote:
>> >> > diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
>> >> > index 40034f77fc2f..6233af50bab7 100644
>> >> > --- a/rust/kernel/str.rs
>> >> > +++ b/rust/kernel/str.rs
>> >> > @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool {
>> >> >      #[inline]
>> >> >      pub const fn from_bytes(bytes: &[u8]) -> &Self {
>> >> >          // SAFETY: `BStr` is transparent to `[u8]`.
>> >> > -        unsafe { &*(bytes as *const [u8] as *const BStr) }
>> >> > +        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }
>> >>
>> >> Hmm I'm not sure about using `transmute` here. Yes the types are
>> >> transparent, but I don't think that we should use it here.
>> >
>> > What's your suggestion? I initially tried
>> >
>> > let bytes: *const [u8] = bytes;
>> > unsafe { &*bytes.cast() }
>> >
>> > but that doesn't compile because of the implicit Sized bound on pointer::cast.
>>
>> This is AFAIK one of the only places where we cannot get rid of the `as`
>> cast. So:
>>
>>     let bytes: *const [u8] = bytes;
>>     // CAST: `BStr` transparently wraps `[u8]`.
>>     let bytes = bytes as *const BStr;
>>     // SAFETY: `bytes` is derived from a reference.
>>     unsafe { &*bytes }
>>
>> IMO a `transmute` is worse than an `as` cast :)
>
> Hmm, looking at this again we can just transmute ref-to-ref and avoid
> pointers entirely. We're already doing that in
> `CStr::from_bytes_with_nul_unchecked`
>
> Why is transmute worse than an `as` cast?

It's right in the docs: "`transmute` should be the absolute last
resort." [1]. IIRC, Gary was a bit more lenient in its use, but I think
we should avoid it as much as possible such that people copying code or
taking inspiration also don't use it.

So for both cases I'd prefer an `as` cast.

[1]: https://doc.rust-lang.org/std/mem/fn.transmute.html

---
Cheers,
Benno
Tamir Duberstein March 26, 2025, 10:35 a.m. UTC | #6
On Wed, Mar 26, 2025 at 6:31 AM Benno Lossin <benno.lossin@proton.me> wrote:
>
> On Wed Mar 26, 2025 at 12:54 AM CET, Tamir Duberstein wrote:
> > On Tue, Mar 25, 2025 at 6:40 PM Benno Lossin <benno.lossin@proton.me> wrote:
> >> On Tue Mar 25, 2025 at 11:33 PM CET, Tamir Duberstein wrote:
> >> > On Tue, Mar 25, 2025 at 6:11 PM Benno Lossin <benno.lossin@proton.me> wrote:
> >> >> On Tue Mar 25, 2025 at 9:07 PM CET, Tamir Duberstein wrote:
> >> >> > diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
> >> >> > index 40034f77fc2f..6233af50bab7 100644
> >> >> > --- a/rust/kernel/str.rs
> >> >> > +++ b/rust/kernel/str.rs
> >> >> > @@ -29,7 +29,7 @@ pub const fn is_empty(&self) -> bool {
> >> >> >      #[inline]
> >> >> >      pub const fn from_bytes(bytes: &[u8]) -> &Self {
> >> >> >          // SAFETY: `BStr` is transparent to `[u8]`.
> >> >> > -        unsafe { &*(bytes as *const [u8] as *const BStr) }
> >> >> > +        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }
> >> >>
> >> >> Hmm I'm not sure about using `transmute` here. Yes the types are
> >> >> transparent, but I don't think that we should use it here.
> >> >
> >> > What's your suggestion? I initially tried
> >> >
> >> > let bytes: *const [u8] = bytes;
> >> > unsafe { &*bytes.cast() }
> >> >
> >> > but that doesn't compile because of the implicit Sized bound on pointer::cast.
> >>
> >> This is AFAIK one of the only places where we cannot get rid of the `as`
> >> cast. So:
> >>
> >>     let bytes: *const [u8] = bytes;
> >>     // CAST: `BStr` transparently wraps `[u8]`.
> >>     let bytes = bytes as *const BStr;
> >>     // SAFETY: `bytes` is derived from a reference.
> >>     unsafe { &*bytes }
> >>
> >> IMO a `transmute` is worse than an `as` cast :)
> >
> > Hmm, looking at this again we can just transmute ref-to-ref and avoid
> > pointers entirely. We're already doing that in
> > `CStr::from_bytes_with_nul_unchecked`
> >
> > Why is transmute worse than an `as` cast?
>
> It's right in the docs: "`transmute` should be the absolute last
> resort." [1]. IIRC, Gary was a bit more lenient in its use, but I think
> we should avoid it as much as possible such that people copying code or
> taking inspiration also don't use it.
>
> So for both cases I'd prefer an `as` cast.
>
> [1]: https://doc.rust-lang.org/std/mem/fn.transmute.html

I don't follow the logic. The trouble with `as` casts is that they are
very lenient in what they allow, and to do these conversions with `as`
casts requires ref -> pointer -> pointer -> pointer deref versus a
single transmute. The safety comment perfectly describes why it's OK
to do: the types are transparent. So why is `as` casting pointers
better? It's just as unchecked as transmuting, and worse, it requires
a raw pointer dereference.
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 2e9eca8b7671..081b5fa2aa81 100644
--- a/Makefile
+++ b/Makefile
@@ -488,6 +488,7 @@  export rust_common_flags := --edition=2021 \
 			    -Wclippy::no_mangle_with_rust_abi \
 			    -Wclippy::ptr_as_ptr \
 			    -Wclippy::ptr_cast_constness \
+			    -Wclippy::ref_as_ptr \
 			    -Wclippy::undocumented_unsafe_blocks \
 			    -Wclippy::unnecessary_safety_comment \
 			    -Wclippy::unnecessary_safety_doc \
diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs
index b105a0d899cc..2b69016070c6 100644
--- a/rust/bindings/lib.rs
+++ b/rust/bindings/lib.rs
@@ -27,6 +27,7 @@ 
 #[allow(dead_code)]
 #[allow(clippy::cast_lossless)]
 #[allow(clippy::ptr_as_ptr)]
+#[allow(clippy::ref_as_ptr)]
 #[allow(clippy::undocumented_unsafe_blocks)]
 mod bindings_raw {
     // Manual definition for blocklisted types.
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index 4063f09d76d9..37cc03d1df4c 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -136,7 +136,8 @@  impl<T: RawDeviceId, U, const N: usize> IdTable<T, U> for IdArray<T, U, N> {
     fn as_ptr(&self) -> *const T::RawType {
         // This cannot be `self.ids.as_ptr()`, as the return pointer must have correct provenance
         // to access the sentinel.
-        (self as *const Self).cast()
+        let this: *const Self = self;
+        this.cast()
     }
 
     fn id(&self, index: usize) -> &T::RawType {
diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs
index 9e2639aee61a..366e23dc0803 100644
--- a/rust/kernel/fs/file.rs
+++ b/rust/kernel/fs/file.rs
@@ -359,12 +359,13 @@  impl core::ops::Deref for File {
     type Target = LocalFile;
     #[inline]
     fn deref(&self) -> &LocalFile {
+        let this: *const Self = self;
         // SAFETY: The caller provides a `&File`, and since it is a reference, it must point at a
         // valid file for the desired duration.
         //
         // By the type invariants, there are no `fdget_pos` calls that did not take the
         // `f_pos_lock` mutex.
-        unsafe { LocalFile::from_raw_file((self as *const Self).cast()) }
+        unsafe { LocalFile::from_raw_file(this.cast()) }
     }
 }
 
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index 40034f77fc2f..6233af50bab7 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -29,7 +29,7 @@  pub const fn is_empty(&self) -> bool {
     #[inline]
     pub const fn from_bytes(bytes: &[u8]) -> &Self {
         // SAFETY: `BStr` is transparent to `[u8]`.
-        unsafe { &*(bytes as *const [u8] as *const BStr) }
+        unsafe { &*(core::mem::transmute::<*const [u8], *const Self>(bytes)) }
     }
 
     /// Strip a prefix from `self`. Delegates to [`slice::strip_prefix`].
@@ -290,7 +290,7 @@  pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, CStrConvertError
     #[inline]
     pub unsafe fn from_bytes_with_nul_unchecked_mut(bytes: &mut [u8]) -> &mut CStr {
         // SAFETY: Properties of `bytes` guaranteed by the safety precondition.
-        unsafe { &mut *(bytes as *mut [u8] as *mut CStr) }
+        unsafe { &mut *(core::mem::transmute::<*mut [u8], *mut Self>(bytes)) }
     }
 
     /// Returns a C pointer to the string.
diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
index 80a9782b1c6e..c042b1fe499e 100644
--- a/rust/kernel/uaccess.rs
+++ b/rust/kernel/uaccess.rs
@@ -242,7 +242,7 @@  pub fn read_raw(&mut self, out: &mut [MaybeUninit<u8>]) -> Result {
     pub fn read_slice(&mut self, out: &mut [u8]) -> Result {
         // SAFETY: The types are compatible and `read_raw` doesn't write uninitialized bytes to
         // `out`.
-        let out = unsafe { &mut *(out as *mut [u8] as *mut [MaybeUninit<u8>]) };
+        let out = unsafe { &mut *(core::mem::transmute::<*mut [u8], *mut [MaybeUninit<u8>]>(out)) };
         self.read_raw(out)
     }
 
@@ -348,6 +348,7 @@  pub fn write<T: AsBytes>(&mut self, value: &T) -> Result {
         if len > self.length {
             return Err(EFAULT);
         }
+        let value: *const T = value;
         // SAFETY: The reference points to a value of type `T`, so it is valid for reading
         // `size_of::<T>()` bytes.
         //
@@ -357,7 +358,7 @@  pub fn write<T: AsBytes>(&mut self, value: &T) -> Result {
         let res = unsafe {
             bindings::_copy_to_user(
                 self.ptr as *mut c_void,
-                (value as *const T).cast::<c_void>(),
+                value.cast::<c_void>(),
                 len,
             )
         };
diff --git a/rust/uapi/lib.rs b/rust/uapi/lib.rs
index d5dab4dfabec..6230ba48201d 100644
--- a/rust/uapi/lib.rs
+++ b/rust/uapi/lib.rs
@@ -16,6 +16,7 @@ 
     clippy::all,
     clippy::cast_lossless,
     clippy::ptr_as_ptr,
+    clippy::ref_as_ptr,
     clippy::undocumented_unsafe_blocks,
     dead_code,
     missing_docs,