Wireshark mailing list archives

Re: FILETIME to nstime_t conversions gives a warning


From: Peter Wu <peter () lekensteyn nl>
Date: Wed, 7 Oct 2015 15:55:15 +0200

Hi Guy,

Thank you for your detailed reply.

On Mon, Oct 05, 2015 at 08:11:55AM -0700, Guy Harris wrote:

On Oct 4, 2015, at 6:09 AM, Peter Wu <peter () lekensteyn nl> wrote:

I am getting these compile warnings using Clang 3.7.0:

   wsutil/nstime.c:235:25: warning: shifting a negative signed value is undefined [-Wshift-negative-value]
       time_t l_time_min = TIME_T_MIN;
                           ^~~~~~~~~~
   wsutil/nstime.c:223:36: note: expanded from macro 'TIME_T_MIN'
                       : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)))
                         ~~~~~~~~~~~~ ^
   wsutil/nstime.c:236:25: warning: shifting a negative signed value is undefined [-Wshift-negative-value]
       time_t l_time_max = TIME_T_MAX;
                           ^~~~~~~~~~
   wsutil/nstime.c:226:46: note: expanded from macro 'TIME_T_MAX'
   #define TIME_T_MAX ((time_t) (~ (time_t) 0 - TIME_T_MIN))
                                                ^~~~~~~~~~
   wsutil/nstime.c:223:36: note: expanded from macro 'TIME_T_MIN'
                       : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)))
                         ~~~~~~~~~~~~ ^

Looking a bit further in the code, I became a bit confused on what is
going on in nstime.c. There is a magic "fixup" constant

Not all that magic - all but 6 hours of it are explained in the
comment (if anybody either 1) knows where those 6 hours came from or
2) where I miscalculated so that there are no 6 hours to explain,
please feel free to submit a change to the comment to Gerrit for
review).

(The code and the comment assume familiarity with 1) UN*X time stamps
and 2) Windows FILENAME time stamps, where the former measure some
form of "seconds-since-the-Epoch" plus fractions of a second, with the
Epoch being January 1, 1970, 00:00:00 UTC, and with "some form of
"seconds-since-the-Epoch"" involving different ways of dealing with
leap seconds, including POSIX's "if we close our eyes, stick our
fingers in our ears, and pretend they don't happen, maybe that'll
work", and the latter measure
100-nanosecond-units-since-the-FILETIME-Epoch, with the Epoch being
what would be January 1, 1601 in the proleptic Gregorian calendar,
00:00:00 "GMT".

The constant is just the delta between the epochs.)

and some special handling for the largest and smallest time_t values.

Right before the offending lines is the comment:

    /* The next two lines are a fix needed for the
       broken SCO compiler. JRA. */

(where I'm guessing "JRA" is Jeremy Allison).

The comments suggest that it is based on Samba code,

Yes - older Samba code.

but their latest version[1] is much simpler:

Probably because they don't care about SCO's compiler any more.

Probably because SCO compilers are fixed by now. This page suggests GCC
for opensource projects, I think we can remove the workaround too:
http://osr507doc.sco.com/en/DevSysoview/Ccompil.html

I'm not sure we should care much about it, either, and can just go
with something based on the current Samba code (but with the comment
explaining the mixup constant, which the Samba code doesn't bother to
do), as long as the new code doesn't fail in cases where the current
code would succeed ("succeed" means "calculates the correct seconds
value").

   #ifndef TIME_T_MIN
   /* we use 0 here, because (time_t)-1 means error */
   #define TIME_T_MIN 0
   #endif

There's nothing about (time_t)-1 that inherently means "error" rather
than "December 31, 1969, 23:59:59 UTC".

Right, the "error" refers to the return value of mktime().

   /*
    * we use the INT32_MAX here as on 64 bit systems,
    * gmtime() fails with INT64_MAX
    */
   #ifndef TIME_T_MAX
   #define TIME_T_MAX MIN(INT32_MAX,_TYPE_MAXIMUM(time_t))
   #endif

If the Samba people want Bad Things to happen on 64-bit systems in
2038, they're more than welcome to do so.  I'd prefer *not* to do so
in Wireshark, even at the expense of some UN*X time in the very very
very far future causing problems.

See the comment on gmtime(), they use that, but we do not. So we do not
need this restriction.

So I'd go with

      #define TIME_T_MIN _TYPE_MINIMUM(time_t)

and

      #define TIME_T_MAX _TYPE_MAXIMUM(time_t)

_TIME_MINIMUM and _TIME_MAXIMUM are defined here:

      https://github.com/samba-team/samba/blob/master/lib/replace/replace.h#L638

as

      /* The extra casts work around common compiler bugs. */
      #define _TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
      /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
         It is necessary at least when t == time_t.  */
      #define _TYPE_MINIMUM(t) ((t) (_TYPE_SIGNED (t) \
                                    ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
      #define _TYPE_MAXIMUM(t) ((t) (~ (t) 0 - _TYPE_MINIMUM (t)))

and the conversion method[2]:

      ...

       t2 = t;
       t2 += TIME_FIXUP_CONSTANT_INT;
       t2 *= 1000*1000*10;

So this is converting *from* UN*X time *to* FILETIME.  We want to
convert in the *opposite* direction, so we would want to look at
nt_time_to_unix_timespec():

      https://github.com/samba-team/samba/blob/master/lib/util/time.c#L756

Our nstime_t is essentially the same as a UN*X struct timespec -
seconds since the UN*X epoch plus nanoseconds since that second.

So the only change I see making would be to go with

      /* The extra casts work around common compiler bugs. */
      #define _TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
      /* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
         It is necessary at least when t == time_t.  */
      #define _TYPE_MINIMUM(t) ((t) (_TYPE_SIGNED (t) \
                                    ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
      #define _TYPE_MAXIMUM(t) ((t) (~ (t) 0 - _TYPE_MINIMUM (t)))

      #ifndef TIME_T_MIN
      #define TIME_T_MIN _TYPE_MINIMUM(time_t)
      #endif
      #ifndef TIME_T_MAX
      #define TIME_T_MAX _TYPE_MAXIMUM(time_t)
      #endif

as a replacement for

      #ifndef TIME_T_MIN
      #define TIME_T_MIN ((time_t) ((time_t)0 < (time_t) -1 ? (time_t) 0 \
                          : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)))
      #endif
      #ifndef TIME_T_MAX
      #define TIME_T_MAX ((time_t) (~ (time_t) 0 - TIME_T_MIN))
      #endif

but that doesn't rid of the offending shifts of negative values.  The
only thing that would do *that* would be to refuse to convert values
prior to January 1, 1970, 00:00:00 UTC by defining TIME_T_MIN as 0,
and I'm not sure we want to do that.

If the validation rejects times, then:

 - netmon and peektagged would reject capture files with an invalid
   timestamp.
 - packet-windows-common will display "Time can't be converted" instead
   of a human-readable time.

It is not a disaster as you will unlikely find such dates, but let's try
to be more forgivable.

Your proposed change is almost the same as the previous one, except the
compile warning (error with default -Werror in cmake). Let's try this:
https://code.wireshark.org/review/10862
-- 
Kind regards,
Peter Wu
https://lekensteyn.nl
___________________________________________________________________________
Sent via:    Wireshark-dev mailing list <wireshark-dev () wireshark org>
Archives:    https://www.wireshark.org/lists/wireshark-dev
Unsubscribe: https://wireshark.org/mailman/options/wireshark-dev
             mailto:wireshark-dev-request () wireshark org?subject=unsubscribe


Current thread: