|
Message-ID: <20190725200107.GM1506@brightrain.aerifal.cx> Date: Thu, 25 Jul 2019 16:01:07 -0400 From: Rich Felker <dalias@...c.org> To: musl@...ts.openwall.com Subject: Re: Final (?) time64 proposal On Thu, Jul 25, 2019 at 01:18:32AM -0400, Rich Felker wrote: > On Wed, Jul 24, 2019 at 01:31:42AM -0400, Rich Felker wrote: > > With that said, the following are the functions I've identified as > > still interfacing with the kernel in ways that involve time_t: > > > > - clock_gettime ** > > [...] > > I think a good next-step here would be modifying all of the above to > go through an intermediate kernel-type struct when using the existing > syscalls. This should not change behavior anywhere except for a slight > increase in size/time-spent, but will set things up so that, when the > time_t definition is switched over, everything should just start > working automatically with 64-bit time. It will also let us drop the > __fixup hacks in arch/x32/syscall_arch.h and > src/thread/x32/syscall_cp_fixup.c, analogous to how commit > 01ae3fc6d48f4a45535189b7a6db286535af08ca let us drop the corresponding > mips hacks. I'm not so sure about this approach now. Conversions will never be needed for 64-bit archs, nor for new code paths using the time64 syscalls on 32-bit archs. Aside from x32 awfulness, the condition for the new version of the code to be needed is SYS_*time64 being defined and time_t being 64-bit. This suggests we might let x32/syscall_arch.h define the SYS_*time64 macros, and undef the legacy names, so that it gets treated like a 32-bit arch with no legacy time32 syscalls (i.e. like riscv32 will be). The logic will roughly be: #ifdef SYS_foo_time64 if (sizeof(time_t)>4) { [if there's an input timespec, copy it and zero padding] SYS_foo_time64 [if success, return] } #ifdef SYS_foo [legacy time32] [if there's input, explicitly convert to legacy time32 form] SYS_foo [if success and there's output, convert back to libc time form] #endif #else SYS_foo [native 64-bit] #endif The "if (sizeof(time_t)>4)" condition will become always-false once 32-bit archs are converted over, and can be removed after that. But for now, it would allow the code to be compile-tested on all archs and to take effect on x32, with the above-described renaming of syscalls made in x32/syscall_arch.h. Granted this is somewhat ugly, but I don't see any other better way; it seems minimal to meet the (hard) requirements of running on pre-5.1 kernels and using correct time representations. Now, on to errors. Below is analysis of valid error handling for conversions that can't be represented. The easiest are timeouts. Whether the overflowing timeout is absolute or relative, it can't happen before the non-Y2038-safe system dies in 2038, so the syscall can just be made with no timeout. That covers: - clock_nanosleep - mq_timedsend - mq_timedreceive - select - pselect - ppoll - recvmmsg - sigtimedwait - semtimedop - __timedwait In the case of clock_nanosleep (and select, if we want to continue providing the updated remaining time for select), we can't just use an unlimited timeout, but instead need to do something like INT32_MAX so that we can add back the remaining time if the syscall does return. The following take as input time values that need conversion and that may not be representable in the legacy type. Thus changing time to 64-bit introduces error cases for them when new syscalls are not available. In general, EOVERFLOW is probably not appropriate for any of these. Its meaning is that the actual value of something is too great to be represented in the C type used for conveying back the result, not that the C type used for input can represent values outside the range of what's supported by the implementation. ENOTSUP is probably the closest to the latter - "Not supported. The implementation does not support the requested feature or value." ^^^^^^^^ - clock_settime - obviously this one can't work for times past Y2038. there's no standard error (note that EOVERFLOW is specified for clock_gettime and matches the above reasoning), so ENOTSUP. - clock_adjtime - same. - timer_settime - no related errors defined. setting max possible value is tempting but would read back inconsistent results via timer_gettime. probably should use ENOTSUP. - timerfd_settime - not governed by standard but should be the same as timer_settime for consistency. - utimensat - possibly EINVAL (defined by POSIX) or ENOTSUP. for this function, EINVAL is defined as: [EINVAL] A new file timestamp would be a value whose tv_sec component is not a value supported by the file system. arguably "the file system" includes the kernel's implementation of it via syscalls like SYS_utimensat. - setitimer - no related errors defined. probably need ENOTSUP for same reasons as timer_settime. - setsockopt (SO_RCVTIMEO and SO_SNDTIMEO, using "_NEW" values) - the timeouts set need to be readable back through getsockopt, so we need an error if falling back to the _OLD versions and the time overflows. ENOTSUP again. The next group is easy. They only produce times as output, and any legacy 32-bit output necessarily fits in the 64-bit type. Thus, these have no error cases: - clock_gettime - timer_gettime - timerfd_gettime - getitimer - clock_getres - sched_rr_get_interval - getrusage - wait4 - wait3 - getsockopt (SO_RCVTIMEO and SO_SNDTIMEO, using "_NEW" values) - shmctl - semctl - msgctl There are a few ugly things I didn't cover yet: setsockopt with SO_TIMESTAMP* either needs to hard-fail if the kernel doesn't support the "_NEW" values, or some slightly-heavy emulation in recvmsg is needed (translating cmsgs to 64-bit). ioctl probably has a number of places where we need to do translations for fallback ioctl numbers. Hopefully the kernel time64 commits show us what these are. That should cover all the interfaces I had on this list so far. I'll probably try some conversions using the above #ifdef idiom and see how it goes. Rich
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.