Hi Arnd,
On Wed, 22 Feb 2017 16:39:53 +0100, Arnd Bergmann arnd@arndb.de wrote :
On Wed, Feb 22, 2017 at 9:05 AM, Albert ARIBAUD albert.aribaud@3adev.fr wrote:
Hi all,
I have produced a fifth draft of what will eventually become the Y2038 design document:
https://sourceware.org/glibc/wiki/Y2038ProofnessDesign?rev=115
Relative to the previous draft:
It makes explicit that the implementation should allow the same application source code to build unchanged whether the default for time size is 32-bit (_TIME_BITS undefined or unequal to 64) or 64-bit (_TIME_BITS defined equal to 64).
Security issues considerations have been added (thanks to Carlos O'Donnel).
Timestamps and IOCTLs sections have been expanded.
Implementation examples for types has been added.
Implementation examples for APIs has been added.
As always, comments welcome.
I found a few minor inaccuracies:
You have classified sched_rr_get_interval() as y2038-compatible, but getrusage() as incompatible. I think these are both in one category, either incompatible or a third category: we pass only intervals here, so there won't be an overflow but redefining timeval still results in an incompatible ABI, unless the structures are redefined in terms of 32-bit types instead of time_t/timeval/timespec.
Actually, sched_rr_get_interval deserves a fourth category, known as 'in a quantum state', because depending on where you look in the document, it is either compatible or incompatible, due to a copy-paste failure. :)
Joke apart, thanks for pointing it out and raising the point. My opinion is as follows:
1. Right now sched_rr_get_interval takes a struct timespec as an argument;
2. One goal of the project is that source code which works with 32-bit time should work unchanged if compiled with default time size set to 64 bits;
3. This means the 64-bit version of sched_rr_get_interval should keep explecting a (now 64-bit) 'struct timespec';
4. This in turn makes the ABIs of the 32- and 64-bit versions of sched_rr_get_interval mutually incompatible, since the actual types involed would be either the 32-bit time 'struct timespec' or the 64-bit time 'struct timespec64';
5. Ergo, sched_rr_get_interval should be classified as Y2038-incompatible.
I've discussed the kernel side for "Y2038-incompatible socket timestamping" with Deep a while ago, and I think we came to a new conclusions for what would be the best approach. I'll let her comment here.
For "Y2038-compatible types", please clarify whether time32_t and time64_t (and related types) are internal-only types or visible to applications through header files. I assume they are internal only, but it is not 100% clear. Related to that, what is the expected definition of time32_t on architectures that never had a 32-bit time_t, such as existing 64-bit architectures? Is it left undefined and all code referring to time32_t compiled conditionally?
(written after reading Joseph's reply, and type names adapted accordingly)
Yes, there would be two internal types, __time_t and __time64_t with sizes invariant with respect to default type size, whereas time_t, in the user facing public API, would be defined as either __time_t or __time64_t depending on which time bit size the user code would choose
For instance, with difftime:
- the existing, 32-bit-time version would be defined as double __difftime(__time_t time1, __time_t time0) ...
- the new, 64-bit-time version would be defined as double __difftime64(__time64_t time1, __time64_t time0);
- for user code which does not define _TIME_BITS=64 at compile time, GLIBC would emit [what amounts to] the following declaration: double __difftime(__time_t time1, __time_t time0); typedef __time_t time_t; __REDIRECT(difftime,(time_t time1, time_t time0),__difftime) so that when the user code would say time_t t1, t2; ...; difftime(t1, t2); this would amount to __time_t t1, t2; ...; __difftime(t1, t2);
- for user code which defines _TIME_BITS=64 at compile time, GLIBC would emit [what amounts to] the following declarations: double __difftime64(__time64_t time1, __time64_t time0); typedef __time64_t time_t; __REDIRECT(difftime,(time_t time1, time_t time0),__difftime64) so that when the user code would say time_t t1, t2; ...; difftime(t1, t2); this would turn into __time64_t t1, t2; ...; difftime64(t1, t2);
(should I rename the current __time_t to a more explicit __time32_t?)
As far as 64-bit architectures are concerned:
- pure 64-bit architectures already have a 64-bit time_t, and are out of the scope of my project; a 64-bit GLIBC is assumed to be Y2038- compatible as far as APIs go (there may be bugs though; again, if I see any, I'll raise an GLIC issue but outside of this project).
- this leaves the case of a 64-bit architecture kernel providing a 32-bit ABI to 32-bit code. I am not planning on supporting such a scenario.
In "Y2038-compatible struct timespec", replace "microseconds" with "nanoseconds.
Oops. Fixed in revision 118.
Also, it's worth pointing out the known problems with the padding:
- on big-endian systems, any code doing a variation of "struct timespec ts = { seconds, nanos };" is broken because it assigns the nanoseconds to the wrong struct member. The example code is nonconforming as neither POSIX nor C11 require a particular order of the struct members, but I could also easily find examples of existing programs doing this. Note that NetBSD, OpenBSD and Windows have no padding but do use 64-bit time_t.
- If the padding is uninitialized, we have to explicitly zero it before calling a kernel function that assumes the 64-bit layout. This can be done in glibc before calling into the kernel, or at the kernel entry (where my last patch set does it), but it is awkward either way.
Unfortunately, there doesn't seem to be a good solution here for either of the two problems. Maybe someone else has more ideas. Using a pointer type for the padding would at least cause a compile-time warning for broken code, other solutions might require GCC extensions or break C11 in another way.
Agreed on the whole line, and I will add these points in the document.
However, this makes me consider an option which would keep source code as happy as possible: keep tv_nsec a long even in struct timespec64 (so the struct would be 12 bytes: 8-byte tv_sec, 4-byte tv_nsec).
It would be ABI-incompatible with 64-bit code, but would conform to Posix, and the same exact user source code would then compile equally well in all three cases: 32-bit time 64-bit time on 32-bit arch, 64-bit arch, including the struct initializers you mentioned above.
There would be a rough edge left when running 32-bit arch, 64-bit time user code over a 64-bit arch GLIBC and kernel, because then we'd have to copy between 64-bit and 32-bit nanosecond fields, but then again, it is not a scenario I am aiming for.
I'll comment on the kernel/glibc incompatibilities section tomorrow, need to collect my thoughts there some more.
Thanks!
Arnd
Cordialement, Albert ARIBAUD 3ADEV