Re: [PATCH v3 3/3] system_data_types.7: note struct timespec::tv_nsec type for x32 and portability

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Dec 07, 2021 at 01:52:52AM +0100, Alejandro Colomar (man-pages) wrote:
> On 12/7/21 01:18, Zack Weinberg wrote:
> > Depending on the kernel, the architecture, the ABI, and the state of _TIME_BITS, the format of `struct timespec` as expected by system calls may be any of
> > 
> > struct timespec { int32_t tv_sec; int32_t tv_nsec; };
> > struct timespec { int64_t tv_sec; int64_t tv_nsec; };
> > struct timespec { int64_t tv_sec; int32_t __padding; int32_t tv_nsec; }
> > struct timespec { int64_t tv_sec; int32_t tv_nsec; int32_t __padding; }
> > 
> > These may be incompatible with using bare `long` for tv_nsec in the user space headers.  The problem cases are when the kernel expects option 2 but user space's `long` is only 32 bits wide, or if the kernel expects option 3 or 4 when `long` is 64 bits wide and the endianness of `long` doesn't agree with the position of the padding.  I don't remember which ABIs actually manifest an incompatibility, but I remember this being discussed repeatedly during the project to make 64-bit time_t available to ILP32 ABIs.
> > 
> > The C library CANNOT paper over the incompatibility by converting formats in the syscall wrappers, because `struct timespec` objects get embedded in an unbounded set of `ioctl` parameter blocks, `sendmsg` ancillary data structures, etc. etc. etc.  We can't even make a list.
This I forgot to take into account, mea culpa!

> > Personally I consider the situation a defect in POSIX and C11; the definition of `struct timespec` should always have been
> > 
> > struct timespec { time_t tv_sec; nsec_t tv_nsec; };
> > 
> > where nsec_t is an integer type that can represent the range [0, 10**9).  Last time this came up, Joseph didn't think there was any chance of persuading either the Austin Group or WG14 to make this change, unfortunately.
Sadly, agree on both fronts.

Looking through "timespec" on Aardvark for prior art reveals nothing,
except for a likely resolution to any proposal of this sort:
> Although we agree that it would have been better if these functions had
> been designed this way to begin with, we believe that making the change
> now will break existing, conforming code with no real benefit. 

Considering this is, as outlined above, a real ABI-meets-standard moment
that affects all users of the Linux syscall ABI, I rolled back to Notes,
replaced glibc references with a "this is unfortunate but unfixable"
stanza, and a link to this post in the libc-alpha archive in the comment.

Regrettably, I think this is the best we can do,
save for committing a plan9. Scissor-patch below.

Cheers,
наб

-- >8 --
There are three files that govern userspace struct timespec on glibc:
1. bits/wordsize.h, defining:
   (a) __WORDSIZE to 32 on ILP32 and 64 on LP64
   (b) on x32: __SYSCALL_WORDSIZE to 64
2. bits/timesize.h, defining
   (a) __TIMESIZE to __WORDSIZE, except on x32 where it's 64
3. bits/types/struct_timespec.h, declaring struct timespec as:
     struct timespec
     {
      __time_t tv_sec;      /* Seconds.  */
     #if __WORDSIZE == 64 \
      || (defined __SYSCALL_WORDSIZE && __SYSCALL_WORDSIZE == 64) \
      || __TIMESIZE == 32
      __syscall_slong_t tv_nsec;    /* Nanoseconds.  */
     #else
     # if __BYTE_ORDER == __BIG_ENDIAN
      int: 32;           /* Padding.  */
      long int tv_nsec;  /* Nanoseconds.  */
     # else
      long int tv_nsec;  /* Nanoseconds.  */
      int: 32;           /* Padding.  */
     # endif
     #endif
     };
   this has two side-effects: struct timespec
   (a) is always sizeof==time_t+8, and
   (b) has tv_nsec as __syscall_slong_t
       *and* !is_same<__syscall_slong_t, long>
       if using LP64 syscalls on an ILP32 system, i.e. on x32.

This means, that the simplified
  struct timespec {
      time_t  tv_sec;  /* Seconds */
      long    tv_nsec; /* Nanoseconds [0 .. 999999999] */
  };
declaration is *invalid* for x32,
where struct timespec::tv_nsec is an int64_t (long long).

Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@xxxxxxxxxxxxxxxxxx>
---
 man7/system_data_types.7 | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/man7/system_data_types.7 b/man7/system_data_types.7
index 1e6a3f74c..46fcf013d 100644
--- a/man7/system_data_types.7
+++ b/man7/system_data_types.7
@@ -1553,6 +1553,36 @@ Describes times in seconds and nanoseconds.
 .IR "Conforming to" :
 C11 and later; POSIX.1-2001 and later.
 .PP
+.IR Notes :
+.I tv_nsec
+is the
+.I syscall
+long, though this affects only fringe architectures like X32,
+which is ILP32, but uses the LP64 AMD64 syscall ABI.
+In reality, the field ends up being defined as:
+.PP
+.in +4
+.EX
+#if __x86_64__ && __ILP32__  /* == x32 */
+    long long tv_nsec;
+#else
+    long      tv_nsec;
+#endif
+.EE
+.in
+.PP
+This is one of the many unfortunate side-effects of the intersection of
+.I tv_nsec
+being defined as
+.IR long ,
+and cannot be addressed while preserving backward compatibility.
+.\" https://sourceware.org/pipermail/libc-alpha/2021-December/133702.html
+However, as even a 32-bit
+.I long
+can hold the entire
+.I tv_nsec
+range, it's always safe to forcibly down-cast it to the standard type.
+.PP
 .IR "See also" :
 .BR clock_gettime (2),
 .BR clock_nanosleep (2),
-- 
2.30.2

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Kernel Documentation]     [Netdev]     [Linux Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux