Re: [PATCH] y2038: uapi: use 64-bit __kernel_old_timespec::tv_nsec on x32

From: Thomas Weißschuh

Date: Fri Feb 27 2026 - 04:57:59 EST


On Fri, Feb 27, 2026 at 10:05:25AM +0100, Arnd Bergmann wrote:
> On Fri, Feb 27, 2026, at 08:18, Thomas Weißschuh wrote:
> > 'struct __kernel_old_timespec' represents the 'native' time ABI of the
> > kernel. On 32-bit systems it uses 32-bit fields and on 64-bit systems
> > it uses 64-bit fields.
> > However the x86 x32 ABI uses the 64-bit time ABI natively.
> > This is correctly handled for the 'tv_sec' fields, through the typedefs
> > of '__kernel_old_time_t' -> '__kernel_long_t' -> 'long long'. The same
> > treatment was missed for 'tv_nsec'.
>
> Not sure about this patch, the choice of 'long' was clearly deliberate
> here because that is what POSIX mandates for 'struct timespec', and what
> glibc uses as well. Any access to an actual userspec timespec
> is supposed to go through get_timespec64(), which handles the padding
> like this

Do we need to follow POSIX here? This is not a POSIX type.
POSIX also specifies 'tv_sec' to be 'time_t', which it isn't here.

Weirdly enough timespec(3type) says this:

tv_nsec is of an implementation-defined signed type capable of
holding the specified range. Under glibc, this is usually long,
and long long on X32. It can be safely down-cast to any concrete
32-bit integer type for processing.

Which does make sense, as x32 uses the time ABI from x86_64, and there
tv_nsec is a 64-bit value.

> int get_timespec64(struct timespec64 *ts,
> const struct __kernel_timespec __user *uts)
> {
> struct __kernel_timespec kts;
> int ret;
>
> ret = copy_from_user(&kts, uts, sizeof(kts));
> if (ret)
> return -EFAULT;
>
> ts->tv_sec = kts.tv_sec;
>
> /* Zero out the padding in compat mode */
> if (in_compat_syscall())
> kts.tv_nsec &= 0xFFFFFFFFUL;
>
> /* In 32-bit mode, this drops the padding */
> ts->tv_nsec = kts.tv_nsec;
>
> return 0;
> }
>
> If there are any code paths that are missing this, they are likely
> also broken for all other compat tasks in the time64 syscalls

Ack.

> > @@ -30,7 +30,7 @@ struct __kernel_old_timeval {
> >
> > struct __kernel_old_timespec {
> > __kernel_old_time_t tv_sec; /* seconds */
> > - long tv_nsec; /* nanoseconds */
> > + __kernel_long_t tv_nsec; /* nanoseconds */
> > };
>
> I think the hidden padding is an actual problem here, but I
> would address that with extra padding, similar to what glibc
> has, e.g.
>
> struct __kernel_old_timespec {
> __kernel_old_time_t tv_sec; /* seconds */
> long tv_nsec; /* nanoseconds */
> #if defined(__x86_64__) && defined(__ILP32__)
> long :32; /* pad to alignment */
> #endif
> };

This patch is a fallout of [0]. With it the type assertions become simpler.
The additional padding would not solve this "issue".

> It may be better to duplicate the structure definition and keep
> the x32 hack in arch/x86/uapi/asm/posix_types.h, the same way
> we do for the sparc64 __kernel_old_timeval, which has a very
> similar issue.

That would be an alternative, yes.

[0] https://lore.kernel.org/lkml/20251113-vdso-test-types-v2-3-0427eff70d08@xxxxxxxxxxxxx/


Thomas