diff mbox series

[RFC,02/15] rseq: Remove broken uapi field layout on 32-bit little endian

Message ID 20220124171253.22072-3-mathieu.desnoyers@efficios.com (mailing list archive)
State New
Headers show
Series rseq uapi and selftest updates | expand

Commit Message

Mathieu Desnoyers Jan. 24, 2022, 5:12 p.m. UTC
The rseq rseq_cs.ptr.{ptr32,padding} uapi endianness handling is
entirely wrong on 32-bit little endian: a preprocessor logic mistake
wrongly uses the big endian field layout on 32-bit little endian
architectures.

Fortunately, those ptr32 accessors were never used within the kernel,
and only meant as a convenience for user-space.

Remove those and only leave the "ptr64" union field, as this is the only
thing really needed to express the ABI. Document how 32-bit
architectures are meant to interact with this "ptr64" union field.

Fixes: ec9c82e03a74 ("rseq: uapi: Declare rseq_cs field as union, update includes")
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Florian Weimer <fw@deneb.enyo.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-api@vger.kernel.org
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Dave Watson <davejwatson@fb.com>
Cc: Paul Turner <pjt@google.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: "H . Peter Anvin" <hpa@zytor.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Ben Maurer <bmaurer@fb.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Joel Fernandes <joelaf@google.com>
Cc: Paul E. McKenney <paulmck@kernel.org>
---
 include/uapi/linux/rseq.h | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

Comments

Christian Brauner Jan. 25, 2022, 12:21 p.m. UTC | #1
On Mon, Jan 24, 2022 at 12:12:40PM -0500, Mathieu Desnoyers wrote:
> The rseq rseq_cs.ptr.{ptr32,padding} uapi endianness handling is
> entirely wrong on 32-bit little endian: a preprocessor logic mistake
> wrongly uses the big endian field layout on 32-bit little endian
> architectures.
> 
> Fortunately, those ptr32 accessors were never used within the kernel,
> and only meant as a convenience for user-space.
> 
> Remove those and only leave the "ptr64" union field, as this is the only
> thing really needed to express the ABI. Document how 32-bit
> architectures are meant to interact with this "ptr64" union field.
> 
> Fixes: ec9c82e03a74 ("rseq: uapi: Declare rseq_cs field as union, update includes")
> Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Cc: Florian Weimer <fw@deneb.enyo.de>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: linux-api@vger.kernel.org
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Boqun Feng <boqun.feng@gmail.com>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: Dave Watson <davejwatson@fb.com>
> Cc: Paul Turner <pjt@google.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: "H . Peter Anvin" <hpa@zytor.com>
> Cc: Andi Kleen <andi@firstfloor.org>
> Cc: Christian Brauner <christian.brauner@ubuntu.com>
> Cc: Ben Maurer <bmaurer@fb.com>
> Cc: Steven Rostedt <rostedt@goodmis.org>
> Cc: Josh Triplett <josh@joshtriplett.org>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Will Deacon <will.deacon@arm.com>
> Cc: Michael Kerrisk <mtk.manpages@gmail.com>
> Cc: Joel Fernandes <joelaf@google.com>
> Cc: Paul E. McKenney <paulmck@kernel.org>
> ---
>  include/uapi/linux/rseq.h | 17 ++++-------------
>  1 file changed, 4 insertions(+), 13 deletions(-)
> 
> diff --git a/include/uapi/linux/rseq.h b/include/uapi/linux/rseq.h
> index 9a402fdb60e9..31290f2424a7 100644
> --- a/include/uapi/linux/rseq.h
> +++ b/include/uapi/linux/rseq.h
> @@ -105,22 +105,13 @@ struct rseq {
>  	 * Read and set by the kernel. Set by user-space with single-copy
>  	 * atomicity semantics. This field should only be updated by the
>  	 * thread which registered this data structure. Aligned on 64-bit.
> +	 *
> +	 * 32-bit architectures should update the low order bits of the
> +	 * rseq_cs.ptr64 field, leaving the high order bits initialized
> +	 * to 0.
>  	 */
>  	union {

A bit unfortunate we seem to have to keep the union around even though
it's just one field now.
Mathieu Desnoyers Jan. 25, 2022, 2:41 p.m. UTC | #2
----- On Jan 25, 2022, at 7:21 AM, Christian Brauner brauner@kernel.org wrote:

> On Mon, Jan 24, 2022 at 12:12:40PM -0500, Mathieu Desnoyers wrote:
>> The rseq rseq_cs.ptr.{ptr32,padding} uapi endianness handling is
>> entirely wrong on 32-bit little endian: a preprocessor logic mistake
>> wrongly uses the big endian field layout on 32-bit little endian
>> architectures.
>> 
>> Fortunately, those ptr32 accessors were never used within the kernel,
>> and only meant as a convenience for user-space.
>> 
>> Remove those and only leave the "ptr64" union field, as this is the only
>> thing really needed to express the ABI. Document how 32-bit
>> architectures are meant to interact with this "ptr64" union field.
>> 
>> Fixes: ec9c82e03a74 ("rseq: uapi: Declare rseq_cs field as union, update
>> includes")
>> Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
>> Cc: Florian Weimer <fw@deneb.enyo.de>
>> Cc: Thomas Gleixner <tglx@linutronix.de>
>> Cc: linux-api@vger.kernel.org
>> Cc: Peter Zijlstra <peterz@infradead.org>
>> Cc: Boqun Feng <boqun.feng@gmail.com>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: Dave Watson <davejwatson@fb.com>
>> Cc: Paul Turner <pjt@google.com>
>> Cc: Andrew Morton <akpm@linux-foundation.org>
>> Cc: Russell King <linux@arm.linux.org.uk>
>> Cc: "H . Peter Anvin" <hpa@zytor.com>
>> Cc: Andi Kleen <andi@firstfloor.org>
>> Cc: Christian Brauner <christian.brauner@ubuntu.com>
>> Cc: Ben Maurer <bmaurer@fb.com>
>> Cc: Steven Rostedt <rostedt@goodmis.org>
>> Cc: Josh Triplett <josh@joshtriplett.org>
>> Cc: Linus Torvalds <torvalds@linux-foundation.org>
>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>> Cc: Will Deacon <will.deacon@arm.com>
>> Cc: Michael Kerrisk <mtk.manpages@gmail.com>
>> Cc: Joel Fernandes <joelaf@google.com>
>> Cc: Paul E. McKenney <paulmck@kernel.org>
>> ---
>>  include/uapi/linux/rseq.h | 17 ++++-------------
>>  1 file changed, 4 insertions(+), 13 deletions(-)
>> 
>> diff --git a/include/uapi/linux/rseq.h b/include/uapi/linux/rseq.h
>> index 9a402fdb60e9..31290f2424a7 100644
>> --- a/include/uapi/linux/rseq.h
>> +++ b/include/uapi/linux/rseq.h
>> @@ -105,22 +105,13 @@ struct rseq {
>>  	 * Read and set by the kernel. Set by user-space with single-copy
>>  	 * atomicity semantics. This field should only be updated by the
>>  	 * thread which registered this data structure. Aligned on 64-bit.
>> +	 *
>> +	 * 32-bit architectures should update the low order bits of the
>> +	 * rseq_cs.ptr64 field, leaving the high order bits initialized
>> +	 * to 0.
>>  	 */
>>  	union {
> 
> A bit unfortunate we seem to have to keep the union around even though
> it's just one field now.

Well, as far as the user-space projects that I know of which use rseq
are concerned (glibc, librseq, tcmalloc), those end up with their own
copy of the uapi header anyway to deal with the big/little endian field
on 32-bit. So I'm very much open to remove the union if we accept that
this uapi header is really just meant to express the ABI and is not
expected to be used as an API by user-space.

That would mean we also bring a uapi header copy into the kernel
rseq selftests as well to minimize the gap between librseq and
the kernel sefltests (the kernel sefltests pretty much include a
copy of librseq for convenience. librseq is maintained out of tree).

Thoughts ?

Thanks,

Mathieu
Mathieu Desnoyers Jan. 25, 2022, 7 p.m. UTC | #3
----- On Jan 25, 2022, at 9:41 AM, Mathieu Desnoyers mathieu.desnoyers@efficios.com wrote:

> ----- On Jan 25, 2022, at 7:21 AM, Christian Brauner brauner@kernel.org wrote:
[...]
>>>  include/uapi/linux/rseq.h | 17 ++++-------------
[...]
>>>  	union {
>> 
>> A bit unfortunate we seem to have to keep the union around even though
>> it's just one field now.
> 
> Well, as far as the user-space projects that I know of which use rseq
> are concerned (glibc, librseq, tcmalloc), those end up with their own
> copy of the uapi header anyway to deal with the big/little endian field
> on 32-bit. So I'm very much open to remove the union if we accept that
> this uapi header is really just meant to express the ABI and is not
> expected to be used as an API by user-space.
> 
> That would mean we also bring a uapi header copy into the kernel
> rseq selftests as well to minimize the gap between librseq and
> the kernel sefltests (the kernel sefltests pretty much include a
> copy of librseq for convenience. librseq is maintained out of tree).
> 
> Thoughts ?

Actually, if we go ahead and remove the union, and replace:

struct rseq {
  union {
    __u64 ptr64;
  } rseq_cs;
[...]
} v;

by:

struct rseq {
  __u64 rseq_cs;
} v;

expressions such as these are unchanged:

- sizeof(v.rseq_cs),
- &v.rseq_cs,
- __alignof__(v.rseq_cs),
- offsetof(struct rseq, rseq_cs).

So users of the uapi rseq.h (as an API) can still use rseq_abi->rseq_cs before
and after the change.

Based on this, I am inclined to remove the union, and just make the rseq_cs field
a __u64.

Any objections ?

Thanks,

Mathieu


> 
> Thanks,
> 
> Mathieu
> 
> 
> --
> Mathieu Desnoyers
> EfficiOS Inc.
> http://www.efficios.com
Christian Brauner Jan. 26, 2022, 8:03 a.m. UTC | #4
On Tue, Jan 25, 2022 at 02:00:48PM -0500, Mathieu Desnoyers wrote:
> ----- On Jan 25, 2022, at 9:41 AM, Mathieu Desnoyers mathieu.desnoyers@efficios.com wrote:
> 
> > ----- On Jan 25, 2022, at 7:21 AM, Christian Brauner brauner@kernel.org wrote:
> [...]
> >>>  include/uapi/linux/rseq.h | 17 ++++-------------
> [...]
> >>>  	union {
> >> 
> >> A bit unfortunate we seem to have to keep the union around even though
> >> it's just one field now.
> > 
> > Well, as far as the user-space projects that I know of which use rseq
> > are concerned (glibc, librseq, tcmalloc), those end up with their own
> > copy of the uapi header anyway to deal with the big/little endian field
> > on 32-bit. So I'm very much open to remove the union if we accept that
> > this uapi header is really just meant to express the ABI and is not
> > expected to be used as an API by user-space.
> > 
> > That would mean we also bring a uapi header copy into the kernel
> > rseq selftests as well to minimize the gap between librseq and
> > the kernel sefltests (the kernel sefltests pretty much include a
> > copy of librseq for convenience. librseq is maintained out of tree).
> > 
> > Thoughts ?
> 
> Actually, if we go ahead and remove the union, and replace:
> 
> struct rseq {
>   union {
>     __u64 ptr64;
>   } rseq_cs;
> [...]
> } v;
> 
> by:
> 
> struct rseq {
>   __u64 rseq_cs;
> } v;
> 
> expressions such as these are unchanged:
> 
> - sizeof(v.rseq_cs),
> - &v.rseq_cs,
> - __alignof__(v.rseq_cs),
> - offsetof(struct rseq, rseq_cs).
> 
> So users of the uapi rseq.h (as an API) can still use rseq_abi->rseq_cs before
> and after the change.
> 
> Based on this, I am inclined to remove the union, and just make the rseq_cs field
> a __u64.
> 
> Any objections ?

I do like it fwiw. But since I haven't been heavily involved in the
userspace usage of this I can't speak confidently to the regression
potential of a change like this. But I would think that we should risk
it instead of dragging a pointless union around forever.
Florian Weimer Jan. 26, 2022, 11 a.m. UTC | #5
* Christian Brauner:

> On Tue, Jan 25, 2022 at 02:00:48PM -0500, Mathieu Desnoyers wrote:
>> So users of the uapi rseq.h (as an API) can still use
>> rseq_abi->rseq_cs before and after the change.
>> 
>> Based on this, I am inclined to remove the union, and just make the
>> rseq_cs field a __u64.
>> 
>> Any objections ?
>
> I do like it fwiw. But since I haven't been heavily involved in the
> userspace usage of this I can't speak confidently to the regression
> potential of a change like this. But I would think that we should risk
> it instead of dragging a pointless union around forever.

I don't think glibc needs changes for this, it will keep building just
fine.  We'll need to adjust the included kernel header fragment that
could be used by applications in some corner cases, but that's it.
David Laight Jan. 26, 2022, 5:16 p.m. UTC | #6
From: Mathieu Desnoyers
> Sent: 25 January 2022 19:01
> 
> ----- On Jan 25, 2022, at 9:41 AM, Mathieu Desnoyers mathieu.desnoyers@efficios.com wrote:
> 
> > ----- On Jan 25, 2022, at 7:21 AM, Christian Brauner brauner@kernel.org wrote:
> [...]
> >>>  include/uapi/linux/rseq.h | 17 ++++-------------
> [...]
> >>>  	union {
> >>
> >> A bit unfortunate we seem to have to keep the union around even though
> >> it's just one field now.
> >
> > Well, as far as the user-space projects that I know of which use rseq
> > are concerned (glibc, librseq, tcmalloc), those end up with their own
> > copy of the uapi header anyway to deal with the big/little endian field
> > on 32-bit. So I'm very much open to remove the union if we accept that
> > this uapi header is really just meant to express the ABI and is not
> > expected to be used as an API by user-space.
> >
> > That would mean we also bring a uapi header copy into the kernel
> > rseq selftests as well to minimize the gap between librseq and
> > the kernel sefltests (the kernel sefltests pretty much include a
> > copy of librseq for convenience. librseq is maintained out of tree).
> >
> > Thoughts ?
> 
> Actually, if we go ahead and remove the union, and replace:
> 
> struct rseq {
>   union {
>     __u64 ptr64;
>   } rseq_cs;
> [...]
> } v;
> 
> by:
> 
> struct rseq {
>   __u64 rseq_cs;
> } v;
> 
> expressions such as these are unchanged:
> 
> - sizeof(v.rseq_cs),
> - &v.rseq_cs,
> - __alignof__(v.rseq_cs),
> - offsetof(struct rseq, rseq_cs).
> 
> So users of the uapi rseq.h (as an API) can still use rseq_abi->rseq_cs before
> and after the change.

But:
	v.rseq_cs.ptr_64 = (uintptr_t)&foo;
is broken.

> Based on this, I am inclined to remove the union, and just make the rseq_cs field
> a __u64.

It really is a shame that you can't do:
	void   *rseq_cs __attribute__((size(8)));
and have the compiler just DTRT on 32bit systems.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Mathieu Desnoyers Jan. 26, 2022, 6:59 p.m. UTC | #7
----- On Jan 26, 2022, at 12:16 PM, David Laight David.Laight@ACULAB.COM wrote:

> From: Mathieu Desnoyers
>> Sent: 25 January 2022 19:01
>> 
>> ----- On Jan 25, 2022, at 9:41 AM, Mathieu Desnoyers
>> mathieu.desnoyers@efficios.com wrote:
>> 
>> > ----- On Jan 25, 2022, at 7:21 AM, Christian Brauner brauner@kernel.org wrote:
>> [...]
>> >>>  include/uapi/linux/rseq.h | 17 ++++-------------
>> [...]
>> >>>  	union {
>> >>
>> >> A bit unfortunate we seem to have to keep the union around even though
>> >> it's just one field now.
>> >
>> > Well, as far as the user-space projects that I know of which use rseq
>> > are concerned (glibc, librseq, tcmalloc), those end up with their own
>> > copy of the uapi header anyway to deal with the big/little endian field
>> > on 32-bit. So I'm very much open to remove the union if we accept that
>> > this uapi header is really just meant to express the ABI and is not
>> > expected to be used as an API by user-space.
>> >
>> > That would mean we also bring a uapi header copy into the kernel
>> > rseq selftests as well to minimize the gap between librseq and
>> > the kernel sefltests (the kernel sefltests pretty much include a
>> > copy of librseq for convenience. librseq is maintained out of tree).
>> >
>> > Thoughts ?
>> 
>> Actually, if we go ahead and remove the union, and replace:
>> 
>> struct rseq {
>>   union {
>>     __u64 ptr64;
>>   } rseq_cs;
>> [...]
>> } v;
>> 
>> by:
>> 
>> struct rseq {
>>   __u64 rseq_cs;
>> } v;
>> 
>> expressions such as these are unchanged:
>> 
>> - sizeof(v.rseq_cs),
>> - &v.rseq_cs,
>> - __alignof__(v.rseq_cs),
>> - offsetof(struct rseq, rseq_cs).
>> 
>> So users of the uapi rseq.h (as an API) can still use rseq_abi->rseq_cs before
>> and after the change.
> 
> But:
>	v.rseq_cs.ptr_64 = (uintptr_t)&foo;
> is broken.

True. But v.rseq_cs.ptr (on 64-bit) and v.rseq_cs.ptr.ptr32 (on 32-bit) are also
broken with the planned field removal. So how is the v.rseq_cs_ptr64 situation
different ?

My thinking here is that it does not matter if we break compilation for some
users of the uapi as an API as long as the ABI stays the same, especially
considering that all known users implement their own copy of the header.

I suspect that as far as the API is concerned, it is nice that we have at least
one way to access the field which works both before and after the change.
Simply using "v.rseq_cs" works both before/after for all use-cases that seem
to matter here.

> 
>> Based on this, I am inclined to remove the union, and just make the rseq_cs
>> field
>> a __u64.
> 
> It really is a shame that you can't do:
>	void   *rseq_cs __attribute__((size(8)));
> and have the compiler just DTRT on 32bit systems.

Indeed, the "size" directive appears to be ignored by the compiler.

Thanks,

Mathieu

> 
>	David
> 
> -
> Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT,
> UK
> Registration No: 1397386 (Wales)
diff mbox series

Patch

diff --git a/include/uapi/linux/rseq.h b/include/uapi/linux/rseq.h
index 9a402fdb60e9..31290f2424a7 100644
--- a/include/uapi/linux/rseq.h
+++ b/include/uapi/linux/rseq.h
@@ -105,22 +105,13 @@  struct rseq {
 	 * Read and set by the kernel. Set by user-space with single-copy
 	 * atomicity semantics. This field should only be updated by the
 	 * thread which registered this data structure. Aligned on 64-bit.
+	 *
+	 * 32-bit architectures should update the low order bits of the
+	 * rseq_cs.ptr64 field, leaving the high order bits initialized
+	 * to 0.
 	 */
 	union {
 		__u64 ptr64;
-#ifdef __LP64__
-		__u64 ptr;
-#else
-		struct {
-#if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(__BIG_ENDIAN)
-			__u32 padding;		/* Initialized to zero. */
-			__u32 ptr32;
-#else /* LITTLE */
-			__u32 ptr32;
-			__u32 padding;		/* Initialized to zero. */
-#endif /* ENDIAN */
-		} ptr;
-#endif
 	} rseq_cs;
 
 	/*