brintos

brintos / linux-shallow public Read only

0
0
Text · 4.2 KiB · 4c311e1 Raw
184 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO4 * Copyright (c) 2014-2015 Andrew Lutomirski5 *6 * This tests __kernel_vsyscall's unwind info.7 */8 9#define _GNU_SOURCE10 11#include <features.h>12#include <stdio.h>13 14#include "helpers.h"15 16#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 1617 18int main()19{20	/* We need getauxval(). */21	printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n");22	return 0;23}24 25#else26 27#include <sys/time.h>28#include <stdlib.h>29#include <syscall.h>30#include <unistd.h>31#include <string.h>32#include <inttypes.h>33#include <sys/mman.h>34#include <signal.h>35#include <sys/ucontext.h>36#include <err.h>37#include <stddef.h>38#include <stdbool.h>39#include <sys/ptrace.h>40#include <sys/user.h>41#include <link.h>42#include <sys/auxv.h>43#include <dlfcn.h>44#include <unwind.h>45 46static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),47		       int flags)48{49	struct sigaction sa;50	memset(&sa, 0, sizeof(sa));51	sa.sa_sigaction = handler;52	sa.sa_flags = SA_SIGINFO | flags;53	sigemptyset(&sa.sa_mask);54	if (sigaction(sig, &sa, 0))55		err(1, "sigaction");56}57 58static volatile sig_atomic_t nerrs;59static unsigned long sysinfo;60static bool got_sysinfo = false;61static unsigned long return_address;62 63struct unwind_state {64	unsigned long ip;	/* trap source */65	int depth;		/* -1 until we hit the trap source */66};67 68_Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque)69{70	struct unwind_state *state = opaque;71	unsigned long ip = _Unwind_GetIP(ctx);72 73	if (state->depth == -1) {74		if (ip == state->ip)75			state->depth = 0;76		else77			return _URC_NO_REASON;	/* Not there yet */78	}79	printf("\t  0x%lx\n", ip);80 81	if (ip == return_address) {82		/* Here we are. */83		unsigned long eax = _Unwind_GetGR(ctx, 0);84		unsigned long ecx = _Unwind_GetGR(ctx, 1);85		unsigned long edx = _Unwind_GetGR(ctx, 2);86		unsigned long ebx = _Unwind_GetGR(ctx, 3);87		unsigned long ebp = _Unwind_GetGR(ctx, 5);88		unsigned long esi = _Unwind_GetGR(ctx, 6);89		unsigned long edi = _Unwind_GetGR(ctx, 7);90		bool ok = (eax == SYS_getpid || eax == getpid()) &&91			ebx == 1 && ecx == 2 && edx == 3 &&92			esi == 4 && edi == 5 && ebp == 6;93 94		if (!ok)95			nerrs++;96		printf("[%s]\t  NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n",97		       (ok ? "OK" : "FAIL"),98		       eax, ebx, ecx, edx, esi, edi, ebp);99 100		return _URC_NORMAL_STOP;101	} else {102		state->depth++;103		return _URC_NO_REASON;104	}105}106 107static void sigtrap(int sig, siginfo_t *info, void *ctx_void)108{109	ucontext_t *ctx = (ucontext_t *)ctx_void;110	struct unwind_state state;111	unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP];112 113	if (!got_sysinfo && ip == sysinfo) {114		got_sysinfo = true;115 116		/* Find the return address. */117		return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP];118 119		printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n",120		       ip, return_address);121	}122 123	if (!got_sysinfo)124		return;		/* Not there yet */125 126	if (ip == return_address) {127		ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;128		printf("\tVsyscall is done\n");129		return;130	}131 132	printf("\tSIGTRAP at 0x%lx\n", ip);133 134	state.ip = ip;135	state.depth = -1;136	_Unwind_Backtrace(trace_fn, &state);137}138 139int main()140{141	sysinfo = getauxval(AT_SYSINFO);142	printf("\tAT_SYSINFO is 0x%lx\n", sysinfo);143 144	Dl_info info;145	if (!dladdr((void *)sysinfo, &info)) {146		printf("[WARN]\tdladdr failed on AT_SYSINFO\n");147	} else {148		printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n",149		       info.dli_fname, info.dli_fbase);150	}151 152	sethandler(SIGTRAP, sigtrap, 0);153 154	syscall(SYS_getpid);  /* Force symbol binding without TF set. */155	printf("[RUN]\tSet TF and check a fast syscall\n");156	set_eflags(get_eflags() | X86_EFLAGS_TF);157	syscall(SYS_getpid, 1, 2, 3, 4, 5, 6);158	if (!got_sysinfo) {159		set_eflags(get_eflags() & ~X86_EFLAGS_TF);160 161		/*162		 * The most likely cause of this is that you're on Debian or163		 * a Debian-based distro, you're missing libc6-i686, and you're164		 * affected by libc/19006 (https://sourceware.org/PR19006).165		 */166		printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n");167	}168 169	if (get_eflags() & X86_EFLAGS_TF) {170		printf("[FAIL]\tTF is still set\n");171		nerrs++;172	}173 174	if (nerrs) {175		printf("[FAIL]\tThere were errors\n");176		return 1;177	} else {178		printf("[OK]\tAll is well\n");179		return 0;180	}181}182 183#endif	/* New enough libc */184