0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #undef _GNU_SOURCE
0012 #define _GNU_SOURCE 1
0013 #undef __USE_GNU
0014 #define __USE_GNU 1
0015 #include <unistd.h>
0016 #include <stdlib.h>
0017 #include <string.h>
0018 #include <stdio.h>
0019 #include <signal.h>
0020 #include <sys/types.h>
0021 #include <sys/select.h>
0022 #include <sys/time.h>
0023 #include <elf.h>
0024 #include <sys/ptrace.h>
0025 #include <sys/wait.h>
0026
0027 #if !defined(__i386__)
0028 int main(int argc, char **argv, char **envp)
0029 {
0030 printf("[SKIP]\tNot a 32-bit x86 userspace\n");
0031 return 0;
0032 }
0033 #else
0034
0035 long syscall_addr;
0036 long get_syscall(char **envp)
0037 {
0038 Elf32_auxv_t *auxv;
0039 while (*envp++ != NULL)
0040 continue;
0041 for (auxv = (void *)envp; auxv->a_type != AT_NULL; auxv++)
0042 if (auxv->a_type == AT_SYSINFO)
0043 return auxv->a_un.a_val;
0044 printf("[WARN]\tAT_SYSINFO not supplied\n");
0045 return 0;
0046 }
0047
0048 asm (
0049 " .pushsection .text\n"
0050 " .global int80\n"
0051 "int80:\n"
0052 " int $0x80\n"
0053 " ret\n"
0054 " .popsection\n"
0055 );
0056 extern char int80;
0057
0058 struct regs64 {
0059 uint64_t rax, rbx, rcx, rdx;
0060 uint64_t rsi, rdi, rbp, rsp;
0061 uint64_t r8, r9, r10, r11;
0062 uint64_t r12, r13, r14, r15;
0063 };
0064 struct regs64 regs64;
0065 int kernel_is_64bit;
0066
0067 asm (
0068 " .pushsection .text\n"
0069 " .code64\n"
0070 "get_regs64:\n"
0071 " push %rax\n"
0072 " mov $regs64, %eax\n"
0073 " pop 0*8(%rax)\n"
0074 " movq %rbx, 1*8(%rax)\n"
0075 " movq %rcx, 2*8(%rax)\n"
0076 " movq %rdx, 3*8(%rax)\n"
0077 " movq %rsi, 4*8(%rax)\n"
0078 " movq %rdi, 5*8(%rax)\n"
0079 " movq %rbp, 6*8(%rax)\n"
0080 " movq %rsp, 7*8(%rax)\n"
0081 " movq %r8, 8*8(%rax)\n"
0082 " movq %r9, 9*8(%rax)\n"
0083 " movq %r10, 10*8(%rax)\n"
0084 " movq %r11, 11*8(%rax)\n"
0085 " movq %r12, 12*8(%rax)\n"
0086 " movq %r13, 13*8(%rax)\n"
0087 " movq %r14, 14*8(%rax)\n"
0088 " movq %r15, 15*8(%rax)\n"
0089 " ret\n"
0090 "poison_regs64:\n"
0091 " movq $0x7f7f7f7f, %r8\n"
0092 " shl $32, %r8\n"
0093 " orq $0x7f7f7f7f, %r8\n"
0094 " movq %r8, %r9\n"
0095 " incq %r9\n"
0096 " movq %r9, %r10\n"
0097 " incq %r10\n"
0098 " movq %r10, %r11\n"
0099 " incq %r11\n"
0100 " movq %r11, %r12\n"
0101 " incq %r12\n"
0102 " movq %r12, %r13\n"
0103 " incq %r13\n"
0104 " movq %r13, %r14\n"
0105 " incq %r14\n"
0106 " movq %r14, %r15\n"
0107 " incq %r15\n"
0108 " ret\n"
0109 " .code32\n"
0110 " .popsection\n"
0111 );
0112 extern void get_regs64(void);
0113 extern void poison_regs64(void);
0114 extern unsigned long call64_from_32(void (*function)(void));
0115 void print_regs64(void)
0116 {
0117 if (!kernel_is_64bit)
0118 return;
0119 printf("ax:%016llx bx:%016llx cx:%016llx dx:%016llx\n", regs64.rax, regs64.rbx, regs64.rcx, regs64.rdx);
0120 printf("si:%016llx di:%016llx bp:%016llx sp:%016llx\n", regs64.rsi, regs64.rdi, regs64.rbp, regs64.rsp);
0121 printf(" 8:%016llx 9:%016llx 10:%016llx 11:%016llx\n", regs64.r8 , regs64.r9 , regs64.r10, regs64.r11);
0122 printf("12:%016llx 13:%016llx 14:%016llx 15:%016llx\n", regs64.r12, regs64.r13, regs64.r14, regs64.r15);
0123 }
0124
0125 int check_regs64(void)
0126 {
0127 int err = 0;
0128 int num = 8;
0129 uint64_t *r64 = ®s64.r8;
0130 uint64_t expected = 0x7f7f7f7f7f7f7f7fULL;
0131
0132 if (!kernel_is_64bit)
0133 return 0;
0134
0135 do {
0136 if (*r64 == expected++)
0137 continue;
0138 if (syscall_addr != (long)&int80) {
0139
0140
0141
0142
0143 if (*r64 == 0)
0144 continue;
0145 if (num == 11) {
0146 printf("[NOTE]\tR11 has changed:%016llx - assuming clobbered by SYSRET insn\n", *r64);
0147 continue;
0148 }
0149 } else {
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161 }
0162 printf("[FAIL]\tR%d has changed:%016llx\n", num, *r64);
0163 err++;
0164 } while (r64++, ++num < 16);
0165
0166 if (!err)
0167 printf("[OK]\tR8..R15 did not leak kernel data\n");
0168 return err;
0169 }
0170
0171 int nfds;
0172 fd_set rfds;
0173 fd_set wfds;
0174 fd_set efds;
0175 struct timespec timeout;
0176 sigset_t sigmask;
0177 struct {
0178 sigset_t *sp;
0179 int sz;
0180 } sigmask_desc;
0181
0182 void prep_args()
0183 {
0184 nfds = 42;
0185 FD_ZERO(&rfds);
0186 FD_ZERO(&wfds);
0187 FD_ZERO(&efds);
0188 FD_SET(0, &rfds);
0189 FD_SET(1, &wfds);
0190 FD_SET(2, &efds);
0191 timeout.tv_sec = 0;
0192 timeout.tv_nsec = 123;
0193 sigemptyset(&sigmask);
0194 sigaddset(&sigmask, SIGINT);
0195 sigaddset(&sigmask, SIGUSR2);
0196 sigaddset(&sigmask, SIGRTMAX);
0197 sigmask_desc.sp = &sigmask;
0198 sigmask_desc.sz = 8;
0199 }
0200
0201 static void print_flags(const char *name, unsigned long r)
0202 {
0203 static const char *bitarray[] = {
0204 "\n" ,"c\n" ,
0205 "0 " ,"1 " ,
0206 "" ,"p " ,
0207 "0 " ,"3? " ,
0208 "" ,"a " ,
0209 "0 " ,"5? " ,
0210 "" ,"z " ,
0211 "" ,"s " ,
0212 "" ,"t " ,
0213 "" ,"i " ,
0214 "" ,"d " ,
0215 "" ,"o " ,
0216 "0 " ,"1 " ,
0217 "0" ,"1" ,
0218 "" ,"n " ,
0219 "0 " ,"15? ",
0220 "" ,"r " ,
0221 "" ,"v " ,
0222 "" ,"ac " ,
0223 "" ,"vif ",
0224 "" ,"vip ",
0225 "" ,"id " ,
0226 NULL
0227 };
0228 const char **bitstr;
0229 int bit;
0230
0231 printf("%s=%016lx ", name, r);
0232 bitstr = bitarray + 42;
0233 bit = 21;
0234 if ((r >> 22) != 0)
0235 printf("(extra bits are set) ");
0236 do {
0237 if (bitstr[(r >> bit) & 1][0])
0238 fputs(bitstr[(r >> bit) & 1], stdout);
0239 bitstr -= 2;
0240 bit--;
0241 } while (bit >= 0);
0242 }
0243
0244 int run_syscall(void)
0245 {
0246 long flags, bad_arg;
0247
0248 prep_args();
0249
0250 if (kernel_is_64bit)
0251 call64_from_32(poison_regs64);
0252
0253
0254 asm("\n"
0255
0256 " push %%ebp\n"
0257 " mov $308, %%eax\n"
0258 " mov nfds, %%ebx\n"
0259 " mov $rfds, %%ecx\n"
0260 " mov $wfds, %%edx\n"
0261 " mov $efds, %%esi\n"
0262 " mov $timeout, %%edi\n"
0263 " mov $sigmask_desc, %%ebp\n"
0264 " push $0x200ed7\n"
0265 " popf\n"
0266 " call *syscall_addr\n"
0267
0268 " pushf\n"
0269 " pop %%eax\n"
0270 " cld\n"
0271 " cmp nfds, %%ebx\n"
0272 " mov $1, %%ebx\n"
0273 " jne 1f\n"
0274 " cmp $rfds, %%ecx\n"
0275 " mov $2, %%ebx\n"
0276 " jne 1f\n"
0277 " cmp $wfds, %%edx\n"
0278 " mov $3, %%ebx\n"
0279 " jne 1f\n"
0280 " cmp $efds, %%esi\n"
0281 " mov $4, %%ebx\n"
0282 " jne 1f\n"
0283 " cmp $timeout, %%edi\n"
0284 " mov $5, %%ebx\n"
0285 " jne 1f\n"
0286 " cmpl $sigmask_desc, %%ebp\n"
0287 " mov $6, %%ebx\n"
0288 " jne 1f\n"
0289 " mov $0, %%ebx\n"
0290 "1:\n"
0291 " pop %%ebp\n"
0292 : "=a" (flags), "=b" (bad_arg)
0293 :
0294 : "cx", "dx", "si", "di"
0295 );
0296
0297 if (kernel_is_64bit) {
0298 memset(®s64, 0x77, sizeof(regs64));
0299 call64_from_32(get_regs64);
0300
0301 }
0302
0303
0304
0305
0306
0307
0308 if ((0x200ed7 ^ flags) != 0) {
0309 print_flags("[WARN]\tFlags before", 0x200ed7);
0310 print_flags("[WARN]\tFlags after", flags);
0311 print_flags("[WARN]\tFlags change", (0x200ed7 ^ flags));
0312 }
0313
0314 if (bad_arg) {
0315 printf("[FAIL]\targ#%ld clobbered\n", bad_arg);
0316 return 1;
0317 }
0318 printf("[OK]\tArguments are preserved across syscall\n");
0319
0320 return check_regs64();
0321 }
0322
0323 int run_syscall_twice()
0324 {
0325 int exitcode = 0;
0326 long sv;
0327
0328 if (syscall_addr) {
0329 printf("[RUN]\tExecuting 6-argument 32-bit syscall via VDSO\n");
0330 exitcode = run_syscall();
0331 }
0332 sv = syscall_addr;
0333 syscall_addr = (long)&int80;
0334 printf("[RUN]\tExecuting 6-argument 32-bit syscall via INT 80\n");
0335 exitcode += run_syscall();
0336 syscall_addr = sv;
0337 return exitcode;
0338 }
0339
0340 void ptrace_me()
0341 {
0342 pid_t pid;
0343
0344 fflush(NULL);
0345 pid = fork();
0346 if (pid < 0)
0347 exit(1);
0348 if (pid == 0) {
0349
0350 if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) != 0)
0351 exit(0);
0352 raise(SIGSTOP);
0353 return;
0354 }
0355
0356 printf("[RUN]\tRunning tests under ptrace\n");
0357 while (1) {
0358 int status;
0359 pid = waitpid(-1, &status, __WALL);
0360 if (WIFEXITED(status))
0361 exit(WEXITSTATUS(status));
0362 if (WIFSIGNALED(status))
0363 exit(WTERMSIG(status));
0364 if (pid <= 0 || !WIFSTOPPED(status))
0365 exit(255);
0366
0367
0368
0369
0370
0371
0372 ptrace(PTRACE_SYSCALL, pid, 0L, 0L );
0373 }
0374 }
0375
0376 int main(int argc, char **argv, char **envp)
0377 {
0378 int exitcode = 0;
0379 int cs;
0380
0381 asm("\n"
0382 " movl %%cs, %%eax\n"
0383 : "=a" (cs)
0384 );
0385 kernel_is_64bit = (cs == 0x23);
0386 if (!kernel_is_64bit)
0387 printf("[NOTE]\tNot a 64-bit kernel, won't test R8..R15 leaks\n");
0388
0389
0390
0391
0392 syscall_addr = get_syscall(envp);
0393
0394 exitcode += run_syscall_twice();
0395 ptrace_me();
0396 exitcode += run_syscall_twice();
0397
0398 return exitcode;
0399 }
0400 #endif