int check_regs64(void)
{ int err = 0; int num = 8;
uint64_t *r64 = ®s64.r8;
uint64_t expected = 0x7f7f7f7f7f7f7f7fULL;
if (!kernel_is_64bit) return 0;
do { if (*r64 == expected++) continue; /* register did not change */ if (syscall_addr != (long)&int80) { /* * Non-INT80 syscall entrypoints are allowed to clobber R8+ regs: * either clear them to 0, or for R11, load EFLAGS.
*/ if (*r64 == 0) continue; if (num == 11) {
printf("[NOTE]\tR11 has changed:%016llx - assuming clobbered by SYSRET insn\n", *r64); continue;
}
} else { /* * INT80 syscall entrypoint can be used by * 64-bit programs too, unlike SYSCALL/SYSENTER. * Therefore it must preserve R12+ * (they are callee-saved registers in 64-bit C ABI). * * Starting in Linux 4.17 (and any kernel that * backports the change), R8..11 are preserved. * Historically (and probably unintentionally), they * were clobbered or zeroed.
*/
}
printf("[FAIL]\tR%d has changed:%016llx\n", num, *r64);
err++;
} while (r64++, ++num < 16);
if (!err)
printf("[OK]\tR8..R15 did not leak kernel data\n"); return err;
}
if (kernel_is_64bit) {
memset(®s64, 0x77, sizeof(regs64));
call64_from_32(get_regs64); /*print_regs64();*/
}
/* * On paravirt kernels, flags are not preserved across syscalls. * Thus, we do not consider it a bug if some are changed. * We just show ones which do.
*/ if ((0x200ed7 ^ flags) != 0) {
print_flags("[WARN]\tFlags before", 0x200ed7);
print_flags("[WARN]\tFlags after", flags);
print_flags("[WARN]\tFlags change", (0x200ed7 ^ flags));
}
if (bad_arg) {
printf("[FAIL]\targ#%ld clobbered\n", bad_arg); return 1;
}
printf("[OK]\tArguments are preserved across syscall\n");
return check_regs64();
}
int run_syscall_twice()
{ int exitcode = 0; long sv;
if (syscall_addr) {
printf("[RUN]\tExecuting 6-argument 32-bit syscall via VDSO\n");
exitcode = run_syscall();
}
sv = syscall_addr;
syscall_addr = (long)&int80;
printf("[RUN]\tExecuting 6-argument 32-bit syscall via INT 80\n");
exitcode += run_syscall();
syscall_addr = sv; return exitcode;
}
void ptrace_me()
{
pid_t pid;
fflush(NULL);
pid = fork(); if (pid < 0) exit(1); if (pid == 0) { /* child */ if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) != 0) exit(0);
raise(SIGSTOP); return;
} /* parent */
printf("[RUN]\tRunning tests under ptrace\n"); while (1) { int status;
pid = waitpid(-1, &status, __WALL); if (WIFEXITED(status)) exit(WEXITSTATUS(status)); if (WIFSIGNALED(status)) exit(WTERMSIG(status)); if (pid <= 0 || !WIFSTOPPED(status)) /* paranoia */ exit(255); /* * Note: we do not inject sig = WSTOPSIG(status). * We probably should, but careful: do not inject SIGTRAP * generated by syscall entry/exit stops. * That kills the child.
*/
ptrace(PTRACE_SYSCALL, pid, 0L, 0L /*sig*/);
}
}
int main(int argc, char **argv, char **envp)
{ int exitcode = 0; int cs;
asm("\n" " movl %%cs, %%eax\n"
: "=a" (cs)
);
kernel_is_64bit = (cs == 0x23); if (!kernel_is_64bit)
printf("[NOTE]\tNot a 64-bit kernel, won't test R8..R15 leaks\n");
/* This only works for non-static builds: * syscall_addr = dlsym(dlopen("linux-gate.so.1", RTLD_NOW), "__kernel_vsyscall");
*/
syscall_addr = get_syscall(envp);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.