Message ID | 54B6E99E.4030109@redhat.com |
---|---|
State | New |
Headers | show |
On Wed, Jan 14, 2015 at 11:11:42PM +0100, Florian Weimer wrote: > This patch removes two different unbounded alloca calls, and also > fixes the TZ parser issue identified here: > > https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772705 > > This is not my preferred approach. I would rather like to sanitize > TZ in AT_SECURE mode, so that specifying a file from a non-default > TZDIR does not work. However, this alternative approach is a bit > involved because the current ld.so setup code is not fit to handle > content-dependent environment variable scrubbing. Doing the scrubbing in the dynamic linker does not seem appropriate anyway. It wouldn't solve the problem for static-linked binaries and it would wrongly remove environment entries rather than just ignoring them. Instead, tzset should be doing its own path enforcement based on the presence of getauxval(AT_SECURE) or similar (e.g. the proposed issetugid function). Rich
On 01/15/2015 02:39 PM, Rich Felker wrote: > On Wed, Jan 14, 2015 at 11:11:42PM +0100, Florian Weimer wrote: >> This patch removes two different unbounded alloca calls, and also >> fixes the TZ parser issue identified here: >> >> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772705 >> >> This is not my preferred approach. I would rather like to sanitize >> TZ in AT_SECURE mode, so that specifying a file from a non-default >> TZDIR does not work. However, this alternative approach is a bit >> involved because the current ld.so setup code is not fit to handle >> content-dependent environment variable scrubbing. > > Doing the scrubbing in the dynamic linker does not seem appropriate > anyway. It wouldn't solve the problem for static-linked binaries and I have working scrubbing for statically linked binaries, I think. > it would wrongly remove environment entries rather than just ignoring > them. Instead, tzset should be doing its own path enforcement based on > the presence of getauxval(AT_SECURE) or similar (e.g. the proposed > issetugid function). We already do that, but we aren't consistent about it: We scrub TZDIR unconditionally (which is cleared in AT_SECURE mode), but we pass TZ variables containing absolute paths to subprocesses. The latter means that the TZDIR scrubbing isn't effective.
On Thu, Jan 15, 2015 at 02:45:55PM +0100, Florian Weimer wrote: > On 01/15/2015 02:39 PM, Rich Felker wrote: > >On Wed, Jan 14, 2015 at 11:11:42PM +0100, Florian Weimer wrote: > >>This patch removes two different unbounded alloca calls, and also > >>fixes the TZ parser issue identified here: > >> > >> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772705 > >> > >>This is not my preferred approach. I would rather like to sanitize > >>TZ in AT_SECURE mode, so that specifying a file from a non-default > >>TZDIR does not work. However, this alternative approach is a bit > >>involved because the current ld.so setup code is not fit to handle > >>content-dependent environment variable scrubbing. > > > >Doing the scrubbing in the dynamic linker does not seem appropriate > >anyway. It wouldn't solve the problem for static-linked binaries and > > I have working scrubbing for statically linked binaries, I think. > > >it would wrongly remove environment entries rather than just ignoring > >them. Instead, tzset should be doing its own path enforcement based on > >the presence of getauxval(AT_SECURE) or similar (e.g. the proposed > >issetugid function). > > We already do that, but we aren't consistent about it: We scrub > TZDIR unconditionally (which is cleared in AT_SECURE mode), but we > pass TZ variables containing absolute paths to subprocesses. The > latter means that the TZDIR scrubbing isn't effective. I fail to see how removing env vars behind the program's back is conforming. I understand that the _intent_ is to improve security, but IMO any contract violation such as this is a potential cause of vulnerabilities in itself (e.g. as a silly example, suppose the child process you were executing is a tool that examines the environment and exits with 0/1 to tell if the environment contained anything dangerous). Rich
On Wed, 14 Jan 2015, Florian Weimer wrote: > @@ -434,6 +437,10 @@ __tzfile_read (const char *file, size_t extra, char **extrap) > goto lose; > > tzspec_len = st.st_size - off - 1; > + if (tzspec_len >= 256) > + /* POSIX time zone specifiers are much shorter than 256 > + characters. */ > + goto lose; > char *tzstr = alloca (tzspec_len); > if (getc_unlocked (f) != '\n' > || (__fread_unlocked (tzstr, 1, tzspec_len - 1, f) Is it possible to have tzspec_len == 0 here? The code doesn't look safe if tzspec_len is 0 - it would pass (size_t)-1 to __fread_unlocked. This code is for the case where time_t is 4-byte (and so size_t is 4-byte). tzspec_len is of type size_t. st.st_size is of type off64_t (st is struct stat64), so 8-byte. If st.st_size < off + 2 we didn't get here, but if st.st_size is off + 4GB + 1 it seems to me you could then get tzspec_len being 0. (This file is opened with fopen not fopen64 so the open should fail if it's a large file at fopen time, but one might suppose it only becomes a large file between the fopen call and the fstat64 call.)
On 01/15/2015 09:02 AM, Rich Felker wrote: > On Thu, Jan 15, 2015 at 02:45:55PM +0100, Florian Weimer wrote: >> On 01/15/2015 02:39 PM, Rich Felker wrote: >>> On Wed, Jan 14, 2015 at 11:11:42PM +0100, Florian Weimer wrote: >>>> This patch removes two different unbounded alloca calls, and also >>>> fixes the TZ parser issue identified here: >>>> >>>> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=772705 >>>> >>>> This is not my preferred approach. I would rather like to sanitize >>>> TZ in AT_SECURE mode, so that specifying a file from a non-default >>>> TZDIR does not work. However, this alternative approach is a bit >>>> involved because the current ld.so setup code is not fit to handle >>>> content-dependent environment variable scrubbing. >>> >>> Doing the scrubbing in the dynamic linker does not seem appropriate >>> anyway. It wouldn't solve the problem for static-linked binaries and >> >> I have working scrubbing for statically linked binaries, I think. >> >>> it would wrongly remove environment entries rather than just ignoring >>> them. Instead, tzset should be doing its own path enforcement based on >>> the presence of getauxval(AT_SECURE) or similar (e.g. the proposed >>> issetugid function). >> >> We already do that, but we aren't consistent about it: We scrub >> TZDIR unconditionally (which is cleared in AT_SECURE mode), but we >> pass TZ variables containing absolute paths to subprocesses. The >> latter means that the TZDIR scrubbing isn't effective. > > I fail to see how removing env vars behind the program's back is > conforming. I understand that the _intent_ is to improve security, but > IMO any contract violation such as this is a potential cause of > vulnerabilities in itself (e.g. as a silly example, suppose the child > process you were executing is a tool that examines the environment and > exits with 0/1 to tell if the environment contained anything > dangerous). I can't help but agree. Removing env vars is a bad idea, ignoring them is the only way I'd handle this. c.
On 01/16/2015 09:27 PM, Carlos O'Donell wrote: >>> We already do that, but we aren't consistent about it: We scrub >>> TZDIR unconditionally (which is cleared in AT_SECURE mode), but we >>> pass TZ variables containing absolute paths to subprocesses. The >>> latter means that the TZDIR scrubbing isn't effective. >> >> I fail to see how removing env vars behind the program's back is >> conforming. I understand that the _intent_ is to improve security, but >> IMO any contract violation such as this is a potential cause of >> vulnerabilities in itself (e.g. as a silly example, suppose the child >> process you were executing is a tool that examines the environment and >> exits with 0/1 to tell if the environment contained anything >> dangerous). > > I can't help but agree. Removing env vars is a bad idea, ignoring them > is the only way I'd handle this. On the other hand, looking at TZDIR at all is non-confirming as well. It is certainly easier not to scrub the TZ variable because no additional patch is required. But I'm worried that the TZ parser crosses a trust boundary, and we'll have to patch it again (and again). Ulrich already tried to robustify the parser in commit 97ac2654b2d831acaa18a2b018b0736245903fd2, BZ #13506, but missed a few corner cases. I don't feel very confident that my latest patch addresses all remaining issues.
On Tue, Jan 20, 2015 at 02:18:01PM +0100, Florian Weimer wrote: > On 01/16/2015 09:27 PM, Carlos O'Donell wrote: > >>> We already do that, but we aren't consistent about it: We scrub > >>> TZDIR unconditionally (which is cleared in AT_SECURE mode), but we > >>> pass TZ variables containing absolute paths to subprocesses. The > >>> latter means that the TZDIR scrubbing isn't effective. > >> > >> I fail to see how removing env vars behind the program's back is > >> conforming. I understand that the _intent_ is to improve security, but > >> IMO any contract violation such as this is a potential cause of > >> vulnerabilities in itself (e.g. as a silly example, suppose the child > >> process you were executing is a tool that examines the environment and > >> exits with 0/1 to tell if the environment contained anything > >> dangerous). > > > > I can't help but agree. Removing env vars is a bad idea, ignoring them > > is the only way I'd handle this. > > On the other hand, looking at TZDIR at all is non-confirming as well. Are you sure? The way in which the TZ is processed when it's not a POSIX TZ string is implementation-defined. Defining that to include processing of the TZDIR environment variable is perfectly acceptable. These interfaces are already permitted to access the environment so there's no issue with concurrent environment access. Rich
On 01/20/2015 04:14 PM, Rich Felker wrote: > On Tue, Jan 20, 2015 at 02:18:01PM +0100, Florian Weimer wrote: >> On 01/16/2015 09:27 PM, Carlos O'Donell wrote: >>>>> We already do that, but we aren't consistent about it: We scrub >>>>> TZDIR unconditionally (which is cleared in AT_SECURE mode), but we >>>>> pass TZ variables containing absolute paths to subprocesses. The >>>>> latter means that the TZDIR scrubbing isn't effective. >>>> >>>> I fail to see how removing env vars behind the program's back is >>>> conforming. I understand that the _intent_ is to improve security, but >>>> IMO any contract violation such as this is a potential cause of >>>> vulnerabilities in itself (e.g. as a silly example, suppose the child >>>> process you were executing is a tool that examines the environment and >>>> exits with 0/1 to tell if the environment contained anything >>>> dangerous). >>> >>> I can't help but agree. Removing env vars is a bad idea, ignoring them >>> is the only way I'd handle this. >> >> On the other hand, looking at TZDIR at all is non-confirming as well. > > Are you sure? No, mainly because I'm not entirely sure what a POSIX TZ string should look like. I tried this, based on some test case: $ TZ=AEST-10AEDST-11,M10.5.0,M3.5.0 strace -eopen date open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/usr/share/zoneinfo/AEST-10AEDST-11,M10.5.0,M3.5.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) Wed Jan 21 02:30:07 AEDST 2015 +++ exited with 0 +++ $ TZDIR=/tmp TZ=AEST-10AEDST-11,M10.5.0,M3.5.0 strace -eopen date open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/tmp/AEST-10AEDST-11,M10.5.0,M3.5.0", O_RDONLY|O_CLOEXEC) = 3 Tue Jan 20 15:30:14 UTC 2015 +++ exited with 0 +++ The file is copied from /usr/share/zoneinfo/UTC. This seems to suggest that the glibc behavior is non-compliant.
Florian Weimer wrote:
> This seems to suggest that the glibc behavior is non-compliant.
No, because POSIX reserves the environment variable name TZDIR for the
implementation (just as it reserves all upper-case-only names).
On Tue, Jan 20, 2015 at 04:37:01PM +0100, Florian Weimer wrote: > On 01/20/2015 04:14 PM, Rich Felker wrote: > > On Tue, Jan 20, 2015 at 02:18:01PM +0100, Florian Weimer wrote: > >> On 01/16/2015 09:27 PM, Carlos O'Donell wrote: > >>>>> We already do that, but we aren't consistent about it: We scrub > >>>>> TZDIR unconditionally (which is cleared in AT_SECURE mode), but we > >>>>> pass TZ variables containing absolute paths to subprocesses. The > >>>>> latter means that the TZDIR scrubbing isn't effective. > >>>> > >>>> I fail to see how removing env vars behind the program's back is > >>>> conforming. I understand that the _intent_ is to improve security, but > >>>> IMO any contract violation such as this is a potential cause of > >>>> vulnerabilities in itself (e.g. as a silly example, suppose the child > >>>> process you were executing is a tool that examines the environment and > >>>> exits with 0/1 to tell if the environment contained anything > >>>> dangerous). > >>> > >>> I can't help but agree. Removing env vars is a bad idea, ignoring them > >>> is the only way I'd handle this. > >> > >> On the other hand, looking at TZDIR at all is non-confirming as well. > > > > Are you sure? > > No, mainly because I'm not entirely sure what a POSIX TZ string should > look like. > > I tried this, based on some test case: > > $ TZ=AEST-10AEDST-11,M10.5.0,M3.5.0 strace -eopen date > open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 > open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 > open("/usr/share/zoneinfo/AEST-10AEDST-11,M10.5.0,M3.5.0", > O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) > Wed Jan 21 02:30:07 AEDST 2015 > +++ exited with 0 +++ > $ TZDIR=/tmp TZ=AEST-10AEDST-11,M10.5.0,M3.5.0 strace -eopen date > open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 > open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 > open("/tmp/AEST-10AEDST-11,M10.5.0,M3.5.0", O_RDONLY|O_CLOEXEC) = 3 > Tue Jan 20 15:30:14 UTC 2015 > +++ exited with 0 +++ > > The file is copied from /usr/share/zoneinfo/UTC. > > This seems to suggest that the glibc behavior is non-compliant. Yes, glibc is wrongly attempting to process strings as filenames first, then as POSIX-format TZ strings. I'd like to see this fixed, but I suspect too many users would complain. Rich
On 01/20/2015 08:54 AM, Rich Felker wrote: > Yes, glibc is wrongly attempting to process strings as filenames > first, then as POSIX-format TZ strings. This is OK, so long as all the file names actually installed in the system cannot be confused with valid POSIX-format TZ strings. That is the case with the standard time zone database normally installed in GNU/Linux systems (at least it is now; long ago there were ambiguities but they were removed many years ago).
On 01/20/2015 05:01 PM, Paul Eggert wrote: > Florian Weimer wrote: >> This seems to suggest that the glibc behavior is non-compliant. > > No, because POSIX reserves the environment variable name TZDIR for the > implementation (just as it reserves all upper-case-only names). Hmm. Does that mean that scrubbing TZ and TZDIR in AT_SECURE mode would also be compliant? Anyway, this part of the discussion is only about potential future patch I might submit. What about the last iteration of the existing parser fixes? <https://sourceware.org/ml/libc-alpha/2015-01/msg00360.html> Okay to commit?
On 01/22/2015 01:54 AM, Florian Weimer wrote: > Does that mean that scrubbing TZ and TZDIR in AT_SECURE mode would > also be compliant? I doubt it, though I can't easily cite chapter and verse on that.
On Thu, Jan 22, 2015 at 10:54:57AM +0100, Florian Weimer wrote: > On 01/20/2015 05:01 PM, Paul Eggert wrote: > > Florian Weimer wrote: > >> This seems to suggest that the glibc behavior is non-compliant. > > > > No, because POSIX reserves the environment variable name TZDIR for the > > implementation (just as it reserves all upper-case-only names). > > Hmm. Does that mean that scrubbing TZ and TZDIR in AT_SECURE mode would > also be compliant? No. Silently removing or modifying env vars is never compliant. Rich
From dcddbd483b8902409f97c778fe9ade2ee533c696 Mon Sep 17 00:00:00 2001 From: Florian Weimer <fweimer@redhat.com> Date: Wed, 14 Jan 2015 16:33:52 +0100 Subject: [PATCH] Make time zone file parser more robust [BZ #17715] This commit does not yet scrub the TZ environment variable in AT_SECURE mode, as suggested in the bug. 2015-01-14 Florian Weimer <fweimer@redhat.com> [BZ #17715] * time/tzfile.c (__tzfile_read): Check for large values of tzh_ttisstdcnt and tzh_ttisgmtcnt. Reject overlong POSIX time zone specifiers. * time/tzset.c (__tzset_parse_tz): Guard against large time zone specifiers. * 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. diff --git a/NEWS b/NEWS index 3bdc96a..4998485 100644 --- a/NEWS +++ b/NEWS @@ -15,9 +15,9 @@ Version 2.21 17501, 17506, 17508, 17522, 17555, 17570, 17571, 17572, 17573, 17574, 17582, 17583, 17584, 17585, 17589, 17594, 17601, 17608, 17616, 17625, 17630, 17633, 17634, 17635, 17647, 17653, 17657, 17658, 17664, 17665, - 17668, 17682, 17717, 17719, 17722, 17723, 17724, 17725, 17732, 17733, - 17744, 17745, 17746, 17747, 17748, 17775, 17777, 17780, 17781, 17782, - 17791, 17793, 17796, 17797, 17803, 17806, 17834 + 17668, 17682, 17715, 17717, 17719, 17722, 17723, 17724, 17725, 17732, + 17733, 17744, 17745, 17746, 17747, 17748, 17775, 17777, 17780, 17781, + 17782, 17791, 17793, 17796, 17797, 17803, 17806, 17834 * Optimized strcpy, stpcpy, strncpy, stpncpy, strcmp, and strncmp implementations for powerpc64/powerpc64le. @@ -48,6 +48,16 @@ Version 2.21 infinite loop if the DNS response contained a PTR record of an unexpected format. +* 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. + +* Privileged executables (running SUID/SGID) unset the TZ environment + variable if it refers to a file outside of the default TZDIR directory. + Overly long time zone specifiers in the TZ variable no longer result in + stack overflows and crashes. + * The minimum GCC version that can be used to build this version of the GNU C Library is GCC 4.6. Older GCC versions, and non-GNU compilers, can still be used to compile programs using the GNU C Library. diff --git a/time/tzfile.c b/time/tzfile.c index bcb408f..52897c7 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -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 (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,6 +437,10 @@ __tzfile_read (const char *file, size_t extra, char **extrap) goto lose; tzspec_len = st.st_size - off - 1; + if (tzspec_len >= 256) + /* POSIX time zone specifiers are much shorter than 256 + characters. */ + goto lose; char *tzstr = alloca (tzspec_len); if (getc_unlocked (f) != '\n' || (__fread_unlocked (tzstr, 1, tzspec_len - 1, f) diff --git a/time/tzset.c b/time/tzset.c index 8bc7a2e..59fd10d 100644 --- a/time/tzset.c +++ b/time/tzset.c @@ -176,11 +176,14 @@ __tzset_parse_tz (tz) memset (tz_rules, '\0', sizeof tz_rules); tz_rules[0].name = tz_rules[1].name = ""; - /* Get the standard timezone name. */ - char *tzbuf = strdupa (tz); + /* POSIX time zone specifiers are much shorter than 256 characters. */ + char tzbuf[256]; + if (strlen (tz) > sizeof (tzbuf) - 1) + goto out; + /* Get the standard timezone name. */ int consumed; - if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1) + if (sscanf (tz, "%255[A-Za-z]%n", tzbuf, &consumed) != 1) { /* Check for the quoted version. */ char *wp = tzbuf; @@ -227,7 +230,7 @@ __tzset_parse_tz (tz) /* Get the DST timezone name (if any). */ if (*tz != '\0') { - if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1) + if (sscanf (tz, "%255[A-Za-z]%n", tzbuf, &consumed) != 1) { /* Check for the quoted version. */ char *wp = tzbuf; diff --git a/timezone/Makefile b/timezone/Makefile index 17424b8..5f18545 100644 --- a/timezone/Makefile +++ b/timezone/Makefile @@ -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)|' \ diff --git a/timezone/README b/timezone/README index 7a5e31c..2268f8e 100644 --- a/timezone/README +++ b/timezone/README @@ -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. diff --git a/timezone/testdata/XT1 b/timezone/testdata/XT1 new file mode 100644 index 0000000000000000000000000000000000000000..67d7ee0ba59389b4aaea0bdce15ff6bf7056c5ef GIT binary patch literal 127 zcmWHE%1kq2Kn4GS04TzYB+3Y6vq1O}A%;Lk2w{C7Jz#x5AR1vL!~iZJWxxdhA3F~_ literal 0 HcmV?d00001 diff --git a/timezone/testdata/XT2 b/timezone/testdata/XT2 new file mode 100644 index 0000000000000000000000000000000000000000..069189e34998662d526b3645891d515a31fd6495 GIT binary patch literal 127 wcmWHE%1kq2zyQqufdEOA5y)nN@FPM%>O%<Y1L*<l`vK7iBOwNG0VxA60ROxXJ^%m! literal 0 HcmV?d00001 diff --git a/timezone/testdata/XT3 b/timezone/testdata/XT3 new file mode 100644 index 0000000000000000000000000000000000000000..fbf5efff9ed5560de8c9fec46c84dc3dafc8f23f GIT binary patch literal 127 wcmWHE%1kq2zyJb35k@3Y5Ss<Uj|edaGC~OJ1L*<l`vK7iBOwNG0VxA60KSR`WdHyG literal 0 HcmV?d00001 diff --git a/timezone/testdata/XT4 b/timezone/testdata/XT4 new file mode 100644 index 0000000000000000000000000000000000000000..990a9763da2c00d82ad235033005047903585b25 GIT binary patch literal 127 wcmWHE%1kq2zyORu5dkDo5T6CYj|edVGC~OJ1L*<l`vK7iBOwNG0VxA60KRGmXaE2J literal 0 HcmV?d00001 diff --git a/timezone/tst-tzset.c b/timezone/tst-tzset.c new file mode 100644 index 0000000..20b92f6 --- /dev/null +++ b/timezone/tst-tzset.c @@ -0,0 +1,152 @@ +/* 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 <time.h> +#include <unistd.h> + +/* Returns the name of a large TZ file. */ +static char * +create_tz_file (void) +{ + char * path = strdup ("/tmp/tst-tzset-XXXXXX"); + if (path == NULL) + { + printf ("strdup failed: %m\n"); + exit (1); + } + int fd = mkstemp (path); + if (fd < 0) + { + printf ("mkstemp failed: %m\n"); + 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 (lseek (fd, 64 * 1024 * 1024, SEEK_CUR) < 0) + { + printf ("lseek failed: %m\n"); + exit (1); + } + if (write (fd, "", 1) != 1) + { + printf ("Single-byte write failed\n"); + exit (1); + } + if (close (fd) != 0) + { + printf ("close failed: %m\n"); + exit (1); + } + return path; +} + +static int +do_test (void) +{ + 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 file. */ + { + char *path = create_tz_file (); + if (setenv ("TZ", path, 1) < 0) + { + printf ("setenv failed: %m\n"); + return 1; + } + /* This will succeed on 64-bit architectures, and fail on 32-bit + architectures. It used to crash on 32-bit. */ + tzset (); + unlink (path); + free (path); + } + + /* Large TZ variable. */ + { + 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 (); + } + + return errors > 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" -- 2.1.0