1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2023 Oxide Comptuer Company
  14  */
  15 
  16 /*
  17  * Verify that the FPU contents are correctly restored after taking a signal. We
  18  * do this by going through and setting up a signal handler for SIGINFO and then
  19  * we do the following as tightly as possible: overwriting the FPU contents and
  20  * then calling thr_kill(). As part of the regression for #15254, we also
  21  * purposefully go off CPU in the signal handler to try to wreak havoc.
  22  */
  23 
  24 #include <err.h>
  25 #include <stdlib.h>
  26 #include <ucontext.h>
  27 #include <limits.h>
  28 #include <signal.h>
  29 #include <thread.h>
  30 #include <string.h>
  31 #include <time.h>
  32 #include <unistd.h>
  33 
  34 #include "xsave_util.h"
  35 
  36 static xsu_fpu_t init_vals, signal_vals, found;
  37 static volatile int exit_status = EXIT_SUCCESS;
  38 static volatile int took_sig = 0;
  39 static uint32_t sr_hwsup;
  40 
  41 static void
  42 signal_restore_siginfo(int sig, siginfo_t *sip, void *ucp)
  43 {
  44         struct timespec ts;
  45         took_sig = 1;
  46 
  47         ts.tv_sec = 0;
  48         ts.tv_nsec = 10 * MILLISEC;
  49 
  50         /*
  51          * yield doesn't guarantee that we go off CPU, but try a few anyways.
  52          * There's a slight chance that nanosleep will modify the FPU state, but
  53          * we can hope we're lucky and that the libc function won'.
  54          */
  55         xsu_setfpu(&signal_vals, sr_hwsup);
  56         yield();
  57         yield();
  58         (void) nanosleep(&ts, NULL);
  59         xsu_getfpu(&found, sr_hwsup);
  60 
  61         if (xsu_same(&signal_vals, &found, sr_hwsup)) {
  62                 (void) printf("TEST PASSED: FPU contents didn't change in "
  63                     "signal handler\n");
  64         } else {
  65                 warnx("TEST FAILED: FPU contents changed in signal handler!");
  66                 exit_status = EXIT_FAILURE;
  67         }
  68 
  69 }
  70 
  71 int
  72 main(void)
  73 {
  74         int ret;
  75         thread_t self = thr_self();
  76         uint32_t start = arc4random();
  77         uint32_t hwsup = xsu_hwsupport();
  78         struct sigaction sa;
  79 
  80         sr_hwsup = hwsup;
  81         sa.sa_sigaction = signal_restore_siginfo;
  82         sa.sa_flags = SA_RESETHAND;
  83 
  84         if (sigaction(SIGINFO, &sa, NULL) != 0) {
  85                 errx(EXIT_FAILURE, "TEST FAILED: failed to set up signal "
  86                     "handler");
  87         }
  88 
  89         (void) printf("filling starting at 0x%x\n", start);
  90         xsu_fill(&init_vals, hwsup, start);
  91         xsu_fill(&signal_vals, hwsup, start + INT_MAX);
  92 
  93         (void) memset(&sa, 0, sizeof (struct sigaction));
  94 
  95         xsu_setfpu(&init_vals, hwsup);
  96         ret = thr_kill(self, SIGINFO);
  97         xsu_getfpu(&found, hwsup);
  98 
  99         if (ret != 0) {
 100                 errc(EXIT_FAILURE, ret, "TEST FAILED: failed to deliver "
 101                     "signal");
 102         }
 103 
 104         if (took_sig == 0) {
 105                 errx(EXIT_FAILURE, "TEST FAILED: signal handler did not run");
 106         }
 107 
 108         (void) printf("TEST PASSED: SIGINFO successfully delivered\n");
 109 
 110         if (xsu_same(&init_vals, &found, hwsup)) {
 111                 (void) printf("TEST PASSED: FPU contents successfully "
 112                     "restored\n");
 113         } else {
 114                 warnx("TEST FAILED: FPU contents were not restored!");
 115                 exit_status = EXIT_FAILURE;
 116         }
 117 
 118         return (exit_status);
 119 }