Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * unwind_vdso.c - tests unwind info for AT_SYSINFO in the vDSO
0004  * Copyright (c) 2014-2015 Andrew Lutomirski
0005  *
0006  * This tests __kernel_vsyscall's unwind info.
0007  */
0008 
0009 #define _GNU_SOURCE
0010 
0011 #include <features.h>
0012 #include <stdio.h>
0013 
0014 #include "helpers.h"
0015 
0016 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16
0017 
0018 int main()
0019 {
0020     /* We need getauxval(). */
0021     printf("[SKIP]\tGLIBC before 2.16 cannot compile this test\n");
0022     return 0;
0023 }
0024 
0025 #else
0026 
0027 #include <sys/time.h>
0028 #include <stdlib.h>
0029 #include <syscall.h>
0030 #include <unistd.h>
0031 #include <string.h>
0032 #include <inttypes.h>
0033 #include <sys/mman.h>
0034 #include <signal.h>
0035 #include <sys/ucontext.h>
0036 #include <err.h>
0037 #include <stddef.h>
0038 #include <stdbool.h>
0039 #include <sys/ptrace.h>
0040 #include <sys/user.h>
0041 #include <link.h>
0042 #include <sys/auxv.h>
0043 #include <dlfcn.h>
0044 #include <unwind.h>
0045 
0046 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
0047                int flags)
0048 {
0049     struct sigaction sa;
0050     memset(&sa, 0, sizeof(sa));
0051     sa.sa_sigaction = handler;
0052     sa.sa_flags = SA_SIGINFO | flags;
0053     sigemptyset(&sa.sa_mask);
0054     if (sigaction(sig, &sa, 0))
0055         err(1, "sigaction");
0056 }
0057 
0058 static volatile sig_atomic_t nerrs;
0059 static unsigned long sysinfo;
0060 static bool got_sysinfo = false;
0061 static unsigned long return_address;
0062 
0063 struct unwind_state {
0064     unsigned long ip;   /* trap source */
0065     int depth;      /* -1 until we hit the trap source */
0066 };
0067 
0068 _Unwind_Reason_Code trace_fn(struct _Unwind_Context * ctx, void *opaque)
0069 {
0070     struct unwind_state *state = opaque;
0071     unsigned long ip = _Unwind_GetIP(ctx);
0072 
0073     if (state->depth == -1) {
0074         if (ip == state->ip)
0075             state->depth = 0;
0076         else
0077             return _URC_NO_REASON;  /* Not there yet */
0078     }
0079     printf("\t  0x%lx\n", ip);
0080 
0081     if (ip == return_address) {
0082         /* Here we are. */
0083         unsigned long eax = _Unwind_GetGR(ctx, 0);
0084         unsigned long ecx = _Unwind_GetGR(ctx, 1);
0085         unsigned long edx = _Unwind_GetGR(ctx, 2);
0086         unsigned long ebx = _Unwind_GetGR(ctx, 3);
0087         unsigned long ebp = _Unwind_GetGR(ctx, 5);
0088         unsigned long esi = _Unwind_GetGR(ctx, 6);
0089         unsigned long edi = _Unwind_GetGR(ctx, 7);
0090         bool ok = (eax == SYS_getpid || eax == getpid()) &&
0091             ebx == 1 && ecx == 2 && edx == 3 &&
0092             esi == 4 && edi == 5 && ebp == 6;
0093 
0094         if (!ok)
0095             nerrs++;
0096         printf("[%s]\t  NR = %ld, args = %ld, %ld, %ld, %ld, %ld, %ld\n",
0097                (ok ? "OK" : "FAIL"),
0098                eax, ebx, ecx, edx, esi, edi, ebp);
0099 
0100         return _URC_NORMAL_STOP;
0101     } else {
0102         state->depth++;
0103         return _URC_NO_REASON;
0104     }
0105 }
0106 
0107 static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
0108 {
0109     ucontext_t *ctx = (ucontext_t *)ctx_void;
0110     struct unwind_state state;
0111     unsigned long ip = ctx->uc_mcontext.gregs[REG_EIP];
0112 
0113     if (!got_sysinfo && ip == sysinfo) {
0114         got_sysinfo = true;
0115 
0116         /* Find the return address. */
0117         return_address = *(unsigned long *)(unsigned long)ctx->uc_mcontext.gregs[REG_ESP];
0118 
0119         printf("\tIn vsyscall at 0x%lx, returning to 0x%lx\n",
0120                ip, return_address);
0121     }
0122 
0123     if (!got_sysinfo)
0124         return;     /* Not there yet */
0125 
0126     if (ip == return_address) {
0127         ctx->uc_mcontext.gregs[REG_EFL] &= ~X86_EFLAGS_TF;
0128         printf("\tVsyscall is done\n");
0129         return;
0130     }
0131 
0132     printf("\tSIGTRAP at 0x%lx\n", ip);
0133 
0134     state.ip = ip;
0135     state.depth = -1;
0136     _Unwind_Backtrace(trace_fn, &state);
0137 }
0138 
0139 int main()
0140 {
0141     sysinfo = getauxval(AT_SYSINFO);
0142     printf("\tAT_SYSINFO is 0x%lx\n", sysinfo);
0143 
0144     Dl_info info;
0145     if (!dladdr((void *)sysinfo, &info)) {
0146         printf("[WARN]\tdladdr failed on AT_SYSINFO\n");
0147     } else {
0148         printf("[OK]\tAT_SYSINFO maps to %s, loaded at 0x%p\n",
0149                info.dli_fname, info.dli_fbase);
0150     }
0151 
0152     sethandler(SIGTRAP, sigtrap, 0);
0153 
0154     syscall(SYS_getpid);  /* Force symbol binding without TF set. */
0155     printf("[RUN]\tSet TF and check a fast syscall\n");
0156     set_eflags(get_eflags() | X86_EFLAGS_TF);
0157     syscall(SYS_getpid, 1, 2, 3, 4, 5, 6);
0158     if (!got_sysinfo) {
0159         set_eflags(get_eflags() & ~X86_EFLAGS_TF);
0160 
0161         /*
0162          * The most likely cause of this is that you're on Debian or
0163          * a Debian-based distro, you're missing libc6-i686, and you're
0164          * affected by libc/19006 (https://sourceware.org/PR19006).
0165          */
0166         printf("[WARN]\tsyscall(2) didn't enter AT_SYSINFO\n");
0167     }
0168 
0169     if (get_eflags() & X86_EFLAGS_TF) {
0170         printf("[FAIL]\tTF is still set\n");
0171         nerrs++;
0172     }
0173 
0174     if (nerrs) {
0175         printf("[FAIL]\tThere were errors\n");
0176         return 1;
0177     } else {
0178         printf("[OK]\tAll is well\n");
0179         return 0;
0180     }
0181 }
0182 
0183 #endif  /* New enough libc */