rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Benno Lossin <benno.lossin@proton.me>
To: Alice Ryhl <aliceryhl@google.com>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Wedson Almeida Filho" <wedsonaf@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Andreas Hindborg" <a.hindborg@samsung.com>,
	"Marco Elver" <elver@google.com>,
	"Kees Cook" <keescook@chromium.org>, "Coly Li" <colyli@suse.de>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Pierre Gondois" <pierre.gondois@arm.com>,
	"Ingo Molnar" <mingo@kernel.org>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Wei Yang" <richard.weiyang@gmail.com>,
	"Matthew Wilcox" <willy@infradead.org>,
	linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org
Subject: Re: [PATCH 5/9] rust: list: add List
Date: Thu, 04 Apr 2024 14:51:16 +0000	[thread overview]
Message-ID: <3f3cf5ae-30df-4032-b752-77126035784c@proton.me> (raw)
In-Reply-To: <CAH5fLgiN3jcd_HMgmP7H-026daMw0xkMk=CGaGc9AM7uHkFXFA@mail.gmail.com>

On 04.04.24 16:12, Alice Ryhl wrote:
> On Thu, Apr 4, 2024 at 4:03 PM Benno Lossin <benno.lossin@proton.me> wrote:
>> On 02.04.24 14:17, Alice Ryhl wrote:
>>> +
>>> +        if self.first.is_null() {
>>> +            self.first = item;
>>> +            // SAFETY: The caller just gave us ownership of these fields.
>>> +            // INVARIANT: A linked list with one item should be cyclic.
>>> +            unsafe {
>>> +                (*item).next = item;
>>> +                (*item).prev = item;
>>> +            }
>>> +        } else {
>>> +            let next = self.first;
>>> +            // SAFETY: We just checked that `next` is non-null.
>>
>> Missing mention of the type invariant.
> 
> SAFETY: By the type invariant, this pointer is valid or null. We just
> checked that it's not null, so it must be valid.

Sounds good.

[...]

>>> +    /// Removes the first item from this list.
>>> +    pub fn pop_front(&mut self) -> Option<ListArc<T, ID>> {
>>> +        if self.first.is_null() {
>>> +            return None;
>>> +        }
>>> +
>>> +        // SAFETY: The first item of this list is in this list.
>>> +        Some(unsafe { self.remove_internal(self.first) })
>>> +    }
>>> +
>>> +    /// Removes the provided item from this list and returns it.
>>> +    ///
>>> +    /// This returns `None` if the item is not in the list.
>>
>> I think this should say "Returns `None` if the item is not in a list.".
>> (Technically it should be "is not in a `List<T, ID>`", since it *can* be
>> in another list with a different ID.)
> 
> I'm not really convinced. The phrases "the list" and "a list" are
> equivalent given the safety requirement for this method, but "the
> list" seems more natural to me. The `remove` method of any other
> collection would say "the list" too.

They are equivalent, but saying "the list" has the potential for this
confusion: "If the function returns `None` if the item is not in the
list, then why do I need to ensure that it is not in a different list?".
> 
>>> +    ///
>>> +    /// # Safety
>>> +    ///
>>> +    /// The provided item must not be in a different linked list.
>>> +    pub unsafe fn remove(&mut self, item: &T) -> Option<ListArc<T, ID>> {
>>> +        let mut item = unsafe { ListLinks::fields(T::view_links(item)) };
>>> +        // SAFETY: The user provided a reference, and reference are never dangling.
>>> +        //
>>> +        // As for why this is not a data race, there are two cases:
>>> +        //
>>> +        //  * If `item` is not in any list, then these fields are read-only and null.
>>> +        //  * If `item` is in this list, then we have exclusive access to these fields since we
>>> +        //    have a mutable reference to the list.
>>> +        //
>>> +        // In either case, there's no race.
>>> +        let ListLinksFields { next, prev } = unsafe { *item };
>>> +
>>> +        debug_assert_eq!(next.is_null(), prev.is_null());
>>> +        if !next.is_null() {
>>> +            // This is really a no-op, but this ensures that `item` is a raw pointer that was
>>> +            // obtained without going through a pointer->reference->pointer conversion rountrip.
>>> +            // This ensures that the list is valid under the more restrictive strict provenance
>>> +            // ruleset.
>>> +            //
>>> +            // SAFETY: We just checked that `next` is not null, and it's not dangling by the
>>> +            // list invariants.
>>> +            unsafe {
>>> +                debug_assert_eq!(item, (*next).prev);
>>> +                item = (*next).prev;
>>> +            }
>>> +
>>> +            // SAFETY: We just checked that `item` is in a list, so the caller guarantees that it
>>> +            // is in this list. The pointers are in the right order.
>>> +            Some(unsafe { self.remove_internal_inner(item, next, prev) })
>>> +        } else {
>>> +            None
>>> +        }
>>> +    }
>>> +
>>> +    /// Removes the provided item from the list.
>>> +    ///
>>> +    /// # Safety
>>> +    ///
>>> +    /// The pointer must point at an item in this list.
>>> +    unsafe fn remove_internal(&mut self, item: *mut ListLinksFields) -> ListArc<T, ID> {
>>> +        // SAFETY: The caller promises that this pointer is not dangling, and there's no data race
>>> +        // since we have a mutable reference to the list containing `item`.
>>> +        let ListLinksFields { next, prev } = unsafe { *item };
>>> +        // SAFETY: The pointers are ok and in the right order.
>>> +        unsafe { self.remove_internal_inner(item, next, prev) }
>>> +    }
>>> +
>>> +    /// Removes the provided item from the list.
>>> +    ///
>>> +    /// # Safety
>>> +    ///
>>> +    /// The `item` pointer must point at an item in this list, and we must have `(*item).next ==
>>> +    /// next` and `(*item).prev == prev`.
>>> +    unsafe fn remove_internal_inner(
>>> +        &mut self,
>>> +        item: *mut ListLinksFields,
>>> +        next: *mut ListLinksFields,
>>> +        prev: *mut ListLinksFields,
>>> +    ) -> ListArc<T, ID> {
>>> +        // SAFETY: We have exclusive access to items in the list, and prev/next pointers are
>>
>> I think you mean that you have exclusive access to the prev/next fields
>> of the `ListLinks` associated with `ID`... But that is rather long.
>> Does anyone have any idea to shorten this?
> 
> SAFETY: We have exclusive access to the pointers of items in the list,
> and the prev/next pointers are never null for items in a list.

I would say that they are valid instead of never null, since you
dereference them below. Otherwise sounds good.

> 
>>> +        // never null for items in a list.
>>> +        //
>>> +        // INVARIANT: There are three cases:
>>> +        //  * If the list has at least three items, then after removing the item, `prev` and `next`
>>> +        //    will be next to each other.
>>> +        //  * If the list has two items, then the remaining item will point at itself.
>>> +        //  * If the list has one item, then `next == prev == item`, so these writes have no effect
>>> +        //    due to the writes to `item` below.
>>
>> I think the writes do not have an effect. (no need to reference the
>> writes to `item` below)
> 
> ?

The first write is

     (*next).prev = prev;

Using the fact that `next == prev == item` we have

     (*item).prev = prev;

But that is already true, since the function requirement is that
`(*item).prev == prev`. So the write has no effect.
The same should hold for `(*prev).next = next`.

> 
>>> +        unsafe {
>>> +            (*next).prev = prev;
>>> +            (*prev).next = next;
>>> +        }
>>> +        // SAFETY: We have exclusive access to items in the list.
>>> +        // INVARIANT: The item is no longer in a list, so the pointers should be null.
>>> +        unsafe {
>>> +            (*item).prev = ptr::null_mut();
>>> +            (*item).next = ptr::null_mut();
>>> +        }
>>> +        // INVARIANT: There are three cases:
>>> +        //  * If `item` was not the first item, then `self.first` should remain unchanged.
>>> +        //  * If `item` was the first item and there is another item, then we just updated
>>> +        //    `prev->next` to `next`, which is the new first item, and setting `item->next` to null
>>> +        //    did not modify `prev->next`.
>>> +        //  * If `item` was the only item in the list, then `prev == item`, and we just set
>>> +        //    `item->next` to null, so this correctly sets `first` to null now that the list is
>>> +        //    empty.
>>> +        if self.first == item {
>>> +            // SAFETY: The `prev` field of an item in a list is never dangling.
>>
>> I don't think this SAFETY comment makes sense.
>>
>>> +            self.first = unsafe { (*prev).next };
> 
> SAFETY: `prev` is the `prev` pointer from a `ListLinks` in a `List`,
> so the pointer is valid. There's no race, since we have exclusive
> access to the list.

Sounds good.

-- 
Cheers,
Benno


  reply	other threads:[~2024-04-04 14:51 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-02 12:16 [PATCH 0/9] Add Rust linked list for reference counted values Alice Ryhl
2024-04-02 12:16 ` [PATCH 1/9] rust: list: add ListArc Alice Ryhl
2024-04-03 15:51   ` Benno Lossin
2024-04-04 14:00     ` Alice Ryhl
2024-05-03 14:36       ` Alice Ryhl
2024-05-04 15:50         ` Benno Lossin
2024-04-03 17:18   ` Kane York
2024-04-02 12:16 ` [PATCH 2/9] rust: list: add tracking for ListArc Alice Ryhl
2024-04-03 15:52   ` Benno Lossin
2024-04-04 14:14     ` Alice Ryhl
2024-04-04 14:41       ` Benno Lossin
2024-04-02 12:17 ` [PATCH 3/9] rust: list: add struct with prev/next pointers Alice Ryhl
2024-04-03 15:57   ` Benno Lossin
2024-04-04 14:03     ` Alice Ryhl
2024-04-04 14:05       ` Benno Lossin
2024-04-05  9:47   ` Benno Lossin
2024-04-08  7:57     ` Alice Ryhl
2024-04-02 12:17 ` [PATCH 4/9] rust: list: add macro for implementing ListItem Alice Ryhl
2024-04-03 10:42   ` Benno Lossin
2024-04-02 12:17 ` [PATCH 5/9] rust: list: add List Alice Ryhl
2024-04-04 14:03   ` Benno Lossin
2024-04-04 14:12     ` Alice Ryhl
2024-04-04 14:51       ` Benno Lossin [this message]
2024-04-08  8:04         ` Alice Ryhl
2024-04-08  8:51           ` Benno Lossin
2024-04-02 12:17 ` [PATCH 6/9] rust: list: add iterators Alice Ryhl
2024-04-04 14:36   ` Benno Lossin
2024-04-04 14:41     ` Alice Ryhl
2024-04-04 14:52       ` Benno Lossin
2024-04-08  8:00         ` Alice Ryhl
2024-04-02 12:17 ` [PATCH 7/9] rust: list: add cursor Alice Ryhl
2024-04-03 12:19   ` Benno Lossin
2024-04-03 12:49     ` Alice Ryhl
2024-04-04 13:28       ` Benno Lossin
2024-04-04 13:40         ` Alice Ryhl
2024-04-02 12:17 ` [PATCH 8/9] rust: list: support heterogeneous lists Alice Ryhl
2024-04-04 15:35   ` Benno Lossin
2024-04-08  7:59     ` Alice Ryhl
2024-04-02 12:17 ` [PATCH 9/9] rust: list: add ListArcField Alice Ryhl
2024-04-04 15:47   ` Benno Lossin

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=3f3cf5ae-30df-4032-b752-77126035784c@proton.me \
    --to=benno.lossin@proton.me \
    --cc=a.hindborg@samsung.com \
    --cc=akpm@linux-foundation.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=colyli@suse.de \
    --cc=elver@google.com \
    --cc=gary@garyguo.net \
    --cc=keescook@chromium.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pierre.gondois@arm.com \
    --cc=richard.weiyang@gmail.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=wedsonaf@gmail.com \
    --cc=willy@infradead.org \
    /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;
as well as URLs for NNTP newsgroup(s).