RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 2110357 - glibc: mktime() fails with -EOVERFLOW when tm_isdst=1 and a neighboring DST boundary is far from tm_year
Summary: glibc: mktime() fails with -EOVERFLOW when tm_isdst=1 and a neighboring DST b...
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 9
Classification: Red Hat
Component: glibc
Version: 9.0
Hardware: Unspecified
OS: Linux
unspecified
medium
Target Milestone: rc
: ---
Assignee: Arjun Shankar
QA Contact: Sergey Kolosov
URL:
Whiteboard:
Depends On: 2141728
Blocks:
TreeView+ depends on / blocked
 
Reported: 2022-07-25 10:04 UTC by Takayuki Nagata
Modified: 2023-05-09 10:18 UTC (History)
10 users (show)

Fixed In Version: glibc-2.34-49.el9
Doc Type: No Doc Update
Doc Text:
Clone Of:
Environment:
Last Closed: 2023-05-09 08:16:01 UTC
Type: Bug
Target Upstream Version:
Embargoed:
pm-rhel: mirror+


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Bugzilla 2027416 0 unspecified CLOSED glibc: mktime sometimes returns -1 with errno EOVERFLOW when the tm_isdst=1 for non-DST dates 2022-08-02 14:30:41 UTC
Red Hat Issue Tracker RHELPLAN-128818 0 None None None 2022-07-25 10:06:12 UTC
Red Hat Product Errata RHBA-2023:2481 0 None None None 2023-05-09 08:16:39 UTC
Sourceware 29035 0 P2 UNCONFIRMED Output of mktime with latest gcc and glibc differs with their older version when is_dst flag set 2022-08-01 14:57:54 UTC

Description Takayuki Nagata 2022-07-25 10:04:11 UTC
Description of problem:
Since RHEL9, the following mktime() fails with -EOVERFLOW.

    setenv("TZ", "Asia/Tokyo", 1);
    tm.tm_mday = 1;
    tm.tm_mon = 1;
    tm.tm_year = 2023;
    tm.tm_isdst = 1;
    mktime(&tm);

On previous versions (RHEL8 and RHEL7), it returns 1503779056.
If the tm_isdst is 0, it returns 1503779056 on RHEL9.

Version-Release number of selected component (if applicable):
glibc-2.34-28.el9_0

How reproducible:
Always

Steps to Reproduce:
1. Run the following program.

~~~
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void main(void)
{
    time_t t;
    struct tm tm;

    setenv("TZ", "Asia/Tokyo", 1);

    memset(&tm, 0, sizeof(tm));
    tm.tm_mday = 1;
    tm.tm_mon = 1;
    tm.tm_year = 2023;
    tm.tm_isdst = 1;

    t = mktime(&tm);
    printf("mktime(&tm) = %d\n", t);
    if (t < 0)
        perror(NULL);
}
~~~

2.
3.

Actual results:
mktime(&tm) = -1
Value too large for defined data type

Expected results:
mktime(&tm) = 1503779056

Additional info:

Comment 1 Florian Weimer 2022-07-25 10:17:27 UTC
Does the description of the bug need to be private?

Do you know if according to the data, 2023-01-01 is supposed to have DST active? Thanks.

Comment 2 Takayuki Nagata 2022-07-26 04:23:01 UTC
(In reply to Florian Weimer from comment #1)
> Does the description of the bug need to be private?

The description does not contain customer/sensitive data, and can be public.

> Do you know if according to the data, 2023-01-01 is supposed to have DST
> active? Thanks.

It appears to me that 2023-01-01 does not have DST in Asia/Tokyo.

(RHEL9: tzdata-2022a-1.el9_0)
# zdump -i /usr/share/zoneinfo/Asia/Tokyo 

TZ="/usr/share/zoneinfo/Asia/Tokyo"
-	-	+091859	LMT
1888-01-01	00	+09	JST
1948-05-02	01	+10	JDT	1
1948-09-12	00	+09	JST
1949-04-03	01	+10	JDT	1
1949-09-11	00	+09	JST
1950-05-07	01	+10	JDT	1
1950-09-10	00	+09	JST
1951-05-06	01	+10	JDT	1
1951-09-09	00	+09	JST

(RHEL8: tzdata-2020a-1.el8)
# zdump -i /usr/share/zoneinfo/Asia/Tokyo 

TZ="/usr/share/zoneinfo/Asia/Tokyo"
-	-	+091859	LMT
1888-01-01	00	+09	JST
1948-05-02	01	+10	JDT	1
1948-09-12	00	+09	JST
1949-04-03	01	+10	JDT	1
1949-09-11	00	+09	JST
1950-05-07	01	+10	JDT	1
1950-09-10	00	+09	JST
1951-05-06	01	+10	JDT	1
1951-09-09	00	+09	JST


However, If TZ=America/New_York is set, mktime() returns a time value for 2023-01-01 with DST as follows even if it does not have DST.

  mktime(&tm) = 1503825856

# zdump -i -c 2020,2025 /usr/share/zoneinfo/America/New_York 

TZ="/usr/share/zoneinfo/America/New_York"
-	-	-05	EST
2020-03-08	03	-04	EDT	1
2020-11-01	01	-05	EST
2021-03-14	03	-04	EDT	1
2021-11-07	01	-05	EST
2022-03-13	03	-04	EDT	1
2022-11-06	01	-05	EST
2023-03-12	03	-04	EDT	1
2023-11-05	01	-05	EST
2024-03-10	03	-04	EDT	1
2024-11-03	01	-05	EST


In addition, mktime() for "1951-08-01" with DST in Asia/Tokyo also fails on RHEL9.

  mktime(&tm) = -1
  Value too large for defined data type

Takayuki Nagata

Comment 3 Florian Weimer 2022-07-26 06:59:55 UTC
1951-08-01 works for me:

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

void main(void)
{
    time_t t;
    struct tm tm;

    setenv("TZ", "Asia/Tokyo", 1);

    memset(&tm, 0, sizeof(tm));
    tm.tm_mday = 1;
    tm.tm_mon = 7;
    tm.tm_year = 51;
    tm.tm_isdst = 1;

    t = mktime(&tm);
    printf("mktime(&tm) = %d\n", t);
    if (t < 0)
        perror(NULL);
}

mktime(&tm) = -581335200
Success

The test case from comment 0 actually tests 3923-02-01, not 2023-01-01. I had forgotten about the offsets from tm_mon and tm_year.

The current implementation treats tm_isdst > 0 as a strong indicator that only time information with the DST flag is valid as a result, and EOVERFLOW is used as an indicator that the search was unsuccessful (presumably because that's the only error code specified by POSIX).

Comment 4 Takayuki Nagata 2022-07-26 07:29:23 UTC
(In reply to Florian Weimer from comment #3)
> 1951-08-01 works for me:
> 
<snip>
> 
> The test case from comment 0 actually tests 3923-02-01, not 2023-01-01. I
> had forgotten about the offsets from tm_mon and tm_year.

I had also forgotten about the offsets... Thanks, it also works for me with 1951-08-01.

> The current implementation treats tm_isdst > 0 as a strong indicator that
> only time information with the DST flag is valid as a result, and EOVERFLOW
> is used as an indicator that the search was unsuccessful (presumably because
> that's the only error code specified by POSIX).

However, 2023-01-01 with tm_isdst = 1 in America/New_york is successful. It does not appear to me that DST is valid from the results of zdump.

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

void main(void)
{
    time_t t;
    struct tm tm;

    setenv("TZ", "America/New_York", 1);

    memset(&tm, 0, sizeof(tm));
    tm.tm_mday = 1;
    tm.tm_mon = 1 - 1;
    tm.tm_year = 2023 - 1900;
    tm.tm_isdst = 1;

    t = mktime(&tm);
    printf("mktime(&tm) = %d\n", t);
    if (t < 0)
        perror(NULL);
}

mktime(&tm) = 1672545600

Comment 5 Takayuki Nagata 2022-07-27 06:50:47 UTC
It appears to me that -EOVERFLOW errors depend on the following commit.

  https://sourceware.org/git/?p=glibc.git;a=commit;h=86aece3bfbd44538ba4fdc947872c81d4c5e6e61

In addition, it appears to me that the issue where 2023-01-01 and tm_isdst=1 fails in Asia/Tokyo but succeeds in America/New_York depend on the following duration_max.

mktime.c
~~~
444       /* The longest period of DST in tzdata2003a is 536454000 seconds
445          (e.g., America/Jujuy starting 1946-10-01 01:00).  The longest
446          period of non-DST is much longer, but it makes no real sense
447          to search for more than a year of non-DST, so use the DST
448          max.  */
449       int duration_max = 536454000;
~~~

So I think that the duration_max is not enough for Asia/Tokyo in this case.

Comment 6 Florian Weimer 2022-08-01 11:34:08 UTC
(In reply to Takayuki Nagata from comment #5)
> It appears to me that -EOVERFLOW errors depend on the following commit.
> 
>  
> https://sourceware.org/git/?p=glibc.git;a=commit;
> h=86aece3bfbd44538ba4fdc947872c81d4c5e6e61
> 
> In addition, it appears to me that the issue where 2023-01-01 and tm_isdst=1
> fails in Asia/Tokyo but succeeds in America/New_York depend on the following
> duration_max.
> 
> mktime.c
> ~~~
> 444       /* The longest period of DST in tzdata2003a is 536454000 seconds
> 445          (e.g., America/Jujuy starting 1946-10-01 01:00).  The longest
> 446          period of non-DST is much longer, but it makes no real sense
> 447          to search for more than a year of non-DST, so use the DST
> 448          max.  */
> 449       int duration_max = 536454000;
> ~~~
> 
> So I think that the duration_max is not enough for Asia/Tokyo in this case.

Thanks for digging into this. The search algorithm really isn't great. This has to be fixed upstream. It's not going to be an easy fix.

How critical is this to the customer? The answer will help us to prioritize this work.

Comment 8 Carlos O'Donell 2022-08-04 21:55:33 UTC
(In reply to Takayuki Nagata from comment #5)
> In addition, it appears to me that the issue where 2023-01-01 and tm_isdst=1
> fails in Asia/Tokyo but succeeds in America/New_York depend on the following
> duration_max.

It does indeed depend on duration_max. I have a test build I was experimenting
with here I expand the search heuristic, but this slows down the API in this
corner case and still doesn't return the exact result you had in RHEL8 (some
of the semantics of the search and forward calculation of the time value have
changed slightly).

I think the root cause of the problem is that you set tm_isdst to 1. Setting
it to 1 tells the algorithm "I know DST is active." And the algorithm tries
its best to convert your broken down time while complying with the requirement
you provided.

It would be better if you set tm_isdst to -1 as is specified in C99 and POSIX
and future standards, which allows mktime to tell you what the DST value was
that it found, rather than to use it as a requirement.

https://pubs.opengroup.org/onlinepubs/9699919799/functions/mktime.html
~~~
A negative value for tm_isdst shall cause mktime() to attempt to determine
whether Daylight Savings Time is in effect for the specified time.
~~~

ISO C99 (N1256)
~~~
page 340, footnote 256)
Thus, a positive or zero value for tm_isdst causes the mktime function to presume initially that
Daylight Saving Time, respectively, is or is not in effect for the specified time. A negative value
causes it to attempt to determine whether Daylight Saving Time is in effect for the specified time.
~~~

Is setting tm_isdst to -1 a possible workaround for the customer?

~~~
    setenv("TZ", "Asia/Tokyo", 1);

    memset(&tm, 0, sizeof(tm));
    tm.tm_mday = 1;
    tm.tm_mon = 1 - 1;
    tm.tm_year = 2023 - 1900;
    tm.tm_isdst = -1;
    t = mktime(&tm);
    printf("mktime(&tm) = %ld\n", (long int)t);
    if (t < 0)
        perror(NULL);
~~~

Example RHEL7:
mktime(&tm) = 1672498800

Example RHEL8:
mktime(&tm) = 1672498800

Example RHEL9:
mktime(&tm) = 1672498800

Setting tm_isdst to -1 should result in the same values as before
and avoid the EOVERFLOW scenario.

Comment 15 DJ Delorie 2022-08-25 02:54:23 UTC
I have put a repo that includes the proposed upstream patch for this bug here:
https://people.redhat.com/dj/bz2110357/glibc-2.34-40.0.0.testfix.1.bz2110357.el9-scratch.repo
With this patch, mktime() in zones that don't have a findable DST will assume a one hour DST offset instead of returning -1

Comment 23 RHEL Program Management 2022-11-09 08:47:47 UTC
Quality Engineering Management has reviewed and declined this request. You may appeal this decision by reopening this request.

Comment 24 Martin Cermak 2022-11-09 08:55:05 UTC
The flip to CLOSED/WONTFIX was not intentional.  It was a consequence of a mis-click.  Apologies.  Let me try to fix the bug status now.https://bugzilla.redhat.com/show_bug.cgi?id=2110357

Comment 26 Arjun Shankar 2022-11-11 14:12:04 UTC
I will sync from ustream release/2.34/master via bug 2141728 to get this fix in.

Comment 30 errata-xmlrpc 2023-05-09 08:16:01 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory (glibc bug fix and enhancement update), and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2023:2481


Note You need to log in before you can comment on or make changes to this bug.