Type punning is necessary for get/put unaligned but the use of a
packed struct violates strict aliasing rules, requiring
-fno-strict-aliasing to be passed to the C compiler. Switch to using
memcpy so that -fno-strict-aliasing isn't necessary.
Signed-off-by: Ian Rogers <irogers@google.com>
---
include/vdso/unaligned.h | 48 +++++++++++++++++++++++++++++++++++-----
1 file changed, 42 insertions(+), 6 deletions(-)
diff --git a/include/vdso/unaligned.h b/include/vdso/unaligned.h
index ff0c06b6513e..f33c1eefbf68 100644
--- a/include/vdso/unaligned.h
+++ b/include/vdso/unaligned.h
@@ -2,14 +2,50 @@
#ifndef __VDSO_UNALIGNED_H
#define __VDSO_UNALIGNED_H
-#define __get_unaligned_t(type, ptr) ({ \
- const struct { type x; } __packed * __get_pptr = (typeof(__get_pptr))(ptr); \
- __get_pptr->x; \
+#define ____get_unaligned_type(type) type: (type)0
+/**
+ * __get_unaligned_t - read an unaligned value from memory.
+ * @ptr: the pointer to load from.
+ * @type: the type to load from the pointer.
+ *
+ * Use memcpy to affect an unaligned type sized load avoiding undefined behavior
+ * from approaches like type punning that require -fno-strict-aliasing in order
+ * to be correct. As type may be const, use _Generic to map to a non-const type
+ * - you can't memcpy into a const type. The void* cast silences ubsan warnings.
+ */
+#define __get_unaligned_t(type, ptr) ({ \
+ type __get_unaligned_map_ctrl = 0; \
+ typeof(_Generic(__get_unaligned_map_ctrl, \
+ ____get_unaligned_type(short int), \
+ ____get_unaligned_type(unsigned short int), \
+ ____get_unaligned_type(int), \
+ ____get_unaligned_type(unsigned int), \
+ ____get_unaligned_type(long), \
+ ____get_unaligned_type(unsigned long), \
+ ____get_unaligned_type(long long), \
+ ____get_unaligned_type(unsigned long long), \
+ default: (type)0 \
+ )) __get_unaligned_val; \
+ (void)__get_unaligned_map_ctrl; \
+ __builtin_memcpy(&__get_unaligned_val, (void *)(ptr), \
+ sizeof(__get_unaligned_val)); \
+ __get_unaligned_val; \
})
-#define __put_unaligned_t(type, val, ptr) do { \
- struct { type x; } __packed * __put_pptr = (typeof(__put_pptr))(ptr); \
- __put_pptr->x = (val); \
+/**
+ * __put_unaligned_t - write an unaligned value to memory.
+ * @type: the type of the value to store.
+ * @val: the value to store.
+ * @ptr: the pointer to store to.
+ *
+ * Use memcpy to affect an unaligned type sized store avoiding undefined
+ * behavior from approaches like type punning that require -fno-strict-aliasing
+ * in order to be correct. The void* cast silences ubsan warnings.
+ */
+#define __put_unaligned_t(type, val, ptr) do { \
+ type __put_unaligned_val = (val); \
+ __builtin_memcpy((void *)(ptr), &__put_unaligned_val, \
+ sizeof(__put_unaligned_val)); \
} while (0)
#endif /* __VDSO_UNALIGNED_H */
--
2.50.0.rc2.701.gf1e915cc24-goog
On Tue, 17 Jun 2025 13:53:18 -0700 Ian Rogers <irogers@google.com> wrote: > Type punning is necessary for get/put unaligned but the use of a > packed struct violates strict aliasing rules, requiring > -fno-strict-aliasing to be passed to the C compiler. Switch to using > memcpy so that -fno-strict-aliasing isn't necessary. >.... \ > +/** > + * __put_unaligned_t - write an unaligned value to memory. > + * @type: the type of the value to store. > + * @val: the value to store. > + * @ptr: the pointer to store to. > + * > + * Use memcpy to affect an unaligned type sized store avoiding undefined > + * behavior from approaches like type punning that require -fno-strict-aliasing > + * in order to be correct. The void* cast silences ubsan warnings. > + */ > +#define __put_unaligned_t(type, val, ptr) do { \ > + type __put_unaligned_val = (val); \ > + __builtin_memcpy((void *)(ptr), &__put_unaligned_val, \ > + sizeof(__put_unaligned_val)); \ > } while (0) Does that actually work? If 'ptr' has type 'long *' gcc will (validly according to C) assume it is aligned and will generate a misaligned memory write. The (void *) cast make no difference. Using (void *)(long)(ptr) might lose the alignment. Otherwise you may need to use OPTIMISER_HIDE_VAR() and live with the extra register-register move it tends to generate. David
Hi Ian, kernel test robot noticed the following build warnings: [auto build test WARNING on linus/master] [also build test WARNING on v6.16-rc2 next-20250618] [cannot apply to tip/timers/vdso acme/perf/core] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Ian-Rogers/vdso-Switch-get-put-unaligned-from-packed-struct-to-memcpy/20250618-045610 base: linus/master patch link: https://lore.kernel.org/r/20250617205320.1580946-2-irogers%40google.com patch subject: [PATCH v2 1/3] vdso: Switch get/put unaligned from packed struct to memcpy config: parisc-allnoconfig (https://download.01.org/0day-ci/archive/20250618/202506182044.OhyWGCSm-lkp@intel.com/config) compiler: hppa-linux-gcc (GCC) 15.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250618/202506182044.OhyWGCSm-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202506182044.OhyWGCSm-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from include/linux/swab.h:5, from include/uapi/linux/byteorder/big_endian.h:14, from include/linux/byteorder/big_endian.h:5, from arch/parisc/include/uapi/asm/byteorder.h:5, from arch/parisc/include/asm/bitops.h:11, from include/linux/bitops.h:67, from include/linux/kernel.h:23, from arch/parisc/include/asm/bug.h:5, from include/linux/bug.h:5, from include/linux/thread_info.h:13, from include/linux/sched.h:14, from include/linux/uaccess.h:9, from arch/parisc/boot/compressed/misc.c:7: In function 'get_unaligned_le32', inlined from 'decompress_kernel' at arch/parisc/boot/compressed/misc.c:312:16: >> include/vdso/unaligned.h:30:9: warning: '__builtin_memcpy' reading 4 bytes from a region of size 1 [-Wstringop-overread] 30 | __builtin_memcpy(&__get_unaligned_val, (void *)(ptr), \ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 | sizeof(__get_unaligned_val)); \ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/uapi/linux/swab.h:120:19: note: in definition of macro '__swab32' 120 | __fswab32(x)) | ^ include/linux/byteorder/generic.h:89:21: note: in expansion of macro '__le32_to_cpu' 89 | #define le32_to_cpu __le32_to_cpu | ^~~~~~~~~~~~~ include/linux/unaligned.h:23:28: note: in expansion of macro '__get_unaligned_t' 23 | return le32_to_cpu(__get_unaligned_t(__le32, p)); | ^~~~~~~~~~~~~~~~~ arch/parisc/boot/compressed/misc.c: In function 'decompress_kernel': arch/parisc/boot/compressed/misc.c:29:13: note: source object 'output_len' of size 1 29 | extern char output_len; | ^~~~~~~~~~ vim +/__builtin_memcpy +30 include/vdso/unaligned.h 4 5 #define ____get_unaligned_type(type) type: (type)0 6 /** 7 * __get_unaligned_t - read an unaligned value from memory. 8 * @ptr: the pointer to load from. 9 * @type: the type to load from the pointer. 10 * 11 * Use memcpy to affect an unaligned type sized load avoiding undefined behavior 12 * from approaches like type punning that require -fno-strict-aliasing in order 13 * to be correct. As type may be const, use _Generic to map to a non-const type 14 * - you can't memcpy into a const type. The void* cast silences ubsan warnings. 15 */ 16 #define __get_unaligned_t(type, ptr) ({ \ 17 type __get_unaligned_map_ctrl = 0; \ 18 typeof(_Generic(__get_unaligned_map_ctrl, \ 19 ____get_unaligned_type(short int), \ 20 ____get_unaligned_type(unsigned short int), \ 21 ____get_unaligned_type(int), \ 22 ____get_unaligned_type(unsigned int), \ 23 ____get_unaligned_type(long), \ 24 ____get_unaligned_type(unsigned long), \ 25 ____get_unaligned_type(long long), \ 26 ____get_unaligned_type(unsigned long long), \ 27 default: (type)0 \ 28 )) __get_unaligned_val; \ 29 (void)__get_unaligned_map_ctrl; \ > 30 __builtin_memcpy(&__get_unaligned_val, (void *)(ptr), \ 31 sizeof(__get_unaligned_val)); \ 32 __get_unaligned_val; \ 33 }) 34 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki
Le 17/06/2025 à 22:53, Ian Rogers a écrit : > Type punning is necessary for get/put unaligned but the use of a > packed struct violates strict aliasing rules, requiring > -fno-strict-aliasing to be passed to the C compiler. Switch to using > memcpy so that -fno-strict-aliasing isn't necessary. > > Signed-off-by: Ian Rogers <irogers@google.com> Reviewed-by: Christophe Leroy <christophe.leroy@csgroup.eu> > --- > include/vdso/unaligned.h | 48 +++++++++++++++++++++++++++++++++++----- > 1 file changed, 42 insertions(+), 6 deletions(-) > > diff --git a/include/vdso/unaligned.h b/include/vdso/unaligned.h > index ff0c06b6513e..f33c1eefbf68 100644 > --- a/include/vdso/unaligned.h > +++ b/include/vdso/unaligned.h > @@ -2,14 +2,50 @@ > #ifndef __VDSO_UNALIGNED_H > #define __VDSO_UNALIGNED_H > > -#define __get_unaligned_t(type, ptr) ({ \ > - const struct { type x; } __packed * __get_pptr = (typeof(__get_pptr))(ptr); \ > - __get_pptr->x; \ > +#define ____get_unaligned_type(type) type: (type)0 > +/** > + * __get_unaligned_t - read an unaligned value from memory. > + * @ptr: the pointer to load from. > + * @type: the type to load from the pointer. > + * > + * Use memcpy to affect an unaligned type sized load avoiding undefined behavior > + * from approaches like type punning that require -fno-strict-aliasing in order > + * to be correct. As type may be const, use _Generic to map to a non-const type > + * - you can't memcpy into a const type. The void* cast silences ubsan warnings. > + */ > +#define __get_unaligned_t(type, ptr) ({ \ > + type __get_unaligned_map_ctrl = 0; \ > + typeof(_Generic(__get_unaligned_map_ctrl, \ > + ____get_unaligned_type(short int), \ > + ____get_unaligned_type(unsigned short int), \ > + ____get_unaligned_type(int), \ > + ____get_unaligned_type(unsigned int), \ > + ____get_unaligned_type(long), \ > + ____get_unaligned_type(unsigned long), \ > + ____get_unaligned_type(long long), \ > + ____get_unaligned_type(unsigned long long), \ > + default: (type)0 \ > + )) __get_unaligned_val; \ > + (void)__get_unaligned_map_ctrl; \ > + __builtin_memcpy(&__get_unaligned_val, (void *)(ptr), \ > + sizeof(__get_unaligned_val)); \ > + __get_unaligned_val; \ > }) > > -#define __put_unaligned_t(type, val, ptr) do { \ > - struct { type x; } __packed * __put_pptr = (typeof(__put_pptr))(ptr); \ > - __put_pptr->x = (val); \ > +/** > + * __put_unaligned_t - write an unaligned value to memory. > + * @type: the type of the value to store. > + * @val: the value to store. > + * @ptr: the pointer to store to. > + * > + * Use memcpy to affect an unaligned type sized store avoiding undefined > + * behavior from approaches like type punning that require -fno-strict-aliasing > + * in order to be correct. The void* cast silences ubsan warnings. > + */ > +#define __put_unaligned_t(type, val, ptr) do { \ > + type __put_unaligned_val = (val); \ > + __builtin_memcpy((void *)(ptr), &__put_unaligned_val, \ > + sizeof(__put_unaligned_val)); \ > } while (0) > > #endif /* __VDSO_UNALIGNED_H */
© 2016 - 2025 Red Hat, Inc.