This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: Fifth draft of the Y2038 design document
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