From: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>, Peter Zijlstra <peterz@infradead.org>
Cc: "Boqun Feng" <boqun@kernel.org>,
"Greg KH" <gregkh@linuxfoundation.org>,
"Lorenzo Stoakes" <lorenzo.stoakes@oracle.com>,
"Liam R. Howlett" <Liam.Howlett@oracle.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Will Deacon" <will@kernel.org>,
"Mark Rutland" <mark.rutland@arm.com>,
linux-mm@kvack.org, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] rust: page: add byte-wise atomic memory copy methods
Date: Tue, 17 Feb 2026 14:56:40 +0100 [thread overview]
Message-ID: <87ecmjcw2v.fsf@kernel.org> (raw)
In-Reply-To: <aZRWOIPThLfIZ1Ph@google.com>
"Alice Ryhl" <aliceryhl@google.com> writes:
> On Tue, Feb 17, 2026 at 12:09:11PM +0100, Peter Zijlstra wrote:
>> On Tue, Feb 17, 2026 at 10:47:03AM +0000, Alice Ryhl wrote:
>> > > > // OK!
>> > > > unsigned long *a, b;
>> > > > b = READ_ONCE(a);
>> > > > if is_valid(b) {
>> > > > // do stuff
>> > > > }
>> > > >
>> > > > Now consider the following code:
>> > > >
>> > > > // Is this ok?
>> > > > unsigned long *a, b;
>> > > > memcpy(a, &b, sizeof(unsigned long));
>> > > > if is_valid(b) {
>> > > > // do stuff
>> > > > }
>> > >
>> > > Why the hell would you want to write that? But sure. I think similar but
>> > > less weird example would be with structures, where value copies end up
>> > > being similar to memcpy.
>> >
>> > I mean sure, let's say that it was a structure or whatever instead of a
>> > long. The point is that the general pattern of memcpy, then checking the
>> > bytes you copied, then use the bytes you copied, is potentially
>> > susceptible to this exacty optimization.
>>
>> > > And in that case, you can still use volatile and compiler must not do
>> > > silly.
>> >
>> > What you mean by "volatile" here is the same as what this patch means
>> > when it says "per-byte atomic". If you agree that a "volatile memcpy"
>> > would be a good idea to use in this scenario, then it sounds like you
>> > agree with the patch except for its naming / terminology.
>>
>> struct foo {
>> int a, b;
>> };
>>
>> struct foo *ptr, val;
>>
>> val = *(volatile struct foo *)ptr;
>>
>> why would we need a an explicit new memcpy for this?
>
> In my experience with dealing with `struct page` that is mapped into a
> vma, you need memcpy because the struct might be split across two
> different pages in the vma. The pages are adjacent in userspace's
> address space, but not necessarily adjacent from the kernel's POV.
>
> So you might end up with something that looks like this:
>
> struct foo val;
> void *ptr1 = kmap_local_page(p1);
> void *ptr2 = kmap_local_page(p2);
> memcpy(ptr1 + offset, val, PAGE_SIZE - offset);
> memcpy(ptr2, val + offset, sizeof(struct foo) - (PAGE_SIZE - offset));
> kunmap_local(ptr2);
> kunmap_local(ptr1);
>
> if (is_valid(&val)) {
> // use val
> }
>
> This exact thing happens in Binder. It has to be a memcpy.
>
>> > > So I'm still not exactly sure why this is a problem all of a sudden?
>> >
>> > I mean, this is for `struct page` specifically. If you have the struct
>> > page for a page that might also be mapped into a userspace vma, then the
>> > way to perform a "copy_from_user" operation is to:
>> >
>> > 1. kmap_local_page()
>> > 2. memcpy()
>> > 3. kunmap_local()
>> >
>> > Correct me if I'm wrong, but my understanding is that on 64-bit systems,
>> > kmap/kunmap are usually complete no-ops since you have enough address
>> > space to simply map all pages into the kernel's address space. Not even
>> > a barrier - just a `static inline` with an empty body.
>>
>> That is all correct -- however that cannot be all you do.
>>
>> Any shared memory will involved memory barriers of a sort. You cannot
>> just memcpy() and think you're done.
>>
>> So yeah, on x86_64 those 1,2,3 are insufficient to inhibit the re-load,
>> but nobody should ever just do 1,2,3 and think job-done. There must
>> always be more.
>>
>> If it is a ring-buffer like thing, you get:
>>
>> * if (LOAD ->data_tail) { LOAD ->data_head
>> * (A) smp_rmb() (C)
>> * STORE $data LOAD $data
>> * smp_wmb() (B) smp_mb() (D)
>> * STORE ->data_head STORE ->data_tail
>> * }
>>
>> if it is a seqlock like thing you get that.
>>
>> If it is DMA, you need dma fences.
>>
>> And the moment you use any of that, the re-load goes out the window.
>
> I don't know how Andreas is using this, but the usage pattern I'm
> familiar with for `struct page` from my work on Binder is this one:
>
> 1. memcpy into the page
> 2. return from ioctl
> 3. userspace reads from vma
>
> or
>
> 1. userspace writes to vma
> 2. call ioctl
> 3. kernel reads from page
>
> which needs no barriers whatsoever. There is nothing to prevent this
> kind of optimization in this kind of code, so an evil userspace could
> trigger TOCTOU bugs in the kernel that are not present in the source
> code if the code was optimized like I described.
I'm processing disk IO in the Rust null block driver. The pages backing
the IO requests may be simultaneously mapped to user space, so there is
no way to guarantee that there is no concurrent memory operation to/from
the memory area. User space programs can do whatever.
I don't have any control flow depending on the data I copy. I just store
it somewhere and return it if a read IO for the same sector arrive.
Let me add a bit of context as to why I sent this patch. I am not an
expert on the finer details of this subject, so I rely on available
expertise in our community. It is my understanding that copying the
memory in the situation outlined above, without special consideration
(in Rust) would be undefined behavior. Such are the rules of the
language. Of course I don't want undefined behavior in the Rust null
block driver. When asked how to solve this, the experts suggested
defining this byte-wise atomic memory copy function, A function that
would have well defined behavior in this particular situation.
That seems like a reasonable course of action to me. I don't understand
why this is such a big deal, and I don't understand the need to use
aggressive language and swearing.
I would suggest that if we are failing to understand each other, or if
we are miscommunicating, let's be curious instead of angry. It get's us
to where we want to be, way faster. And it is a much more pleasant
journey.
Best regards,
Andreas Hindborg
next prev parent reply other threads:[~2026-02-17 13:56 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-12 14:51 Andreas Hindborg
2026-02-12 16:41 ` Boqun Feng
2026-02-12 17:10 ` Andreas Hindborg
2026-02-12 17:23 ` Andreas Hindborg
2026-02-13 9:55 ` Peter Zijlstra
2026-02-13 12:18 ` Greg KH
2026-02-13 12:58 ` Andreas Hindborg
2026-02-13 13:20 ` Greg KH
2026-02-13 14:13 ` Andreas Hindborg
2026-02-13 14:26 ` Peter Zijlstra
2026-02-13 15:34 ` Greg KH
2026-02-13 15:45 ` Boqun Feng
2026-02-13 15:58 ` Greg KH
2026-02-13 16:19 ` Boqun Feng
2026-02-17 9:13 ` Peter Zijlstra
2026-02-17 9:33 ` Alice Ryhl
2026-02-17 9:45 ` Peter Zijlstra
2026-02-17 10:01 ` Alice Ryhl
2026-02-17 10:25 ` Peter Zijlstra
2026-02-17 10:47 ` Alice Ryhl
2026-02-17 11:09 ` Peter Zijlstra
2026-02-17 11:51 ` Alice Ryhl
2026-02-17 12:09 ` Peter Zijlstra
2026-02-17 13:00 ` Peter Zijlstra
2026-02-17 13:54 ` Danilo Krummrich
2026-02-17 15:50 ` Peter Zijlstra
2026-02-17 16:10 ` Danilo Krummrich
2026-02-17 13:09 ` Alice Ryhl
2026-02-17 15:48 ` Peter Zijlstra
2026-02-17 23:39 ` Gary Guo
2026-02-18 8:37 ` Peter Zijlstra
2026-02-18 9:31 ` Alice Ryhl
2026-02-18 10:09 ` Peter Zijlstra
2026-02-17 13:56 ` Andreas Hindborg [this message]
2026-02-17 16:04 ` Peter Zijlstra
2026-02-17 18:43 ` Andreas Hindborg
2026-02-17 20:32 ` Jens Axboe
2026-02-17 15:52 ` Boqun Feng
2026-02-17 9:17 ` Peter Zijlstra
2026-02-17 9:23 ` Peter Zijlstra
2026-02-17 9:37 ` Alice Ryhl
2026-02-17 10:01 ` Peter Zijlstra
2026-02-17 9:33 ` Peter Zijlstra
2026-02-14 0:07 ` Gary Guo
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=87ecmjcw2v.fsf@kernel.org \
--to=a.hindborg@kernel.org \
--cc=Liam.Howlett@oracle.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=boqun@kernel.org \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=lorenzo.stoakes@oracle.com \
--cc=lossin@kernel.org \
--cc=mark.rutland@arm.com \
--cc=ojeda@kernel.org \
--cc=peterz@infradead.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
--cc=will@kernel.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