Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

year 2038 bug in POSIX::mktime #21551

Closed
kernigh opened this issue Oct 9, 2023 · 7 comments · Fixed by #21554
Closed

year 2038 bug in POSIX::mktime #21551

kernigh opened this issue Oct 9, 2023 · 7 comments · Fixed by #21554

Comments

@kernigh
Copy link

kernigh commented Oct 9, 2023

Module: POSIX


Description

On my old 32-bit powerpc, POSIX::mktime overflows from January 2038
to December 1901. I guess it's because ivsize=4.
Time::Local::timelocal_posix doesn't have this bug.

Steps to Reproduce

use POSIX 'mktime';
for (17 .. 21) {
  $_ = mktime(0, 0, 0, $_, 0, 2038 - 1900);
  printf "%s => %s\n", $_, scalar(localtime $_);
}

Expected behavior

This script should show the days 17 to 21 in January 2038, but I see
wrong days in 1901:

2147317200 => Sun Jan 17 00:00:00 2038
2147403600 => Mon Jan 18 00:00:00 2038
-2147477296 => Fri Dec 13 17:31:44 1901
-2147390896 => Sat Dec 14 17:31:44 1901
-2147304496 => Sun Dec 15 17:31:44 1901

Flags

  • category=library
  • severity=low
  • module=POSIX

Perl configuration

Site configuration information for perl 5.38.0:

Configured by kernigh at Wed Jul 26 13:15:53 EDT 2023.

Summary of my perl5 (revision 5 version 38 subversion 0) configuration:
  Commit id: 76298ae68aa7796f0ffc05095b127d23f4b2de8f
  Platform:
    osname=openbsd
    osvers=7.3
    archname=OpenBSD.powerpc-openbsd
    uname='openbsd wisconsin.my.domain 7.3 generic#144 macppc '
    config_args='-des -Dprefix=/home/kernigh/prefix/lop'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=undef
    usemultiplicity=undef
    use64bitint=undef
    use64bitall=undef
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='cc'
    ccflags ='-fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    optimize='-O2'
    cppflags='-fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion=''
    gccversion='OpenBSD Clang 13.0.0'
    gccosandvers=''
    intsize=4
    longsize=4
    ptrsize=4
    doublesize=8
    byteorder=4321
    doublekind=4
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=8
    longdblkind=0
    ivtype='long'
    ivsize=4
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='cc'
    ldflags ='-Wl,-E  -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/lib/clang/13.0.0/lib /usr/lib /usr/local/lib
    libs=-lpthread -lm -lutil -lc
    perllibs=-lpthread -lm -lutil -lc
    libc=/usr/lib/libc.so.97.0
    so=so
    useshrplib=false
    libperl=libperl.a
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=so
    d_dlsymun=undef
    ccdlflags=' '
    cccdlflags='-DPIC -fPIC '
    lddlflags='-shared -fPIC  -L/usr/local/lib -fstack-protector-strong'


---
@INC for perl 5.38.0:
    /home/kernigh/prefix/lop/lib/perl5/site_perl/5.38.0/OpenBSD.powerpc-openbsd
    /home/kernigh/prefix/lop/lib/perl5/site_perl/5.38.0
    /home/kernigh/prefix/lop/lib/perl5/5.38.0/OpenBSD.powerpc-openbsd
    /home/kernigh/prefix/lop/lib/perl5/5.38.0

---
Environment for perl 5.38.0:
    HOME=/home/kernigh
    LANG (unset)
    LANGUAGE (unset)
    LC_CTYPE=en_US.UTF-8
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/kernigh/prefix/lop/bin:/home/kernigh/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin:/usr/games:/home/kernigh/.local/share/gem/ruby/3.1/bin
    PERL_BADLANG (unset)
    SHELL=/bin/ksh
@Leont
Copy link
Contributor

Leont commented Oct 9, 2023

You may want to compile with -Duse64bitint

@kernigh
Copy link
Author

kernigh commented Oct 10, 2023

I tried -Duse64bitint with perl 5.36.0 in January 2023, but that perl was slightly slower. My program took 53 seconds with -Duse64bitint, versus 50 seconds without it. I now avoid -Duse64bitint, because I believe that perl packages in bsd and linux distros never enable -Duse64bitint. Because I don't -Duse64bitint, I sometimes need Math::BigInteger for 64-bit integers. I have an XS typemap that calls Math::BigInteger->from_bytes to output a 64-bit integer (from C to Perl).

sv_setiv(TARG, (IV)result); in POSIX.xs probably causes this bug. My 64-bit cpu, where perl -V:ivsize is 8, doesn't reproduce the bug.

Back on my 32-bit cpu, the bug is in Perl's mktime, not in C's. My C code is,

#include <stdio.h>
#include <time.h>

int
main(void)
{
  for (int i = 17; i <= 21; i++) {
    time_t clock = mktime(&(struct tm){
        .tm_mday = i, .tm_mon = 0, .tm_year = 2038 - 1900});
    printf("%lld => %s", (long long)clock, ctime(&clock));
  }
}

The C code outputs the correct dates,

2147317200 => Sun Jan 17 00:00:00 2038
2147403600 => Mon Jan 18 00:00:00 2038
2147490000 => Tue Jan 19 00:00:00 2038
2147576400 => Wed Jan 20 00:00:00 2038
2147662800 => Thu Jan 21 00:00:00 2038

(The numbers on the left side depend on your time zone; I'm in America/New_York.)

@xenu
Copy link
Member

xenu commented Oct 10, 2023

I believe that perl packages in bsd and linux distros never enable -Duse64bitint

This is incorrect, most distros do enable it. Some examples: FreeBSD, Debian, Fedora.

Back on my 32-bit cpu, the bug is in Perl's mktime, not in C's. My C code is,

Your C code uses 64-bit integers.

@xenu
Copy link
Member

xenu commented Oct 10, 2023

I guess we could make POSIX use floats for time, we already do this in some other places.

But, to be honest, I would much rather just remove support for 32-bit IVs. It's a much cleaner solution.

@Leont
Copy link
Contributor

Leont commented Oct 10, 2023

But, to be honest, I would much rather just remove support for 32-bit IVs. It's a much cleaner solution.

I don't think it's helpful to remove it entirely, but I do think it's fair to say that people who choose them get to deal with the consequences.

I do think we should make -Duse64bitint the default on 32 bit perls though.

mauke added a commit to mauke/perl5 that referenced this issue Oct 10, 2023
If IV is a 32-bit type, but time_t is bigger, then it is possible for
mktime() to return values outside the 32-bit range (> 0x7fff_ffff after
2038-01-19). In that case, fall back to NV (i.e. floating point). A
double-precision floating point number provides more than 52 bits of
precision, which should be plenty.

Fixes Perl#21551.
mauke added a commit that referenced this issue Oct 11, 2023
If IV is a 32-bit type, but time_t is bigger, then it is possible for
mktime() to return values outside the 32-bit range (> 0x7fff_ffff after
2038-01-19). In that case, fall back to NV (i.e. floating point). A
double-precision floating point number provides more than 52 bits of
precision, which should be plenty.

Fixes #21551.
@Tux
Copy link
Contributor

Tux commented Oct 11, 2023

I do think we should make -Duse64bitint the default on 32 bit perls though.

I agree, but only when the OS is known to support it in the given configuration

@kernigh
Copy link
Author

kernigh commented Oct 12, 2023

I believe that perl packages in bsd and linux distros never enable -Duse64bitint

This is incorrect, most distros do enable it. Some examples: FreeBSD, Debian, Fedora.

We're using different distros. Adélie Linux, NetBSD, OpenBSD don't enable -Duse64bitint. I checked by running perl -V:ivsize on 32-bit powerpc (on real hardware for OpenBSD; in qemu for Adélie and NetBSD).

Fedora doesn't count; I can't find a 32-bit Fedora. We now have 2 out of 5 known distros using -Duse64bitint, more than "never", but less than "most".

I do think we should make -Duse64bitint the default on 32 bit perls though.

If we want -Duse64bitint by default, how it would we make it happen?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants