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