brintos

brintos / linux-shallow public Read only

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