0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define _GNU_SOURCE
0011 #include <stdio.h>
0012 #include <stdlib.h>
0013 #include <string.h>
0014 #include <signal.h>
0015
0016 #include <unistd.h>
0017
0018 #include "pkeys.h"
0019
0020 #define PPC_INST_NOP 0x60000000
0021 #define PPC_INST_TRAP 0x7fe00008
0022 #define PPC_INST_BLR 0x4e800020
0023
0024 static volatile sig_atomic_t fault_pkey, fault_code, fault_type;
0025 static volatile sig_atomic_t remaining_faults;
0026 static volatile unsigned int *fault_addr;
0027 static unsigned long pgsize, numinsns;
0028 static unsigned int *insns;
0029
0030 static void trap_handler(int signum, siginfo_t *sinfo, void *ctx)
0031 {
0032
0033 if (sinfo->si_addr != (void *) fault_addr)
0034 sigsafe_err("got a fault for an unexpected address\n");
0035
0036 _exit(1);
0037 }
0038
0039 static void segv_handler(int signum, siginfo_t *sinfo, void *ctx)
0040 {
0041 int signal_pkey;
0042
0043 signal_pkey = siginfo_pkey(sinfo);
0044 fault_code = sinfo->si_code;
0045
0046
0047 if (sinfo->si_addr != (void *) fault_addr) {
0048 sigsafe_err("got a fault for an unexpected address\n");
0049 _exit(1);
0050 }
0051
0052
0053 if (!remaining_faults) {
0054 sigsafe_err("got too many faults for the same address\n");
0055 _exit(1);
0056 }
0057
0058
0059
0060 switch (fault_code) {
0061 case SEGV_ACCERR:
0062 if (mprotect(insns, pgsize, PROT_READ | PROT_WRITE)) {
0063 sigsafe_err("failed to set access permissions\n");
0064 _exit(1);
0065 }
0066 break;
0067 case SEGV_PKUERR:
0068 if (signal_pkey != fault_pkey) {
0069 sigsafe_err("got a fault for an unexpected pkey\n");
0070 _exit(1);
0071 }
0072
0073 switch (fault_type) {
0074 case PKEY_DISABLE_ACCESS:
0075 pkey_set_rights(fault_pkey, 0);
0076 break;
0077 case PKEY_DISABLE_EXECUTE:
0078
0079
0080
0081
0082
0083
0084 if (mprotect(insns, pgsize, PROT_EXEC)) {
0085 sigsafe_err("failed to set execute permissions\n");
0086 _exit(1);
0087 }
0088 break;
0089 default:
0090 sigsafe_err("got a fault with an unexpected type\n");
0091 _exit(1);
0092 }
0093 break;
0094 default:
0095 sigsafe_err("got a fault with an unexpected code\n");
0096 _exit(1);
0097 }
0098
0099 remaining_faults--;
0100 }
0101
0102 static int test(void)
0103 {
0104 struct sigaction segv_act, trap_act;
0105 unsigned long rights;
0106 int pkey, ret, i;
0107
0108 ret = pkeys_unsupported();
0109 if (ret)
0110 return ret;
0111
0112
0113 segv_act.sa_handler = 0;
0114 segv_act.sa_sigaction = segv_handler;
0115 FAIL_IF(sigprocmask(SIG_SETMASK, 0, &segv_act.sa_mask) != 0);
0116 segv_act.sa_flags = SA_SIGINFO;
0117 segv_act.sa_restorer = 0;
0118 FAIL_IF(sigaction(SIGSEGV, &segv_act, NULL) != 0);
0119
0120
0121 trap_act.sa_handler = 0;
0122 trap_act.sa_sigaction = trap_handler;
0123 FAIL_IF(sigprocmask(SIG_SETMASK, 0, &trap_act.sa_mask) != 0);
0124 trap_act.sa_flags = SA_SIGINFO;
0125 trap_act.sa_restorer = 0;
0126 FAIL_IF(sigaction(SIGTRAP, &trap_act, NULL) != 0);
0127
0128
0129 pgsize = getpagesize();
0130 numinsns = pgsize / sizeof(unsigned int);
0131 insns = (unsigned int *) mmap(NULL, pgsize, PROT_READ | PROT_WRITE,
0132 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
0133 FAIL_IF(insns == MAP_FAILED);
0134
0135
0136 for (i = 1; i < numinsns - 1; i++)
0137 insns[i] = PPC_INST_NOP;
0138
0139
0140
0141
0142
0143
0144 insns[0] = PPC_INST_TRAP;
0145
0146
0147
0148
0149
0150
0151 insns[numinsns - 1] = PPC_INST_BLR;
0152
0153
0154 rights = PKEY_DISABLE_EXECUTE;
0155 pkey = sys_pkey_alloc(0, rights);
0156 FAIL_IF(pkey < 0);
0157
0158
0159
0160
0161
0162 fault_addr = insns;
0163
0164
0165 fault_type = -1;
0166 fault_pkey = -1;
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178 remaining_faults = 0;
0179 FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
0180 printf("read from %p, pkey permissions are %s\n", fault_addr,
0181 pkey_rights(rights));
0182 i = *fault_addr;
0183 FAIL_IF(remaining_faults != 0);
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195 remaining_faults = 1;
0196 FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
0197 printf("write to %p, pkey permissions are %s\n", fault_addr,
0198 pkey_rights(rights));
0199 *fault_addr = PPC_INST_TRAP;
0200 FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR);
0201
0202
0203 rights |= PKEY_DISABLE_ACCESS;
0204 fault_type = PKEY_DISABLE_ACCESS;
0205 fault_pkey = pkey;
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215 remaining_faults = 1;
0216 FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
0217 pkey_set_rights(pkey, rights);
0218 printf("read from %p, pkey permissions are %s\n", fault_addr,
0219 pkey_rights(rights));
0220 i = *fault_addr;
0221 FAIL_IF(remaining_faults != 0 || fault_code != SEGV_PKUERR);
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232 remaining_faults = 2;
0233 FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
0234 pkey_set_rights(pkey, rights);
0235 printf("write to %p, pkey permissions are %s\n", fault_addr,
0236 pkey_rights(rights));
0237 *fault_addr = PPC_INST_NOP;
0238 FAIL_IF(remaining_faults != 0 || fault_code != SEGV_ACCERR);
0239
0240
0241 sys_pkey_free(pkey);
0242
0243 rights = 0;
0244 do {
0245
0246
0247
0248
0249 pkey = sys_pkey_alloc(0, rights);
0250 FAIL_IF(pkey < 0);
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262 fault_pkey = pkey;
0263 fault_type = -1;
0264 remaining_faults = 0;
0265 if (rights & PKEY_DISABLE_EXECUTE) {
0266 fault_type = PKEY_DISABLE_EXECUTE;
0267 remaining_faults = 1;
0268 }
0269
0270 FAIL_IF(sys_pkey_mprotect(insns, pgsize, PROT_EXEC, pkey) != 0);
0271 printf("execute at %p, pkey permissions are %s\n", fault_addr,
0272 pkey_rights(rights));
0273 asm volatile("mtctr %0; bctrl" : : "r"(insns));
0274 FAIL_IF(remaining_faults != 0);
0275 if (rights & PKEY_DISABLE_EXECUTE)
0276 FAIL_IF(fault_code != SEGV_PKUERR);
0277
0278
0279 sys_pkey_free(pkey);
0280
0281
0282 rights = next_pkey_rights(rights);
0283 } while (rights);
0284
0285
0286 munmap((void *) insns, pgsize);
0287
0288 return 0;
0289 }
0290
0291 int main(void)
0292 {
0293 return test_harness(test, "pkey_exec_prot");
0294 }