Re: [PATCH] linux: Fix stat layout divergence under pre-POSIX.1-2008 macros [BZ #32119]
Adhemerval Zanella Netto <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Organization | Linaro |
| Message-ID | <[email protected]> |
Sign, I forgot to properly tested on armhf and the CI triggered a regression because the patch does not update the internal __stat64_t64. I will send a new version. On 14/08/26 12:53, Adhemerval Zanella wrote: > The struct stat layout must not depend on the feature-test macros in > use. Currently there are three divergences: > > - bits/struct_stat_time64_helper.h tests __BYTE_ORDER without > including <bits/endian.h>. In strict pre-POSIX.1-2008 modes both > operands are undefined, the big-endian member order is selected > on little-endian targets, displacing the time64 nanosecond fields > on targets whose bits/struct_stat.h does not already include > <bits/endian.h> (x86, mips o32/n32, microblazeel). > > - The same helper adds two reserved words only when POSIX.1-2008 > interfaces are hidden, making the public time64 structure larger > under older feature-test macros. > > - The generic bits/struct_stat.h declares the strict-mode timestamps > as a bare seconds/nanoseconds pair with no endianness handling or > explicit padding. On 32-bit ports with 64-bit time_t whose ABI > aligns 64-bit types to 4 bytes, the timestamps end up packed 12 > bytes apart instead of 16 (arc); on big-endian the nanoseconds also > land on the wrong side of the padding (or1k). This triggers with > plain -D_POSIX_C_SOURCE=200112L, without any _TIME_BITS opt-in, > since time_t is always 64-bit on such ports. > > Fix the helper by including <bits/endian.h>, and give the generic > header the same endianness-aware nanosecond padding as > <bits/types/struct_timespec.h>. > > For the reserved words, instead of removing them unify all > feature-test-macro modes on the largest layout. The internal > __stat64_t64 does not write on them, so libc keeps writing the same > number of bytes and previously built objects using the smaller > default-mode structure remain correct (newly built objects will > over-allocate bytes). > > The resulting sizeof (struct stat) changes, with _TIME_BITS=64 for > the first two rows (the time32 layouts are unchanged) and with any > feature-test macro combination on the time64-only arc and or1k: > > ABI default and pre-POSIX.1-2008 > POSIX.1-2008 strict modes > i686, csky, m68k, microblaze, > sh 108 -> 116 116 (unchanged) > arm, hppa, mips o32/n32, > powerpc32, sparc32 112 -> 120 120 (unchanged) > arc, or1k 128 (unchanged) 116 -> 128 > > Add a compile-only Python test based on glibcextract that compares > sizeof and the member offsets of struct stat across several feature > modes on all configurations. > > Co-authored-by: Matthias Goergens <[email protected]> > --- > sysdeps/unix/sysv/linux/Makefile | 11 +++ > sysdeps/unix/sysv/linux/bits/struct_stat.h | 36 +++++--- > .../linux/bits/struct_stat_time64_helper.h | 13 ++- > sysdeps/unix/sysv/linux/struct_stat_time64.h | 2 + > .../unix/sysv/linux/tst-stat-layout-time64.py | 91 +++++++++++++++++++ > 5 files changed, 138 insertions(+), 15 deletions(-) > create mode 100644 sysdeps/unix/sysv/linux/tst-stat-layout-time64.py > > diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile > index 14a56d5cc3f..a5811977d4a 100644 > --- a/sysdeps/unix/sysv/linux/Makefile > +++ b/sysdeps/unix/sysv/linux/Makefile > @@ -413,6 +413,17 @@ $(objpfx)tst-sched-consts.out: ../sysdeps/unix/sysv/linux/tst-sched-consts.py > < /dev/null > $@ 2>&1; $(evaluate-test) > $(objpfx)tst-sched-consts.out: $(sysdeps-linux-python-deps) > > +tests-special += \ > + $(objpfx)tst-stat-layout-time64.out \ > + # tests-special > +$(objpfx)tst-stat-layout-time64.out: \ > + ../sysdeps/unix/sysv/linux/tst-stat-layout-time64.py > + $(sysdeps-linux-python) \ > + ../sysdeps/unix/sysv/linux/tst-stat-layout-time64.py \ > + $(sysdeps-linux-python-cc) \ > + < /dev/null > $@ 2>&1; $(evaluate-test) > +$(objpfx)tst-stat-layout-time64.out: $(sysdeps-linux-python-deps) > + > tst-rseq-disable-TUNABLES += glibc.pthread.rseq=0 > tst-rseq-disable-static-TUNABLES += glibc.pthread.rseq=0 > > diff --git a/sysdeps/unix/sysv/linux/bits/struct_stat.h b/sysdeps/unix/sysv/linux/bits/struct_stat.h > index e912c3f6ba5..cdbe1f8f762 100644 > --- a/sysdeps/unix/sysv/linux/bits/struct_stat.h > +++ b/sysdeps/unix/sysv/linux/bits/struct_stat.h > @@ -41,6 +41,22 @@ > int __##name##_pad __attribute__((__aligned__ (__alignof__ (type64)))); type name > #endif > > +/* The pre-POSIX.1-2008 timestamp fields must match the layout of the > + 'struct timespec' members used in the POSIX.1-2008 case, including the > + padding required when the seconds field is wider than the word size. */ > +#if __WORDSIZE == 64 \ > + || (defined __SYSCALL_WORDSIZE && __SYSCALL_WORDSIZE == 64) \ > + || (__TIMESIZE == 32 && !defined __USE_TIME64_REDIRECTS) > +# define __fieldts(name) \ > + __time_t name; unsigned long int name ## nsec > +#elif __BYTE_ORDER == __BIG_ENDIAN > +# define __fieldts(name) \ > + __time_t name; int: 32; unsigned long int name ## nsec > +#else > +# define __fieldts(name) \ > + __time_t name; unsigned long int name ## nsec; int: 32 > +#endif > + > struct stat > { > __dev_t st_dev; /* Device. */ > @@ -69,12 +85,9 @@ struct stat > # define st_mtime st_mtim.tv_sec > # define st_ctime st_ctim.tv_sec > #else > - __time_t st_atime; /* Time of last access. */ > - unsigned long int st_atimensec; /* Nscecs of last access. */ > - __time_t st_mtime; /* Time of last modification. */ > - unsigned long int st_mtimensec; /* Nsecs of last modification. */ > - __time_t st_ctime; /* Time of last status change. */ > - unsigned long int st_ctimensec; /* Nsecs of last status change. */ > + __fieldts (st_atime); /* Time of last access. */ > + __fieldts (st_mtime); /* Time of last modification. */ > + __fieldts (st_ctime); /* Time of last status change. */ > #endif > int __glibc_reserved[2]; > }; > @@ -107,17 +120,16 @@ struct stat64 > struct timespec st_mtim; /* Time of last modification. */ > struct timespec st_ctim; /* Time of last status change. */ > #else > - __time_t st_atime; /* Time of last access. */ > - unsigned long int st_atimensec; /* Nscecs of last access. */ > - __time_t st_mtime; /* Time of last modification. */ > - unsigned long int st_mtimensec; /* Nsecs of last modification. */ > - __time_t st_ctime; /* Time of last status change. */ > - unsigned long int st_ctimensec; /* Nsecs of last status change. */ > + __fieldts (st_atime); /* Time of last access. */ > + __fieldts (st_mtime); /* Time of last modification. */ > + __fieldts (st_ctime); /* Time of last status change. */ > #endif > int __glibc_reserved[2]; > }; > #endif > > +#undef __fieldts > + > /* Tell code we have these members. */ > #define _STATBUF_ST_BLKSIZE > #define _STATBUF_ST_RDEV > diff --git a/sysdeps/unix/sysv/linux/bits/struct_stat_time64_helper.h b/sysdeps/unix/sysv/linux/bits/struct_stat_time64_helper.h > index e0b78b314ad..7cbb664d6df 100644 > --- a/sysdeps/unix/sysv/linux/bits/struct_stat_time64_helper.h > +++ b/sysdeps/unix/sysv/linux/bits/struct_stat_time64_helper.h > @@ -16,6 +16,8 @@ > License along with the GNU C Library. If not, see > <https://www.gnu.org/licenses/>. */ > > +#include <bits/endian.h> > + > /* Content of internal __stat64_t64 struct. */ > __dev_t st_dev; /* Device. */ > __ino64_t st_ino; /* file serial number. */ > @@ -59,8 +61,13 @@ > __fieldts64 (st_mtime); > __fieldts64 (st_ctime); > > - unsigned long int __glibc_reserved4; > - unsigned long int __glibc_reserved5; > - > # undef __fieldts64 > #endif > + > +#ifndef __struct_stat_time64_internal > + /* Trailing padding, never set by the stat functions. It keeps > + sizeof (struct stat) as the largest one for all feature-test-macro > + modes. */ > + unsigned long int __glibc_reserved4; > + unsigned long int __glibc_reserved5; > +#endif > diff --git a/sysdeps/unix/sysv/linux/struct_stat_time64.h b/sysdeps/unix/sysv/linux/struct_stat_time64.h > index e3b878e1bed..38e603febdb 100644 > --- a/sysdeps/unix/sysv/linux/struct_stat_time64.h > +++ b/sysdeps/unix/sysv/linux/struct_stat_time64.h > @@ -27,8 +27,10 @@ > struct __stat64_t64 > { > # define __struct_timespec struct __timespec64 > +# define __struct_stat_time64_internal 1 > # include <bits/struct_stat_time64_helper.h> > }; > +# undef __struct_stat_time64_internal > #endif /* __TIMESIZE == 64 */ > > #endif /* _BITS_STRUCT_STAT_TIME64_H */ > diff --git a/sysdeps/unix/sysv/linux/tst-stat-layout-time64.py b/sysdeps/unix/sysv/linux/tst-stat-layout-time64.py > new file mode 100644 > index 00000000000..288a03bb661 > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/tst-stat-layout-time64.py > @@ -0,0 +1,91 @@ > +#!/usr/bin/python3 > +# Check that feature-test macros do not change the time64 stat layout. > +# Copyright (C) 2026 Free Software Foundation, Inc. > +# This file is part of the GNU C Library. > +# > +# The GNU C Library is free software; you can redistribute it and/or > +# modify it under the terms of the GNU Lesser General Public > +# License as published by the Free Software Foundation; either > +# version 2.1 of the License, or (at your option) any later version. > +# > +# The GNU C Library is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +# Lesser General Public License for more details. > +# > +# You should have received a copy of the GNU Lesser General Public > +# License along with the GNU C Library; if not, see > +# <https://www.gnu.org/licenses/>. > + > +import argparse > +import sys > + > +import glibcextract > + > +MODES = { > + 'POSIX.1-1996': '#define _POSIX_C_SOURCE 199506L', > + 'POSIX.1-2001': '#define _POSIX_C_SOURCE 200112L', > + 'POSIX.1-2008': '#define _POSIX_C_SOURCE 200809L', > + 'XPG6': '#define _XOPEN_SOURCE 600', > +} > + > + > +def compute_stat_layout(cc, mode_define): > + sym_data = [ > + '#undef _GNU_SOURCE', > + mode_define, > + '#define _TIME_BITS 64', > + '#define _FILE_OFFSET_BITS 64', > + '#include <stddef.h>', > + '#include <sys/stat.h>', > + 'START', > + ('sizeof_stat', 'sizeof (struct stat)'), > + ('st_dev', 'offsetof (struct stat, st_dev)'), > + ('st_ino', 'offsetof (struct stat, st_ino)'), > + ('st_mode', 'offsetof (struct stat, st_mode)'), > + ('st_nlink', 'offsetof (struct stat, st_nlink)'), > + ('st_uid', 'offsetof (struct stat, st_uid)'), > + ('st_gid', 'offsetof (struct stat, st_gid)'), > + ('st_rdev', 'offsetof (struct stat, st_rdev)'), > + ('st_size', 'offsetof (struct stat, st_size)'), > + ('st_blksize', 'offsetof (struct stat, st_blksize)'), > + ('st_blocks', 'offsetof (struct stat, st_blocks)'), > + ('st_atime', 'offsetof (struct stat, st_atime)'), > + ('st_mtime', 'offsetof (struct stat, st_mtime)'), > + ('st_ctime', 'offsetof (struct stat, st_ctime)'), > + '#ifdef __USE_XOPEN2K8', > + ('st_atimensec', 'offsetof (struct stat, st_atim.tv_nsec)'), > + ('st_mtimensec', 'offsetof (struct stat, st_mtim.tv_nsec)'), > + ('st_ctimensec', 'offsetof (struct stat, st_ctim.tv_nsec)'), > + '#else', > + ('st_atimensec', 'offsetof (struct stat, st_atimensec)'), > + ('st_mtimensec', 'offsetof (struct stat, st_mtimensec)'), > + ('st_ctimensec', 'offsetof (struct stat, st_ctimensec)'), > + '#endif', > + ] > + return glibcextract.compute_c_consts(sym_data, cc) > + > + > +def main(): > + parser = argparse.ArgumentParser( > + description='Check that feature-test macros do not change ' > + 'the time64 stat layout.') > + parser.add_argument('--cc', metavar='CC', > + help='C compiler (including options) to use') > + args = parser.parse_args() > + default_layout = compute_stat_layout(args.cc, '#define _GNU_SOURCE 1') > + status = 0 > + for mode, mode_define in sorted(MODES.items()): > + mode_layout = compute_stat_layout(args.cc, mode_define) > + for name, value in default_layout.items(): > + if mode_layout[name] != value: > + print('FAIL: %s: %s is %s, %s in default mode' > + % (mode, name, mode_layout[name], value)) > + status = 1 > + if status == 0: > + print('PASS: struct stat layout is feature-test-macro invariant') > + sys.exit(status) > + > + > +if __name__ == '__main__': > + main()