NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: lib/58041: strptime("%s") bug: undefined behaviour in 64-bit; accepts invalid values in 32-bit



The following reply was made to PR lib/58041; it has been noted by GNATS.

From: Emanuele Torre <torreemanuele6%gmail.com@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: riastradh%netbsd.org@localhost, gnats-admin%netbsd.org@localhost, netbsd-bugs%netbsd.org@localhost
Subject: Re: lib/58041: strptime("%s") bug: undefined behaviour in 64-bit;
 accepts invalid values in 32-bit
Date: Mon, 18 Mar 2024 16:30:53 +0100

 On Sat, Mar 16, 2024 at 12:20:03AM +0000, Taylor R Campbell wrote:
 >  Thanks for the report!
 >  
 >  > This makes the code, if the check is not optimised out, trigger a signed
 >  > integer overflow (undefined behaviour in standard C) for inputs to %s
 >  > that start with a sequence of characters between "922337203685477581"
 >  > and "999999999999999999" (inclusive, 64-bit time_t).
 >  
 >  Yep, that looks like undefined behaviour.  I added some automatic
 >  tests to exercise this case -- although you can't verify from the
 >  outside if it's wrong or not, because even if strptime parses it
 >  correctly, the time is outside the range that can be represented by
 >  struct tm, so strptime needs to fail either way.
 >  
 >  > Additionally, in builds where there is `#define TIME_MAX INT32_MAX', and
 >  > `time_t' is `int32_t', if we assume that signed integer overflow works
 >  > like unsigned integer overflow, this makes %s accept, for example,
 >  > "9999999999" as a valid timestamp equivalent to
 >  > "1410065398" (Sat 20 Nov 17:46:39 UTC) instead of rejecting it.
 >  > This is thanks to the fact that UINT32_MAX and INT32_MAX (unlike their
 >  > 64-bit counterparts) have the same number of digits in decimal, so
 >  > `rulim /= 10' can't reject the overflow in time.
 >  
 >  In NetBSD, this is not an issue because time_t is always 64-bit and
 >  has been since netbsd-6.  (We've been working on getting ready for
 >  Y2.038k for many years now!)
 >  
 
 \o/
 
 >  However, to make it more portable and easier to understand, I've
 >  rewritten the logic in a way that uses a simpler idiom to reliably
 >  avoid overflow no matter what integer type time_t is.  (Porting to
 >  systems with floating-point time_t left as an exercise for the
 >  reader!)
 
 There is a problem with the rewrite
 
   if (sse > TIME_MAX - d) {
 
 There (TIME_MAX - d) is unsigned (because d is unsigned int), while sse
 is signed (time_t), and that makes compilers trigger a -Wsign-compare
 warning.
 
 Also, that code, following C integer promotion rules, is ambiguous
 depending on the type of time_t:
 
 If time_t is int64_t, it is equivalent to
 
   if (sse > (int64_t)(TIME_MAX - d)) {
 
 While, if time_t is int32_t, it is equivalent to
 
   if ((unsigned)sse > TIME_MAX - d) {
 
 Either interpretation should be fine in this case, but casting d to
 time_t to prevent the warning would be nice.
 
 Another alternative, and cleaner fix is to just declare d as int or
 time_t instead of unsigned.
 
 o/
  emanuele6
 



Home | Main Index | Thread Index | Old Index