lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <5F3254A4020000A10003A897@gwsmtp.uni-regensburg.de>
Date:   Tue, 11 Aug 2020 10:19:48 +0200
From:   "Ulrich Windl" <Ulrich.Windl@...uni-regensburg.de>
To:     <linux-kernel@...r.kernel.org>
Subject: heads up: clock_gettime(CLOCK_MONOTONIC, ...) is not monotonic
 with Xen PVM

Hi!

I'm not subscribed to the kernel list, and my kernels are somewhat outdated, but I'd like to let you know:
I found an issue where clock_gettime(CLOCK_MONOTONIC, ...) returned the identical value to two subsequent calls, while the resolution claimed is 1 ns.

Environment: Xen Host x86-64 using SLES11 SP4 (3.0.101-108.87-xen), Xen-Guest using SLES12 SP5 (4.12.14-122.26-default, glibc 2.22)

Typically I take 100 fast clock samples ("samples"), build the differences ("deltas"), and sort the differences. The smallest number shouldn't be zero, right?

Here is one case:
(gdb) print *samples@5
$5 = {{tv_sec = 4127194, tv_nsec = 210786012}, {tv_sec = 4127194,
    tv_nsec = 210790437}, {tv_sec = 4127194, tv_nsec = 210794819}, {
    tv_sec = 4127194, tv_nsec = 210799202}, {tv_sec = 4127194,
    tv_nsec = 210808344}}
(gdb) print *deltas@5
$6 = {{tv_sec = 0, tv_nsec = 4425}, {tv_sec = 0, tv_nsec = 4382}, {tv_sec = 0,
    tv_nsec = 4383}, {tv_sec = 0, tv_nsec = 9142}, {tv_sec = 0, tv_nsec = 0}}
(gdb) print *samples@6
$7 = {{tv_sec = 4127194, tv_nsec = 210786012}, {tv_sec = 4127194,
    tv_nsec = 210790437}, {tv_sec = 4127194, tv_nsec = 210794819}, {
    tv_sec = 4127194, tv_nsec = 210799202}, {tv_sec = 4127194,
    tv_nsec = 210808344}, {tv_sec = 4127194, tv_nsec = 210808344}}

Obviously the last two samples are identical!

In the non-error case I get numbers like this (the hardware is about 10 years old):
get_res: resolution is 0.000000001
get_res: smallest delta is 0.000004171
get_res: largest delta is 0.000007364

Here's the code I used:

#include        <stdlib.h>
#include        <stdarg.h>
#include        <stdio.h>
#include        <string.h>
#include        <unistd.h>
#include        <limits.h>
#include        <errno.h>
#include        <assert.h>
#include        <time.h>
#include        <fcntl.h>
#include        <math.h>
#include        <sys/types.h>

#define NSECS_PER_SECOND        1000000000L     /* nanoseconds per second */
#define CLOCK_SAMPLES   100             /* number of clock samples for
                                         * get_res()
                                         */
typedef struct timespec ts_t;           /* short-cut for struct timespec */

#if _POSIX_C_SOURCE < 199309L
# error "Need POSIX clock_gettime(CLOCK_MONOTONIC)"
#endif

/* get timespec value */
static  int     get_time(ts_t *tsp)
{
        int     result;

        result = clock_gettime(CLOCK_MONOTONIC, tsp);
        return(result);
}

/* time difference tsp1 -= tsp2 */
static  void    subtract_time1(ts_t *tsp1, const ts_t *tsp2)
{
        tsp1->tv_sec -= tsp2->tv_sec;
        tsp1->tv_nsec -= tsp2->tv_nsec;
        if ( tsp1->tv_nsec < 0 )
                --tsp1->tv_sec, tsp1->tv_nsec += NSECS_PER_SECOND;
}

/* time difference tsp1 - tsp2 */
static  ts_t    subtract_time(const ts_t *tsp1, const ts_t *tsp2)
{
        ts_t    result = *tsp1;

        subtract_time1(&result, tsp2);
        return(result);
}

/* compare time stamps tsp1 - tsp2 */
static  int     compare_time(const ts_t *tsp1, const ts_t *tsp2)
{
        if ( tsp1->tv_sec < tsp2->tv_sec )
                return(-1);
        else if ( tsp1->tv_sec > tsp2->tv_sec )
                return(1);
        else if ( tsp1->tv_nsec < tsp2->tv_nsec )
                return(-1);
        else if ( tsp1->tv_nsec > tsp2->tv_nsec )
                return(1);
        return(0);
}

/* qsort-compatible compare time stamps tsp1 - tsp2 */
static  int     cmp_time(const void *tsp1, const void *tsp2)
{
        return(compare_time((const ts_t *) tsp1, (const ts_t *) tsp2));
}

/* get clock resolution */
static  long    get_res(ts_t *tsp)
{
        int             result;
        ts_t            samples[CLOCK_SAMPLES];
        ts_t            deltas[CLOCK_SAMPLES];
        unsigned        u;

        if ( (result = clock_getres(CLOCK_MONOTONIC, tsp)) != 0 )
                goto leave;
        printf("%s: resolution is %ld.%09ld\n", __func__,
               tsp->tv_sec, tsp->tv_nsec);
        assert(tsp->tv_sec == 0);
        for ( u = 0; u < CLOCK_SAMPLES; ++u )
        {
                get_time(samples + u);
        }
        for ( u = 1; u < CLOCK_SAMPLES; ++u )
        {
                deltas[u - 1] = subtract_time(&samples[u], &samples[u - 1]);
                assert(deltas[u - 1].tv_nsec != 0);
        }
        qsort(deltas, CLOCK_SAMPLES - 1, sizeof(deltas[0]), cmp_time);
        for ( u = 0; u < CLOCK_SAMPLES - 1 && deltas[u].tv_nsec == 0; ++u )
        {
                assert(deltas[u].tv_sec == 0);
        }
        if ( u >= CLOCK_SAMPLES - 1 )
        {
                printf("%s: clock did not advance within %d samples\n",
                      __func__, u);
                result = -1;
                goto leave;
        }
        assert(u == 0);
        if ( u > 0 )
                printf("%s: skipped %d zero samples\n", __func__, u);
        printf("%s: smallest delta is 0.%09ld\n", __func__,
               deltas[u].tv_nsec);
        printf("%s: largest delta is 0.%09ld\n", __func__,
              deltas[CLOCK_SAMPLES - 2].tv_nsec);
        if ( deltas[u].tv_nsec > tsp->tv_nsec )
                tsp->tv_nsec = deltas[0].tv_nsec;
leave:  return(result);
}

/* main */
int     main(int argc, char *argv[])
{
        int     result  = 0;
        ts_t    res;

        get_res(&res);
        printf("resolution is 0.%09ld\n", res.tv_nsec);
        return(result);
}
--------- it is intentional that the program aborts on error--------
Output from a newer machine:
get_res: resolution is 0.000000001
get_res: smallest delta is 0.000000030
get_res: largest delta is 0.000000050
resolution is 0.000000030

Regards,
Ulrich Windl
(Keep me on CC if I should read your replies)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ