162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO 462306a36Sopenharmony_ci * Copyright (c) 2014-2015 Andrew Lutomirski 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This tests __kernel_vsyscall's unwind info. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define _GNU_SOURCE 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <features.h> 1262306a36Sopenharmony_ci#include <stdio.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "helpers.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciint main() 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci /* We need getauxval(). */ 2162306a36Sopenharmony_ci printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n"); 2262306a36Sopenharmony_ci return 0; 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#else 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <sys/time.h> 2862306a36Sopenharmony_ci#include <stdlib.h> 2962306a36Sopenharmony_ci#include <syscall.h> 3062306a36Sopenharmony_ci#include <unistd.h> 3162306a36Sopenharmony_ci#include <string.h> 3262306a36Sopenharmony_ci#include <inttypes.h> 3362306a36Sopenharmony_ci#include <sys/mman.h> 3462306a36Sopenharmony_ci#include <signal.h> 3562306a36Sopenharmony_ci#include <sys/ucontext.h> 3662306a36Sopenharmony_ci#include <err.h> 3762306a36Sopenharmony_ci#include <stddef.h> 3862306a36Sopenharmony_ci#include <stdbool.h> 3962306a36Sopenharmony_ci#include <sys/ptrace.h> 4062306a36Sopenharmony_ci#include <sys/user.h> 4162306a36Sopenharmony_ci#include <link.h> 4262306a36Sopenharmony_ci#include <sys/auxv.h> 4362306a36Sopenharmony_ci#include <dlfcn.h> 4462306a36Sopenharmony_ci#include <unwind.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void sethandler(int sig, void (*handler)(int, siginfo_t *, void *), 4762306a36Sopenharmony_ci int flags) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct sigaction sa; 5062306a36Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 5162306a36Sopenharmony_ci sa.sa_sigaction = handler; 5262306a36Sopenharmony_ci sa.sa_flags = SA_SIGINFO | flags; 5362306a36Sopenharmony_ci sigemptyset(&sa.sa_mask); 5462306a36Sopenharmony_ci if (sigaction(sig, &sa, 0)) 5562306a36Sopenharmony_ci err(1, "sigaction"); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic volatile sig_atomic_t nerrs; 5962306a36Sopenharmony_cistatic unsigned long sysinfo; 6062306a36Sopenharmony_cistatic bool got_sysinfo = false; 6162306a36Sopenharmony_cistatic unsigned long return_address; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct unwind_state { 6462306a36Sopenharmony_ci unsigned long ip; /* trap source */ 6562306a36Sopenharmony_ci int depth; /* -1 until we hit the trap source */ 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci_Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct unwind_state *state = opaque; 7162306a36Sopenharmony_ci unsigned long ip = _Unwind_GetIP(ctx); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (state->depth == -1) { 7462306a36Sopenharmony_ci if (ip == state->ip) 7562306a36Sopenharmony_ci state->depth = 0; 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci return _URC_NO_REASON; /* Not there yet */ 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci printf("\t 0x%lx\n", ip); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (ip == return_address) { 8262306a36Sopenharmony_ci /* Here we are. */ 8362306a36Sopenharmony_ci unsigned long eax = _Unwind_GetGR(ctx, 0); 8462306a36Sopenharmony_ci unsigned long ecx = _Unwind_GetGR(ctx, 1); 8562306a36Sopenharmony_ci unsigned long edx = _Unwind_GetGR(ctx, 2); 8662306a36Sopenharmony_ci unsigned long ebx = _Unwind_GetGR(ctx, 3); 8762306a36Sopenharmony_ci unsigned long ebp = _Unwind_GetGR(ctx, 5); 8862306a36Sopenharmony_ci unsigned long esi = _Unwind_GetGR(ctx, 6); 8962306a36Sopenharmony_ci unsigned long edi = _Unwind_GetGR(ctx, 7); 9062306a36Sopenharmony_ci bool ok = (eax == SYS_getpid || eax == getpid()) && 9162306a36Sopenharmony_ci ebx == 1 && ecx == 2 && edx == 3 && 9262306a36Sopenharmony_ci esi == 4 && edi == 5 && ebp == 6; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (!ok) 9562306a36Sopenharmony_ci nerrs++; 9662306a36Sopenharmony_ci printf("[%s]\t NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n", 9762306a36Sopenharmony_ci (ok ? "OK" : "FAIL"), 9862306a36Sopenharmony_ci eax, ebx, ecx, edx, esi, edi, ebp); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return _URC_NORMAL_STOP; 10162306a36Sopenharmony_ci } else { 10262306a36Sopenharmony_ci state->depth++; 10362306a36Sopenharmony_ci return _URC_NO_REASON; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void sigtrap(int sig, siginfo_t *info, void *ctx_void) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci ucontext_t *ctx = (ucontext_t *)ctx_void; 11062306a36Sopenharmony_ci struct unwind_state state; 11162306a36Sopenharmony_ci unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP]; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!got_sysinfo && ip == sysinfo) { 11462306a36Sopenharmony_ci got_sysinfo = true; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Find the return address. */ 11762306a36Sopenharmony_ci return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP]; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n", 12062306a36Sopenharmony_ci ip, return_address); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!got_sysinfo) 12462306a36Sopenharmony_ci return; /* Not there yet */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (ip == return_address) { 12762306a36Sopenharmony_ci ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF; 12862306a36Sopenharmony_ci printf("\tVsyscall is done\n"); 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci printf("\tSIGTRAP at 0x%lx\n", ip); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci state.ip = ip; 13562306a36Sopenharmony_ci state.depth = -1; 13662306a36Sopenharmony_ci _Unwind_Backtrace(trace_fn, &state); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciint main() 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci sysinfo = getauxval(AT_SYSINFO); 14262306a36Sopenharmony_ci printf("\tAT_SYSINFO is 0x%lx\n", sysinfo); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci Dl_info info; 14562306a36Sopenharmony_ci if (!dladdr((void *)sysinfo, &info)) { 14662306a36Sopenharmony_ci printf("[WARN]\tdladdr failed on AT_SYSINFO\n"); 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n", 14962306a36Sopenharmony_ci info.dli_fname, info.dli_fbase); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci sethandler(SIGTRAP, sigtrap, 0); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci syscall(SYS_getpid); /* Force symbol binding without TF set. */ 15562306a36Sopenharmony_ci printf("[RUN]\tSet TF and check a fast syscall\n"); 15662306a36Sopenharmony_ci set_eflags(get_eflags() | X86_EFLAGS_TF); 15762306a36Sopenharmony_ci syscall(SYS_getpid, 1, 2, 3, 4, 5, 6); 15862306a36Sopenharmony_ci if (!got_sysinfo) { 15962306a36Sopenharmony_ci set_eflags(get_eflags() & ~X86_EFLAGS_TF); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * The most likely cause of this is that you're on Debian or 16362306a36Sopenharmony_ci * a Debian-based distro, you're missing libc6-i686, and you're 16462306a36Sopenharmony_ci * affected by libc/19006 (https://sourceware.org/PR19006). 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n"); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (get_eflags() & X86_EFLAGS_TF) { 17062306a36Sopenharmony_ci printf("[FAIL]\tTF is still set\n"); 17162306a36Sopenharmony_ci nerrs++; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (nerrs) { 17562306a36Sopenharmony_ci printf("[FAIL]\tThere were errors\n"); 17662306a36Sopenharmony_ci return 1; 17762306a36Sopenharmony_ci } else { 17862306a36Sopenharmony_ci printf("[OK]\tAll is well\n"); 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#endif /* New enough libc */ 184