Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <tuio6enntsgvex4py3zzvt3ebqiav6l57lgpd5dy6vlek6gvbh@rjg32jmhmwr4>
Date: Mon, 7 Apr 2025 01:54:14 +0200
From: Alejandro Colomar <alx@...nel.org>
To: musl@...ts.openwall.com, Rich Felker <dalias@...c.org>, 
	ipedrosa@...hat.com, ~hallyn/shadow@...ts.sr.ht, libc-alpha@...rceware.org
Subject: Re: strptime("UTC", "%Z", &tm) in musl

Hi Haelwenn,

On Sun, Apr 06, 2025 at 10:55:46PM +0200, Haelwenn (lanodan) Monnier wrote:
> [2025-04-06 21:57:35+0200] Alejandro Colomar:
> > I was trying to develop a function that parses a UTC date in the shadow
> > project, and I came up with this:
> > <https://github.com/shadow-maint/shadow/pull/1217/>
> > 
> > 	$ grepc -htfd get_date .
> > 	static long
> > 	get_date(const char *s)
> > 	{
> > 		struct tm   tm;
> > 		const char  *p;
> > 
> > 		bzero(&tm, sizeof(tm));
> > 
> > 		p = strptime("UTC", "%Z", &tm);
> > 		if (p == NULL || !streq(p, ""))
> > 			return -1;
> 
> That bzero seems like a bit of a bad idea, `struct tm` can have some extra
> fields where 0 isn't the correct value.
> Quite like how `tm_isdst` uses -1 for unknown state, 0 for no-DST, 1 for DST.
> 
> Could probably use `if(!gmtime_r(0, &tm)) { /* handle err */ }` instead
> which also has the nice effect of initializing tm for UTC,
> allowing to skip the problematic strptime %Z.

Thanks!  This is quite clever.  I've changed this to my patch:

	diff --git i/lib/strtoday.c w/lib/strtoday.c
	index 2d88e8b6..ab03b921 100644
	--- i/lib/strtoday.c
	+++ w/lib/strtoday.c
	@@ -47,13 +47,13 @@ strtoday(const char *str)
	 static long
	 get_date(const char *s)
	 {
	+       time_t      t;
		struct tm   tm;
		const char  *p;
	 
	-       bzero(&tm, sizeof(tm));
	-
	-       p = strptime("UTC", "%Z", &tm);
	-       if (p == NULL || !streq(p, ""))
	+       // Set timezone to UTC
	+       t = 0;
	+       if (gmtime_r(&t, &tm) == NULL)
			return -1;
	 
		p = strptime(s, "%F", &tm);

If you don't mind, I'd like to add you as a co-author of the patch, and
assign you copyright in that file.  Please sign the following commit, if
you approve it.


Have a lovely night!
Alex


---

<https://github.com/shadow-maint/shadow/pull/1217>

Author: Alejandro Colomar <alx@...nel.org>
Date:   Tue Feb 18 23:25:29 2025 +0100

    lib/getdate.c: Use strptime(3) to simplify
    
    The following trick:
    
            t = 0;
            gmtime_r(&t, &tm);
    
    is a clever way to clear the tm(3type) structure, and set it to use UTC.
    
    We need to set it to set UTC with this trick, because strptime(3)
    doesn't set the timezone.  I (Alex) tried previously using
    
            bzero(&tm, sizeof(tm));
            strptime("UTC", "%Z", &tm);
    
    but glibc ignores the timezone, and musl (at least I tried in an Alpine
    container) seems to report an error.
    
    The idea to use gmtime_r(3) was from lanodan.
    
    Link: <https://inbox.sourceware.org/libc-alpha/Z_LqUgildoq33vI-@cloudsdale.the-delta.net.eu.org/T/#u>
    Cc: Iker Pedrosa <ipedrosa@...hat.com>
    Cc: Serge Hallyn <serge@...lyn.com>
    Cc: Rich Felker <dalias@...c.org>
    Co-authored-by: "Haelwenn (lanodan) Monnier" <contact@...ktivis.me>
    Signed-off-by: Alejandro Colomar <alx@...nel.org>

diff --git a/lib/getdate.c b/lib/getdate.c
index 31a5c25b..fa2df83e 100644
--- a/lib/getdate.c
+++ b/lib/getdate.c
@@ -1,81 +1,31 @@
 // SPDX-FileCopyrightText: 2025, Alejandro Colomar <alx@...nel.org>
+// SPDX-FileCopyrightText: 2025, "Haelwenn (lanodan) Monnier" <contact@...ktivis.me>
 // SPDX-License-Identifier: BSD-3-Clause
 
 
 #include <config.h>
 
-#include <errno.h>
-#include <limits.h>
 #include <stddef.h>
-#include <string.h>
 #include <time.h>
 
-#include "atoi/a2i/a2s.h"
 #include "getdate.h"
-#include "string/strchr/strchrcnt.h"
 #include "string/strcmp/streq.h"
-#include "string/strcmp/strprefix.h"
-#include "string/strcmp/strsuffix.h"
-#include "string/strspn/stpspn.h"
-
-
-#define TM_YEAR_ORIGIN 1900
-
-
-static long  yyDay;
-static long  yyMonth;
-static long  yyYear;
-
-
-static int parse_date(const char *s);
 
 
 time_t
-get_date(const char *s);
+get_date(const char *s)
 {
-       struct tm  tm;
+       time_t      t;
+       struct tm   tm;
+       const char  *p;
 
-       if (parse_date(p) == -1)
+       t = 0;
+       if (gmtime_r(&t, &tm) == NULL)
                return -1;
 
-       tm.tm_year = yyYear - TM_YEAR_ORIGIN;
-       tm.tm_mon = yyMonth - 1;
-       tm.tm_mday = yyDay;
-       tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
-       tm.tm_isdst = 0;
+       p = strptime(s, "%F", &tm);
+       if (p == NULL || !streq(p, ""))
+               return -1;
 
        return timegm(&tm);
 }
-
-
-static int
-parse_date(const char *s)
-{
-       long  n;
-
-       if (!streq(stpspn(s, "0123456789-"), "")
-        || strchrcnt(s, '-') != 2
-        || strprefix(s, "-")
-        || strsuffix(s, "-")
-        || strstr(s, "--"))
-       {
-               return -1;
-       }
-
-       if (a2sl(&yyYear, s, &s, 10, LONG_MIN, LONG_MAX) == -1 && errno != ENOTSUP)
-               return -1;
-
-       if (!strprefix(s++, "-"))
-               return -1;
-
-       if (a2sl(&yyMonth, s, &s, 10, LONG_MIN, LONG_MAX) == -1 && errno != ENOTSUP)
-               return -1;
-
-       if (!strprefix(s++, "-"))
-               return -1;
-
-       if (a2sl(&yyDay, s, NULL, 10, LONG_MIN, LONG_MAX) == -1)
-               return -1;
-
-       return 0;
-}

-- 
<https://www.alejandro-colomar.es/>
<https://www.alejandro-colomar.es:8443/>
<http://www.alejandro-colomar.es:8080/>

Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.