Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rseq: Remove broken uapi field layout on 32-bit little endian

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 replace the whole rseq_cs union by a __u64 type, as
this is the only thing really needed to express the ABI. Document how
32-bit architectures are meant to interact with this field.

Fixes: ec9c82e03a74 ("rseq: uapi: Declare rseq_cs field as union, update includes")
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20220127152720.25898-1-mathieu.desnoyers@efficios.com

authored by

Mathieu Desnoyers and committed by
Peter Zijlstra
bfdf4e62 5c105d55

+8 -20
+4 -16
include/uapi/linux/rseq.h
··· 105 105 * Read and set by the kernel. Set by user-space with single-copy 106 106 * atomicity semantics. This field should only be updated by the 107 107 * thread which registered this data structure. Aligned on 64-bit. 108 + * 109 + * 32-bit architectures should update the low order bits of the 110 + * rseq_cs field, leaving the high order bits initialized to 0. 108 111 */ 109 - union { 110 - __u64 ptr64; 111 - #ifdef __LP64__ 112 - __u64 ptr; 113 - #else 114 - struct { 115 - #if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(__BIG_ENDIAN) 116 - __u32 padding; /* Initialized to zero. */ 117 - __u32 ptr32; 118 - #else /* LITTLE */ 119 - __u32 ptr32; 120 - __u32 padding; /* Initialized to zero. */ 121 - #endif /* ENDIAN */ 122 - } ptr; 123 - #endif 124 - } rseq_cs; 112 + __u64 rseq_cs; 125 113 126 114 /* 127 115 * Restartable sequences flags field.
+4 -4
kernel/rseq.c
··· 128 128 int ret; 129 129 130 130 #ifdef CONFIG_64BIT 131 - if (get_user(ptr, &t->rseq->rseq_cs.ptr64)) 131 + if (get_user(ptr, &t->rseq->rseq_cs)) 132 132 return -EFAULT; 133 133 #else 134 - if (copy_from_user(&ptr, &t->rseq->rseq_cs.ptr64, sizeof(ptr))) 134 + if (copy_from_user(&ptr, &t->rseq->rseq_cs, sizeof(ptr))) 135 135 return -EFAULT; 136 136 #endif 137 137 if (!ptr) { ··· 217 217 * Set rseq_cs to NULL. 218 218 */ 219 219 #ifdef CONFIG_64BIT 220 - return put_user(0UL, &t->rseq->rseq_cs.ptr64); 220 + return put_user(0UL, &t->rseq->rseq_cs); 221 221 #else 222 - if (clear_user(&t->rseq->rseq_cs.ptr64, sizeof(t->rseq->rseq_cs.ptr64))) 222 + if (clear_user(&t->rseq->rseq_cs, sizeof(t->rseq->rseq_cs))) 223 223 return -EFAULT; 224 224 return 0; 225 225 #endif