linux/tools/testing/selftests/vDSO/vdso_test_correctness.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * ldt_gdt.c - Test cases for LDT and GDT access
   4 * Copyright (c) 2011-2015 Andrew Lutomirski
   5 */
   6
   7#define _GNU_SOURCE
   8
   9#include <stdio.h>
  10#include <sys/time.h>
  11#include <time.h>
  12#include <stdlib.h>
  13#include <unistd.h>
  14#include <sys/syscall.h>
  15#include <dlfcn.h>
  16#include <string.h>
  17#include <errno.h>
  18#include <sched.h>
  19#include <stdbool.h>
  20#include <limits.h>
  21
  22#include "vdso_config.h"
  23#include "../kselftest.h"
  24
  25static const char **name;
  26
  27#ifndef SYS_getcpu
  28# ifdef __x86_64__
  29#  define SYS_getcpu 309
  30# else
  31#  define SYS_getcpu 318
  32# endif
  33#endif
  34
  35#ifndef __NR_clock_gettime64
  36#define __NR_clock_gettime64    403
  37#endif
  38
  39#ifndef __kernel_timespec
  40struct __kernel_timespec {
  41        long long       tv_sec;
  42        long long       tv_nsec;
  43};
  44#endif
  45
  46/* max length of lines in /proc/self/maps - anything longer is skipped here */
  47#define MAPS_LINE_LEN 128
  48
  49int nerrs = 0;
  50
  51typedef int (*vgettime_t)(clockid_t, struct timespec *);
  52
  53vgettime_t vdso_clock_gettime;
  54
  55typedef int (*vgettime64_t)(clockid_t, struct __kernel_timespec *);
  56
  57vgettime64_t vdso_clock_gettime64;
  58
  59typedef long (*vgtod_t)(struct timeval *tv, struct timezone *tz);
  60
  61vgtod_t vdso_gettimeofday;
  62
  63typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
  64
  65getcpu_t vgetcpu;
  66getcpu_t vdso_getcpu;
  67
  68static void *vsyscall_getcpu(void)
  69{
  70#ifdef __x86_64__
  71        FILE *maps;
  72        char line[MAPS_LINE_LEN];
  73        bool found = false;
  74
  75        maps = fopen("/proc/self/maps", "r");
  76        if (!maps) /* might still be present, but ignore it here, as we test vDSO not vsyscall */
  77                return NULL;
  78
  79        while (fgets(line, MAPS_LINE_LEN, maps)) {
  80                char r, x;
  81                void *start, *end;
  82                char name[MAPS_LINE_LEN];
  83
  84                /* sscanf() is safe here as strlen(name) >= strlen(line) */
  85                if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
  86                           &start, &end, &r, &x, name) != 5)
  87                        continue;
  88
  89                if (strcmp(name, "[vsyscall]"))
  90                        continue;
  91
  92                /* assume entries are OK, as we test vDSO here not vsyscall */
  93                found = true;
  94                break;
  95        }
  96
  97        fclose(maps);
  98
  99        if (!found) {
 100                printf("Warning: failed to find vsyscall getcpu\n");
 101                return NULL;
 102        }
 103        return (void *) (0xffffffffff600800);
 104#else
 105        return NULL;
 106#endif
 107}
 108
 109
 110static void fill_function_pointers()
 111{
 112        void *vdso = dlopen("linux-vdso.so.1",
 113                            RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
 114        if (!vdso)
 115                vdso = dlopen("linux-gate.so.1",
 116                              RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
 117        if (!vdso) {
 118                printf("[WARN]\tfailed to find vDSO\n");
 119                return;
 120        }
 121
 122        vdso_getcpu = (getcpu_t)dlsym(vdso, name[4]);
 123        if (!vdso_getcpu)
 124                printf("Warning: failed to find getcpu in vDSO\n");
 125
 126        vgetcpu = (getcpu_t) vsyscall_getcpu();
 127
 128        vdso_clock_gettime = (vgettime_t)dlsym(vdso, name[1]);
 129        if (!vdso_clock_gettime)
 130                printf("Warning: failed to find clock_gettime in vDSO\n");
 131
 132#if defined(VDSO_32BIT)
 133        vdso_clock_gettime64 = (vgettime64_t)dlsym(vdso, name[5]);
 134        if (!vdso_clock_gettime64)
 135                printf("Warning: failed to find clock_gettime64 in vDSO\n");
 136#endif
 137
 138        vdso_gettimeofday = (vgtod_t)dlsym(vdso, name[0]);
 139        if (!vdso_gettimeofday)
 140                printf("Warning: failed to find gettimeofday in vDSO\n");
 141
 142}
 143
 144static long sys_getcpu(unsigned * cpu, unsigned * node,
 145                       void* cache)
 146{
 147        return syscall(__NR_getcpu, cpu, node, cache);
 148}
 149
 150static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
 151{
 152        return syscall(__NR_clock_gettime, id, ts);
 153}
 154
 155static inline int sys_clock_gettime64(clockid_t id, struct __kernel_timespec *ts)
 156{
 157        return syscall(__NR_clock_gettime64, id, ts);
 158}
 159
 160static inline int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
 161{
 162        return syscall(__NR_gettimeofday, tv, tz);
 163}
 164
 165static void test_getcpu(void)
 166{
 167        printf("[RUN]\tTesting getcpu...\n");
 168
 169        for (int cpu = 0; ; cpu++) {
 170                cpu_set_t cpuset;
 171                CPU_ZERO(&cpuset);
 172                CPU_SET(cpu, &cpuset);
 173                if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
 174                        return;
 175
 176                unsigned cpu_sys, cpu_vdso, cpu_vsys,
 177                        node_sys, node_vdso, node_vsys;
 178                long ret_sys, ret_vdso = 1, ret_vsys = 1;
 179                unsigned node;
 180
 181                ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
 182                if (vdso_getcpu)
 183                        ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
 184                if (vgetcpu)
 185                        ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
 186
 187                if (!ret_sys)
 188                        node = node_sys;
 189                else if (!ret_vdso)
 190                        node = node_vdso;
 191                else if (!ret_vsys)
 192                        node = node_vsys;
 193
 194                bool ok = true;
 195                if (!ret_sys && (cpu_sys != cpu || node_sys != node))
 196                        ok = false;
 197                if (!ret_vdso && (cpu_vdso != cpu || node_vdso != node))
 198                        ok = false;
 199                if (!ret_vsys && (cpu_vsys != cpu || node_vsys != node))
 200                        ok = false;
 201
 202                printf("[%s]\tCPU %u:", ok ? "OK" : "FAIL", cpu);
 203                if (!ret_sys)
 204                        printf(" syscall: cpu %u, node %u", cpu_sys, node_sys);
 205                if (!ret_vdso)
 206                        printf(" vdso: cpu %u, node %u", cpu_vdso, node_vdso);
 207                if (!ret_vsys)
 208                        printf(" vsyscall: cpu %u, node %u", cpu_vsys,
 209                               node_vsys);
 210                printf("\n");
 211
 212                if (!ok)
 213                        nerrs++;
 214        }
 215}
 216
 217static bool ts_leq(const struct timespec *a, const struct timespec *b)
 218{
 219        if (a->tv_sec != b->tv_sec)
 220                return a->tv_sec < b->tv_sec;
 221        else
 222                return a->tv_nsec <= b->tv_nsec;
 223}
 224
 225static bool ts64_leq(const struct __kernel_timespec *a,
 226                     const struct __kernel_timespec *b)
 227{
 228        if (a->tv_sec != b->tv_sec)
 229                return a->tv_sec < b->tv_sec;
 230        else
 231                return a->tv_nsec <= b->tv_nsec;
 232}
 233
 234static bool tv_leq(const struct timeval *a, const struct timeval *b)
 235{
 236        if (a->tv_sec != b->tv_sec)
 237                return a->tv_sec < b->tv_sec;
 238        else
 239                return a->tv_usec <= b->tv_usec;
 240}
 241
 242static char const * const clocknames[] = {
 243        [0] = "CLOCK_REALTIME",
 244        [1] = "CLOCK_MONOTONIC",
 245        [2] = "CLOCK_PROCESS_CPUTIME_ID",
 246        [3] = "CLOCK_THREAD_CPUTIME_ID",
 247        [4] = "CLOCK_MONOTONIC_RAW",
 248        [5] = "CLOCK_REALTIME_COARSE",
 249        [6] = "CLOCK_MONOTONIC_COARSE",
 250        [7] = "CLOCK_BOOTTIME",
 251        [8] = "CLOCK_REALTIME_ALARM",
 252        [9] = "CLOCK_BOOTTIME_ALARM",
 253        [10] = "CLOCK_SGI_CYCLE",
 254        [11] = "CLOCK_TAI",
 255};
 256
 257static void test_one_clock_gettime(int clock, const char *name)
 258{
 259        struct timespec start, vdso, end;
 260        int vdso_ret, end_ret;
 261
 262        printf("[RUN]\tTesting clock_gettime for clock %s (%d)...\n", name, clock);
 263
 264        if (sys_clock_gettime(clock, &start) < 0) {
 265                if (errno == EINVAL) {
 266                        vdso_ret = vdso_clock_gettime(clock, &vdso);
 267                        if (vdso_ret == -EINVAL) {
 268                                printf("[OK]\tNo such clock.\n");
 269                        } else {
 270                                printf("[FAIL]\tNo such clock, but __vdso_clock_gettime returned %d\n", vdso_ret);
 271                                nerrs++;
 272                        }
 273                } else {
 274                        printf("[WARN]\t clock_gettime(%d) syscall returned error %d\n", clock, errno);
 275                }
 276                return;
 277        }
 278
 279        vdso_ret = vdso_clock_gettime(clock, &vdso);
 280        end_ret = sys_clock_gettime(clock, &end);
 281
 282        if (vdso_ret != 0 || end_ret != 0) {
 283                printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
 284                       vdso_ret, errno);
 285                nerrs++;
 286                return;
 287        }
 288
 289        printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
 290               (unsigned long long)start.tv_sec, start.tv_nsec,
 291               (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
 292               (unsigned long long)end.tv_sec, end.tv_nsec);
 293
 294        if (!ts_leq(&start, &vdso) || !ts_leq(&vdso, &end)) {
 295                printf("[FAIL]\tTimes are out of sequence\n");
 296                nerrs++;
 297                return;
 298        }
 299
 300        printf("[OK]\tTest Passed.\n");
 301}
 302
 303static void test_clock_gettime(void)
 304{
 305        if (!vdso_clock_gettime) {
 306                printf("[SKIP]\tNo vDSO, so skipping clock_gettime() tests\n");
 307                return;
 308        }
 309
 310        for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
 311                test_one_clock_gettime(clock, clocknames[clock]);
 312
 313        /* Also test some invalid clock ids */
 314        test_one_clock_gettime(-1, "invalid");
 315        test_one_clock_gettime(INT_MIN, "invalid");
 316        test_one_clock_gettime(INT_MAX, "invalid");
 317}
 318
 319static void test_one_clock_gettime64(int clock, const char *name)
 320{
 321        struct __kernel_timespec start, vdso, end;
 322        int vdso_ret, end_ret;
 323
 324        printf("[RUN]\tTesting clock_gettime64 for clock %s (%d)...\n", name, clock);
 325
 326        if (sys_clock_gettime64(clock, &start) < 0) {
 327                if (errno == EINVAL) {
 328                        vdso_ret = vdso_clock_gettime64(clock, &vdso);
 329                        if (vdso_ret == -EINVAL) {
 330                                printf("[OK]\tNo such clock.\n");
 331                        } else {
 332                                printf("[FAIL]\tNo such clock, but __vdso_clock_gettime64 returned %d\n", vdso_ret);
 333                                nerrs++;
 334                        }
 335                } else {
 336                        printf("[WARN]\t clock_gettime64(%d) syscall returned error %d\n", clock, errno);
 337                }
 338                return;
 339        }
 340
 341        vdso_ret = vdso_clock_gettime64(clock, &vdso);
 342        end_ret = sys_clock_gettime64(clock, &end);
 343
 344        if (vdso_ret != 0 || end_ret != 0) {
 345                printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
 346                       vdso_ret, errno);
 347                nerrs++;
 348                return;
 349        }
 350
 351        printf("\t%llu.%09lld %llu.%09lld %llu.%09lld\n",
 352               (unsigned long long)start.tv_sec, start.tv_nsec,
 353               (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
 354               (unsigned long long)end.tv_sec, end.tv_nsec);
 355
 356        if (!ts64_leq(&start, &vdso) || !ts64_leq(&vdso, &end)) {
 357                printf("[FAIL]\tTimes are out of sequence\n");
 358                nerrs++;
 359                return;
 360        }
 361
 362        printf("[OK]\tTest Passed.\n");
 363}
 364
 365static void test_clock_gettime64(void)
 366{
 367        if (!vdso_clock_gettime64) {
 368                printf("[SKIP]\tNo vDSO, so skipping clock_gettime64() tests\n");
 369                return;
 370        }
 371
 372        for (int clock = 0; clock < ARRAY_SIZE(clocknames); clock++)
 373                test_one_clock_gettime64(clock, clocknames[clock]);
 374
 375        /* Also test some invalid clock ids */
 376        test_one_clock_gettime64(-1, "invalid");
 377        test_one_clock_gettime64(INT_MIN, "invalid");
 378        test_one_clock_gettime64(INT_MAX, "invalid");
 379}
 380
 381static void test_gettimeofday(void)
 382{
 383        struct timeval start, vdso, end;
 384        struct timezone sys_tz, vdso_tz;
 385        int vdso_ret, end_ret;
 386
 387        if (!vdso_gettimeofday)
 388                return;
 389
 390        printf("[RUN]\tTesting gettimeofday...\n");
 391
 392        if (sys_gettimeofday(&start, &sys_tz) < 0) {
 393                printf("[FAIL]\tsys_gettimeofday failed (%d)\n", errno);
 394                nerrs++;
 395                return;
 396        }
 397
 398        vdso_ret = vdso_gettimeofday(&vdso, &vdso_tz);
 399        end_ret = sys_gettimeofday(&end, NULL);
 400
 401        if (vdso_ret != 0 || end_ret != 0) {
 402                printf("[FAIL]\tvDSO returned %d, syscall errno=%d\n",
 403                       vdso_ret, errno);
 404                nerrs++;
 405                return;
 406        }
 407
 408        printf("\t%llu.%06ld %llu.%06ld %llu.%06ld\n",
 409               (unsigned long long)start.tv_sec, start.tv_usec,
 410               (unsigned long long)vdso.tv_sec, vdso.tv_usec,
 411               (unsigned long long)end.tv_sec, end.tv_usec);
 412
 413        if (!tv_leq(&start, &vdso) || !tv_leq(&vdso, &end)) {
 414                printf("[FAIL]\tTimes are out of sequence\n");
 415                nerrs++;
 416        }
 417
 418        if (sys_tz.tz_minuteswest == vdso_tz.tz_minuteswest &&
 419            sys_tz.tz_dsttime == vdso_tz.tz_dsttime) {
 420                printf("[OK]\ttimezones match: minuteswest=%d, dsttime=%d\n",
 421                       sys_tz.tz_minuteswest, sys_tz.tz_dsttime);
 422        } else {
 423                printf("[FAIL]\ttimezones do not match\n");
 424                nerrs++;
 425        }
 426
 427        /* And make sure that passing NULL for tz doesn't crash. */
 428        vdso_gettimeofday(&vdso, NULL);
 429}
 430
 431int main(int argc, char **argv)
 432{
 433        name = (const char **)&names[VDSO_NAMES];
 434
 435        fill_function_pointers();
 436
 437        test_clock_gettime();
 438        test_clock_gettime64();
 439        test_gettimeofday();
 440
 441        /*
 442         * Test getcpu() last so that, if something goes wrong setting affinity,
 443         * we still run the other tests.
 444         */
 445        test_getcpu();
 446
 447        return nerrs ? 1 : 0;
 448}
 449