diff options
author | Andy Lutomirski <luto@mit.edu> | 2011-06-16 16:49:51 -0400 |
---|---|---|
committer | Andy Lutomirski <luto@mit.edu> | 2011-06-16 16:49:51 -0400 |
commit | c16e2637a871527ac0f1c3790f8cf96cb41dfcb0 (patch) | |
tree | 1fc53b76249fcf5dee5feba287b5cdff0768e101 | |
parent | d892847eea2cca8ff86877919c360bbdfb1e15fe (diff) | |
download | misc-tests-c16e2637a871527ac0f1c3790f8cf96cb41dfcb0.tar.gz |
test_vsyscall: Add intcc32 test case to try int 0xcc from a 32-bit CS
-rw-r--r-- | test_vsyscall.cc | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/test_vsyscall.cc b/test_vsyscall.cc index c3976b9..cbb1fe7 100644 --- a/test_vsyscall.cc +++ b/test_vsyscall.cc @@ -11,6 +11,16 @@ #include <inttypes.h> #include <signal.h> #include <sys/ucontext.h> +#include <asm/ldt.h> +#include <errno.h> + +static inline int modify_ldt(int mode, void *ptr, unsigned long size) +{ + int ret = syscall(__NR_modify_ldt, mode, ptr, size); + if (ret != 0) + errno = -ret; + return (ret == 0 ? 0 : -1); +} /* vsyscalls and vDSO */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); @@ -257,6 +267,90 @@ int call(int argc, char **argv) int intcc(int argc, char **argv) { + if (argc != 0) { + printf("Usage: intcc\n"); + return 1; + } + + extern char intcc_addr; + printf("About to execute int 0xcc from RIP = %lX\n", + (unsigned long)&intcc_addr); + + asm volatile ("intcc_addr: int $0xcc"); + return 0; +} + +struct __attribute__((packed)) farptr { + uint32_t offset; + uint16_t sel; +}; + +static bool to_farptr(farptr *out, uint16_t sel, void *offset) +{ + out->sel = sel; + out->offset = (uint32_t)(unsigned long)offset; + return out->offset == (unsigned long)offset; +} + +int intcc32(int argc, char **argv) +{ + if (argc != 0) { + printf("Usage: intcc32\n"); + return 1; + } + + // Install a 32-bit code descriptor + struct user_desc desc; + memset(&desc, 0, sizeof(desc)); + desc.entry_number = 0; + desc.base_addr = 0; + desc.limit = 0xFFFFF; + desc.seg_32bit = 1; + desc.contents = MODIFY_LDT_CONTENTS_CODE; + desc.limit_in_pages = 1; + + if (modify_ldt(1, &desc, sizeof(desc)) != 0) { + perror("modify_ldt"); + return 1; + } + + /* Load the initial CS. */ + uint16_t initial_cs; + asm ("mov %%cs,%[initial_cs]" : [initial_cs] "=rm" (initial_cs)); + printf("Initial CS = 0x%04X (entry %d)\n", + (unsigned)initial_cs, (int)(initial_cs >> 3)); + + extern char landing_32, landing_64; + + /* Set up the pointers. */ + static farptr ptr32, ptr64; + if (!to_farptr(&ptr32, 0x4, &landing_32) || !to_farptr(&ptr64, initial_cs, &landing_64)) { + printf("Something's mapped too high\n"); + return 1; + } + + /* Go for it! */ + asm volatile ( + "mov %%rsp,%%rsi\n" // Save rsp (avoids truncation). + "ljmpl *(%%eax)\n" // Switch to 32-bit mode. + + // 32-bit mode! + // (Well, sort of. DS and ES are 0, so we can't use them.) + ".code32\n" + "landing_32:\n" + "\tint $0xcc\n" // Try int 0xcc. + "\tljmpl *%%cs:(%%ecx)\n" // Switch back. + + // 64-bit mode again! + ".code64\n" + "landing_64:\n" + "\tmov %%rsi,%%rsp" + : + : "a" (&ptr32), "c" (&ptr64) + : "rsi", "cc"); + + printf("Holy cow! We survived!\n"); + return 0; } @@ -283,6 +377,8 @@ int main(int argc, char **argv) return bench(argc - 2, argv + 2); if (!strcmp(argv[1], "intcc")) return intcc(argc - 2, argv + 2); + if (!strcmp(argv[1], "intcc32")) + return intcc32(argc - 2, argv + 2); if (!strcmp(argv[1], "call")) return call(argc - 2, argv + 2); |