Make time zone file parser more robust [BZ #17715]

This commit is contained in:
Florian Weimer 2015-04-24 17:34:47 +02:00
parent ed159672eb
commit 42261ad731
11 changed files with 468 additions and 226 deletions

View File

@ -1,3 +1,30 @@
2015-04-24 Florian Weimer <fweimer@redhat.com>
[BZ #17715]
* time/tzfile.c (__tzfile_read): Check for large values of
tzh_ttisstdcnt and tzh_ttisgmtcnt. Use malloc instead of alloca.
* time/tzset.c (__tzstring_len): New function, based on the old
__tzstring function.
(__tzstring): Call __tzstring_len.
(parse_tzname): New helper function extracted from
__tzset_parse_tz. Call __tzstring_len, without making a copy of
the input string.
(parse_offset): New helper function extracted from
__tzset_parse_tz. Replace switch with fallthrough with
initialization before sscanf.
(parse_rule): Likewise.
(__tzset_parse_tz): Rewrite using the new helper functions. Use
new-style function definition.
* timezone/Makefile (tests): Add tst-tzset.
(tst-tzset.out): Dependencies on time zone files.
(tst-tzset-ENV): Set TZDIR.
(testdata/XT%): Copy crafted time zone files.
* timezone/README: Mention crafted time zone files.
* timezone/testdata/XT1, timezone/testdata/XT2,
timezone/testdata/XT3, timezone/testdata/XT4: New time zone test
files.
* timezone/tst-tzset.c: New test.
2015-04-24 Florian Weimer <fweimer@redhat.com>
* Makeconfig (+gccwarn): Remove -Winline.

18
NEWS
View File

@ -11,12 +11,12 @@ Version 2.22
4719, 6792, 13064, 14094, 14841, 14906, 15319, 15467, 15790, 15969, 16351,
16512, 16560, 16783, 16850, 17090, 17195, 17269, 17523, 17542, 17569,
17588, 17596, 17620, 17621, 17628, 17631, 17711, 17776, 17779, 17792,
17836, 17912, 17916, 17930, 17932, 17944, 17949, 17964, 17965, 17967,
17969, 17978, 17987, 17991, 17996, 17998, 17999, 18019, 18020, 18029,
18030, 18032, 18036, 18038, 18039, 18042, 18043, 18046, 18047, 18068,
18080, 18093, 18100, 18104, 18110, 18111, 18128, 18138, 18185, 18197,
18206, 18210, 18211, 18247, 18287.
17588, 17596, 17620, 17621, 17628, 17631, 17711, 17715, 17776, 17779,
17792, 17836, 17912, 17916, 17930, 17932, 17944, 17949, 17964, 17965,
17967, 17969, 17978, 17987, 17991, 17996, 17998, 17999, 18019, 18020,
18029, 18030, 18032, 18036, 18038, 18039, 18042, 18043, 18046, 18047,
18068, 18080, 18093, 18100, 18104, 18110, 18111, 18128, 18138, 18185,
18197, 18206, 18210, 18211, 18247, 18287.
* Cache information can be queried via sysconf() function on s390 e.g. with
_SC_LEVEL1_ICACHE_SIZE as argument.
@ -28,6 +28,12 @@ Version 2.22
potentially arbitrary code execution, using crafted, but syntactically
valid DNS responses. (CVE-2015-1781)
* The time zone file parser has been made more robust against crafted time
zone files, avoiding heap buffer overflows related to the processing of
the tzh_ttisstdcnt and tzh_ttisgmtcnt fields, and a stack overflow due to
large time zone data files. Overly long time zone specifiers in the TZ
variable no longer result in stack overflows and crashes.
* A powerpc and powerpc64 optimization for TLS, similar to TLS descriptors
for LD and GD on x86 and x86-64, has been implemented. You will need
binutils-2.24 or later to enable this optimization.

View File

@ -200,6 +200,9 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types))
goto lose;
/* For platforms with 64-bit time_t we use the new format if available. */
if (sizeof (time_t) == 8 && trans_width == 4
&& tzhead.tzh_version[0] != '\0')
@ -434,13 +437,21 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
goto lose;
tzspec_len = st.st_size - off - 1;
char *tzstr = alloca (tzspec_len);
if (tzspec_len == 0)
goto lose;
char *tzstr = malloc (tzspec_len);
if (tzstr == NULL)
goto lose;
if (getc_unlocked (f) != '\n'
|| (__fread_unlocked (tzstr, 1, tzspec_len - 1, f)
!= tzspec_len - 1))
goto lose;
{
free (tzstr);
goto lose;
}
tzstr[tzspec_len - 1] = '\0';
tzspec = __tzstring (tzstr);
free (tzstr);
}
/* Don't use an empty TZ string. */

View File

@ -18,6 +18,7 @@
#include <ctype.h>
#include <errno.h>
#include <bits/libc-lock.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@ -82,15 +83,14 @@ struct tzstring_l
static struct tzstring_l *tzstring_list;
/* Allocate a permanent home for S. It will never be moved or deallocated,
but may share space with other strings.
Don't modify the returned string. */
char *
__tzstring (const char *s)
/* Allocate a permanent home for the first LEN characters of S. It
will never be moved or deallocated, but may share space with other
strings. Don't modify the returned string. */
static char *
__tzstring_len (const char *s, size_t len)
{
char *p;
struct tzstring_l *t, *u, *new;
size_t len = strlen (s);
/* Walk the list and look for a match. If this string is the same
as the end of an already-allocated string, it can share space. */
@ -98,7 +98,7 @@ __tzstring (const char *s)
if (len <= t->len)
{
p = &t->data[t->len - len];
if (strcmp (s, p) == 0)
if (memcmp (s, p, len) == 0)
return p;
}
@ -109,7 +109,8 @@ __tzstring (const char *s)
new->next = NULL;
new->len = len;
strcpy (new->data, s);
memcpy (new->data, s, len);
new->data[len] = '\0';
if (u)
u->next = new;
@ -118,6 +119,15 @@ __tzstring (const char *s)
return new->data;
}
/* Allocate a permanent home for S. It will never be moved or
deallocated, but may share space with other strings. Don't modify
the returned string. */
char *
__tzstring (const char *s)
{
return __tzstring_len (s, strlen (s));
}
/* Maximum length of a timezone name. tzset_internal keeps this up to date
(never decreasing it) when ! __use_tzfile.
@ -164,234 +174,215 @@ compute_offset (unsigned int ss, unsigned int mm, unsigned int hh)
return min (ss, 59) + min (mm, 59) * 60 + min (hh, 24) * 60 * 60;
}
/* Parses the time zone name at *TZP, and writes a pointer to an
interned string to tz_rules[WHICHRULE].name. On success, advances
*TZP, and returns true. Returns false otherwise. */
static bool
parse_tzname (const char **tzp, int whichrule)
{
const char *start = *tzp;
const char *p = start;
while (('a' <= *p && *p <= 'z')
|| ('A' <= *p && *p <= 'Z'))
++p;
size_t len = p - start;
if (len < 3)
{
p = *tzp;
if (__glibc_unlikely (*p++ != '<'))
return false;
start = p;
while (('a' <= *p && *p <= 'z')
|| ('A' <= *p && *p <= 'Z')
|| ('0' <= *p && *p <= '9')
|| *p == '+' || *p == '-')
++p;
len = p - start;
if (*p++ != '>' || len < 3)
return false;
}
tz_rules[whichrule].name = __tzstring_len (start, len);
*tzp = p;
return true;
}
/* Parses the time zone offset at *TZP, and writes it to
tz_rules[WHICHRULE].offset. Returns true if the parse was
successful. */
static bool
parse_offset (const char **tzp, int whichrule)
{
const char *tz = *tzp;
if (whichrule == 0
&& (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz))))
return false;
long sign;
if (*tz == '-' || *tz == '+')
sign = *tz++ == '-' ? 1L : -1L;
else
sign = -1L;
*tzp = tz;
unsigned short int hh;
unsigned short mm = 0;
unsigned short ss = 0;
int consumed = 0;
if (sscanf (tz, "%hu%n:%hu%n:%hu%n",
&hh, &consumed, &mm, &consumed, &ss, &consumed) > 0)
tz_rules[whichrule].offset = sign * compute_offset (ss, mm, hh);
else
/* Nothing could be parsed. */
if (whichrule == 0)
{
/* Standard time defaults to offset zero. */
tz_rules[0].offset = 0;
return false;
}
else
/* DST defaults to one hour later than standard time. */
tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
*tzp = tz + consumed;
return true;
}
/* Parses the standard <-> DST rules at *TZP. Updates
tz_rule[WHICHRULE]. On success, advances *TZP and returns true.
Otherwise, returns false. */
static bool
parse_rule (const char **tzp, int whichrule)
{
const char *tz = *tzp;
tz_rule *tzr = &tz_rules[whichrule];
/* Ignore comma to support string following the incorrect
specification in early POSIX.1 printings. */
tz += *tz == ',';
/* Get the date of the change. */
if (*tz == 'J' || isdigit (*tz))
{
char *end;
tzr->type = *tz == 'J' ? J1 : J0;
if (tzr->type == J1 && !isdigit (*++tz))
return false;
unsigned long int d = strtoul (tz, &end, 10);
if (end == tz || d > 365)
return false;
if (tzr->type == J1 && d == 0)
return false;
tzr->d = d;
tz = end;
}
else if (*tz == 'M')
{
tzr->type = M;
int consumed;
if (sscanf (tz, "M%hu.%hu.%hu%n",
&tzr->m, &tzr->n, &tzr->d, &consumed) != 3
|| tzr->m < 1 || tzr->m > 12
|| tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
return false;
tz += consumed;
}
else if (*tz == '\0')
{
/* Daylight time rules in the U.S. are defined in the U.S. Code,
Title 15, Chapter 6, Subchapter IX - Standard Time. These
dates were established by Congress in the Energy Policy Act
of 2005 [Pub. L. no. 109-58, 119 Stat 594 (2005)].
Below is the equivalent of "M3.2.0,M11.1.0" [/2 not needed
since 2:00AM is the default]. */
tzr->type = M;
if (tzr == &tz_rules[0])
{
tzr->m = 3;
tzr->n = 2;
tzr->d = 0;
}
else
{
tzr->m = 11;
tzr->n = 1;
tzr->d = 0;
}
}
else
return false;
if (*tz != '\0' && *tz != '/' && *tz != ',')
return false;
else if (*tz == '/')
{
/* Get the time of day of the change. */
int negative;
++tz;
if (*tz == '\0')
return false;
negative = *tz == '-';
tz += negative;
/* Default to 2:00 AM. */
unsigned short hh = 2;
unsigned short mm = 0;
unsigned short ss = 0;
int consumed = 0;
sscanf (tz, "%hu%n:%hu%n:%hu%n",
&hh, &consumed, &mm, &consumed, &ss, &consumed);;
tz += consumed;
tzr->secs = (negative ? -1 : 1) * ((hh * 60 * 60) + (mm * 60) + ss);
}
else
/* Default to 2:00 AM. */
tzr->secs = 2 * 60 * 60;
tzr->computed_for = -1;
*tzp = tz;
return true;
}
/* Parse the POSIX TZ-style string. */
void
__tzset_parse_tz (tz)
const char *tz;
__tzset_parse_tz (const char *tz)
{
unsigned short int hh, mm, ss;
/* Clear out old state and reset to unnamed UTC. */
memset (tz_rules, '\0', sizeof tz_rules);
tz_rules[0].name = tz_rules[1].name = "";
/* Get the standard timezone name. */
char *tzbuf = strdupa (tz);
int consumed;
if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1)
if (parse_tzname (&tz, 0) && parse_offset (&tz, 0))
{
/* Check for the quoted version. */
char *wp = tzbuf;
if (__glibc_unlikely (*tz++ != '<'))
goto out;
while (isalnum (*tz) || *tz == '+' || *tz == '-')
*wp++ = *tz++;
if (__glibc_unlikely (*tz++ != '>' || wp - tzbuf < 3))
goto out;
*wp = '\0';
}
else if (__glibc_unlikely (consumed < 3))
goto out;
else
tz += consumed;
tz_rules[0].name = __tzstring (tzbuf);
/* Figure out the standard offset from UTC. */
if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
goto out;
if (*tz == '-' || *tz == '+')
tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
else
tz_rules[0].offset = -1L;
switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
&hh, &consumed, &mm, &consumed, &ss, &consumed))
{
default:
tz_rules[0].offset = 0;
goto out;
case 1:
mm = 0;
case 2:
ss = 0;
case 3:
break;
}
tz_rules[0].offset *= compute_offset (ss, mm, hh);
tz += consumed;
/* Get the DST timezone name (if any). */
if (*tz != '\0')
{
if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1)
/* Get the DST timezone name (if any). */
if (*tz != '\0')
{
/* Check for the quoted version. */
char *wp = tzbuf;
const char *rp = tz;
if (__glibc_unlikely (*rp++ != '<'))
/* Punt on name, set up the offsets. */
goto done_names;
while (isalnum (*rp) || *rp == '+' || *rp == '-')
*wp++ = *rp++;
if (__glibc_unlikely (*rp++ != '>' || wp - tzbuf < 3))
/* Punt on name, set up the offsets. */
goto done_names;
*wp = '\0';
tz = rp;
}
else if (__glibc_unlikely (consumed < 3))
/* Punt on name, set up the offsets. */
goto done_names;
else
tz += consumed;
tz_rules[1].name = __tzstring (tzbuf);
/* Figure out the DST offset from GMT. */
if (*tz == '-' || *tz == '+')
tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
else
tz_rules[1].offset = -1L;
switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
&hh, &consumed, &mm, &consumed, &ss, &consumed))
{
default:
/* Default to one hour later than standard time. */
tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
break;
case 1:
mm = 0;
case 2:
ss = 0;
case 3:
tz_rules[1].offset *= compute_offset (ss, mm, hh);
tz += consumed;
break;
}
if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
{
/* There is no rule. See if there is a default rule file. */
__tzfile_default (tz_rules[0].name, tz_rules[1].name,
tz_rules[0].offset, tz_rules[1].offset);
if (__use_tzfile)
if (parse_tzname (&tz, 1))
{
free (old_tz);
old_tz = NULL;
return;
}
}
}
else
{
/* There is no DST. */
tz_rules[1].name = tz_rules[0].name;
tz_rules[1].offset = tz_rules[0].offset;
goto out;
}
done_names:
/* Figure out the standard <-> DST rules. */
for (unsigned int whichrule = 0; whichrule < 2; ++whichrule)
{
tz_rule *tzr = &tz_rules[whichrule];
/* Ignore comma to support string following the incorrect
specification in early POSIX.1 printings. */
tz += *tz == ',';
/* Get the date of the change. */
if (*tz == 'J' || isdigit (*tz))
{
char *end;
tzr->type = *tz == 'J' ? J1 : J0;
if (tzr->type == J1 && !isdigit (*++tz))
goto out;
unsigned long int d = strtoul (tz, &end, 10);
if (end == tz || d > 365)
goto out;
if (tzr->type == J1 && d == 0)
goto out;
tzr->d = d;
tz = end;
}
else if (*tz == 'M')
{
tzr->type = M;
if (sscanf (tz, "M%hu.%hu.%hu%n",
&tzr->m, &tzr->n, &tzr->d, &consumed) != 3
|| tzr->m < 1 || tzr->m > 12
|| tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
goto out;
tz += consumed;
}
else if (*tz == '\0')
{
/* Daylight time rules in the U.S. are defined in the
U.S. Code, Title 15, Chapter 6, Subchapter IX - Standard
Time. These dates were established by Congress in the
Energy Policy Act of 2005 [Pub. L. no. 109-58, 119 Stat 594
(2005)].
Below is the equivalent of "M3.2.0,M11.1.0" [/2 not needed
since 2:00AM is the default]. */
tzr->type = M;
if (tzr == &tz_rules[0])
{
tzr->m = 3;
tzr->n = 2;
tzr->d = 0;
}
else
{
tzr->m = 11;
tzr->n = 1;
tzr->d = 0;
parse_offset (&tz, 1);
if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
{
/* There is no rule. See if there is a default rule
file. */
__tzfile_default (tz_rules[0].name, tz_rules[1].name,
tz_rules[0].offset, tz_rules[1].offset);
if (__use_tzfile)
{
free (old_tz);
old_tz = NULL;
return;
}
}
}
/* Figure out the standard <-> DST rules. */
if (parse_rule (&tz, 0))
parse_rule (&tz, 1);
}
else
goto out;
if (*tz != '\0' && *tz != '/' && *tz != ',')
goto out;
else if (*tz == '/')
{
/* Get the time of day of the change. */
int negative;
++tz;
if (*tz == '\0')
goto out;
negative = *tz == '-';
tz += negative;
consumed = 0;
switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
&hh, &consumed, &mm, &consumed, &ss, &consumed))
{
default:
hh = 2; /* Default to 2:00 AM. */
case 1:
mm = 0;
case 2:
ss = 0;
case 3:
break;
}
tz += consumed;
tzr->secs = (negative ? -1 : 1) * ((hh * 60 * 60) + (mm * 60) + ss);
/* There is no DST. */
tz_rules[1].name = tz_rules[0].name;
tz_rules[1].offset = tz_rules[0].offset;
}
else
/* Default to 2:00 AM. */
tzr->secs = 2 * 60 * 60;
tzr->computed_for = -1;
}
out:
update_vars ();
}

View File

@ -25,7 +25,7 @@ include ../Makeconfig
extra-objs := scheck.o ialloc.o
others := zdump zic
tests := test-tz tst-timezone
tests := test-tz tst-timezone tst-tzset
# pacificnew doesn't compile; if it is to be used, it should be included in
# northamerica.
@ -90,9 +90,11 @@ $(objpfx)tst-timezone.out: $(addprefix $(testdata)/, \
Australia/Melbourne \
America/Sao_Paulo Asia/Tokyo \
Europe/London)
$(objpfx)tst-tzset.out: $(addprefix $(testdata)/XT, 1 2 3 4)
test-tz-ENV = TZDIR=$(testdata)
tst-timezone-ENV = TZDIR=$(testdata)
tst-tzset-ENV = TZDIR=$(testdata)
# Note this must come second in the deps list for $(built-program-cmd) to work.
zic-deps = $(objpfx)zic $(leapseconds) yearistype
@ -114,6 +116,8 @@ $(testdata)/America/Sao_Paulo: southamerica $(zic-deps)
$(testdata)/Asia/Tokyo: asia $(zic-deps)
$(build-testdata)
$(testdata)/XT%: testdata/XT%
cp $< $@
$(objpfx)tzselect: tzselect.ksh $(common-objpfx)config.make
sed -e 's|/bin/bash|$(BASH)|' \

View File

@ -15,3 +15,6 @@ version of the tzcode and tzdata packages.
These packages may be found at ftp://ftp.iana.org/tz/releases/. Commentary
should be addressed to tz@iana.org.
The subdirectory testdata contains manually edited data files for
regression testing purposes.

BIN
timezone/testdata/XT1 vendored Normal file

Binary file not shown.

BIN
timezone/testdata/XT2 vendored Normal file

Binary file not shown.

BIN
timezone/testdata/XT3 vendored Normal file

Binary file not shown.

BIN
timezone/testdata/XT4 vendored Normal file

Binary file not shown.

200
timezone/tst-tzset.c Normal file
View File

@ -0,0 +1,200 @@
/* tzset tests with crafted time zone data.
Copyright (C) 2015 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#define _GNU_SOURCE 1
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <time.h>
#include <unistd.h>
static int do_test (void);
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
/* Returns the name of a large TZ file. */
static char *
create_tz_file (off64_t size)
{
char *path;
int fd = create_temp_file ("tst-tzset-", &path);
if (fd < 0)
exit (1);
// Reopen for large-file support.
close (fd);
fd = open64 (path, O_WRONLY);
if (fd < 0)
{
printf ("open64 (%s) failed: %m\n", path);
exit (1);
}
static const char data[] = {
0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x58, 0x54, 0x47, 0x00, 0x00, 0x00,
0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x04, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x58, 0x54, 0x47, 0x00, 0x00,
0x00, 0x0a, 0x58, 0x54, 0x47, 0x30, 0x0a
};
ssize_t ret = write (fd, data, sizeof (data));
if (ret < 0)
{
printf ("write failed: %m\n");
exit (1);
}
if ((size_t) ret != sizeof (data))
{
printf ("Short write\n");
exit (1);
}
if (lseek64 (fd, size, SEEK_CUR) < 0)
{
printf ("lseek failed: %m\n");
close (fd);
return NULL;
}
if (write (fd, "", 1) != 1)
{
printf ("Single-byte write failed\n");
close (fd);
return NULL;
}
if (close (fd) != 0)
{
printf ("close failed: %m\n");
exit (1);
}
return path;
}
static void
test_tz_file (off64_t size)
{
char *path = create_tz_file (size);
if (setenv ("TZ", path, 1) < 0)
{
printf ("setenv failed: %m\n");
exit (1);
}
tzset ();
free (path);
}
static int
do_test (void)
{
/* Limit the size of the process. Otherwise, some of the tests will
consume a lot of resources. */
{
struct rlimit limit;
if (getrlimit (RLIMIT_AS, &limit) != 0)
{
printf ("getrlimit (RLIMIT_AS) failed: %m\n");
return 1;
}
long target = 512 * 1024 * 1024;
if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
{
limit.rlim_cur = 512 * 1024 * 1024;
if (setrlimit (RLIMIT_AS, &limit) != 0)
{
printf ("setrlimit (RLIMIT_AS) failed: %m\n");
return 1;
}
}
}
int errors = 0;
for (int i = 1; i <= 4; ++i)
{
char tz[16];
snprintf (tz, sizeof (tz), "XT%d", i);
if (setenv ("TZ", tz, 1) < 0)
{
printf ("setenv failed: %m\n");
return 1;
}
tzset ();
if (strcmp (tzname[0], tz) == 0)
{
printf ("Unexpected success for %s\n", tz);
++errors;
}
}
/* Large TZ files. */
/* This will succeed on 64-bit architectures, and fail on 32-bit
architectures. It used to crash on 32-bit. */
test_tz_file (64 * 1024 * 1024);
/* This will fail on 64-bit and 32-bit architectures. It used to
cause a test timeout on 64-bit and crash on 32-bit if the TZ file
open succeeded for some reason (it does not use O_LARGEFILE in
regular builds). */
test_tz_file (4LL * 1024 * 1024 * 1024 - 6);
/* Large TZ variables. */
{
size_t length = 64 * 1024 * 1024;
char *value = malloc (length + 1);
if (value == NULL)
{
puts ("malloc failed: %m");
return 1;
}
value[length] = '\0';
memset (value, ' ', length);
value[0] = 'U';
value[1] = 'T';
value[2] = 'C';
if (setenv ("TZ", value, 1) < 0)
{
printf ("setenv failed: %m\n");
return 1;
}
tzset ();
memset (value, '0', length);
value[0] = '<';
value[length - 1] = '>';
if (setenv ("TZ", value, 1) < 0)
{
printf ("setenv failed: %m\n");
return 1;
}
tzset ();
}
return errors > 0;
}