brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.6 KiB · 396aef7 Raw
122 lines · c
1#include <asm/hwcap.h>2#include <stdbool.h>3#include <sys/auxv.h>4#include <sys/prctl.h>5 6#ifndef HWCAP_GCS7#define HWCAP_GCS (1UL << 32)8#endif9 10#define PR_GET_SHADOW_STACK_STATUS 7411#define PR_SET_SHADOW_STACK_STATUS 7512#define PR_LOCK_SHADOW_STACK_STATUS 7613 14#define PR_SHADOW_STACK_ENABLE (1UL << 0)15#define PR_SHADOW_STACK_WRITE (1UL << 1)16#define PR_SHADOW_STACK_PUSH (1UL << 2)17 18#define PRCTL_SYSCALL_NO 16719 20// Once we enable GCS, we cannot return from the function that made the syscall21// to enable it. This is because the control stack is empty, there is no valid22// address for us to return to. So for the initial enable we must use inline asm23// instead of the libc's prctl wrapper function.24#define my_prctl(option, arg2, arg3, arg4, arg5)                               \25  ({                                                                           \26    register unsigned long x0 __asm__("x0") = option;                          \27    register unsigned long x1 __asm__("x1") = arg2;                            \28    register unsigned long x2 __asm__("x2") = arg3;                            \29    register unsigned long x3 __asm__("x3") = arg4;                            \30    register unsigned long x4 __asm__("x4") = arg5;                            \31    register unsigned long x8 __asm__("x8") = PRCTL_SYSCALL_NO;                \32    __asm__ __volatile__("svc #0\n"                                            \33                         : "=r"(x0)                                            \34                         : "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4),        \35                           "r"(x8)                                             \36                         : "cc", "memory");                                    \37  })38 39unsigned long get_gcs_status() {40  unsigned long mode = 0;41  prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);42  return mode;43}44 45extern void _start();46bool change_gcs_config(bool enable) {47  // The test unlocks and disables all features (excluding the main enable bit)48  // before calling this expression. Enable them again.49  unsigned long new_status =50      enable | PR_SHADOW_STACK_PUSH | PR_SHADOW_STACK_WRITE;51 52  if (enable) {53    // We would not be able to return from prctl().54    my_prctl(PR_SET_SHADOW_STACK_STATUS, new_status, 0, 0, 0);55 56    // This is a stack, so we must push in reverse order to the pops we want to57    // have later. So push the return of __lldb_expr (_start), then the return58    // address of this function (__lldb_expr).59    __asm__ __volatile__("sys	#3, C7, C7, #0, %0\n"  // gcspushm _start60                         "sys	#3, C7, C7, #0, x30\n" // gcspushm x3061                         :62                         : "r"(_start));63  } else {64    if (prctl(PR_SET_SHADOW_STACK_STATUS, new_status, 0, 0, 0) != 0)65      return false;66  }67 68  // Turn back on all locks.69  if (prctl(PR_LOCK_SHADOW_STACK_STATUS, ~(0UL), 0, 0, 0) != 0)70    return false;71 72  return true;73}74 75void gcs_signal() {76  // If we enabled GCS manually, then we could just return from main to generate77  // a signal. However, if the C library enabled it, then we'd just exit78  // normally. Assume the latter, and try to return to some bogus address to79  // generate the signal.80  __asm__ __volatile__(81      // Corrupt the link register. This could be many numbers but 16 is a82      // nicely aligned value that is unlikely to result in a fault because the83      // PC is misaligned, which would hide the GCS fault.84      "add x30, x30, #10\n"85      "ret\n");86}87 88// These functions are used to observe gcspr_el0 changing as we enter them, and89// the fault we cause by changing its value. Also used to check expression90// eval can handle function calls.91int test_func2() { return 99; }92 93int test_func() { return test_func2(); }94 95int main() {96  if (!(getauxval(AT_HWCAP) & HWCAP_GCS))97    return 1;98 99  unsigned long mode = get_gcs_status();100  if ((mode & 1) == 0) {101    // If GCS wasn't already enabled by the C library, enable it.102    my_prctl(PR_SET_SHADOW_STACK_STATUS, PR_SHADOW_STACK_ENABLE, 0, 0, 0);103    // From this point on, we cannot return from main without faulting because104    // the return address from main, and every function before that, is not on105    // the guarded control stack.106  }107 108  // By now we should have one memory region where the GCS is stored.109 110  // For register read/write tests.111  volatile int i = test_func();112 113  // If this was a register test, we would have disabled GCS during the114  // test_func call. We cannot re-enable it from ptrace so skip this part in115  // this case.116  mode = get_gcs_status();117  if ((mode & 1) == 1)118    gcs_signal(); // Set break point at this line.119 120  return 0;121}122