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
next prev parent 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).