summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@mit.edu>2011-06-16 16:49:51 -0400
committerAndy Lutomirski <luto@mit.edu>2011-06-16 16:49:51 -0400
commitc16e2637a871527ac0f1c3790f8cf96cb41dfcb0 (patch)
tree1fc53b76249fcf5dee5feba287b5cdff0768e101
parentd892847eea2cca8ff86877919c360bbdfb1e15fe (diff)
downloadmisc-tests-c16e2637a871527ac0f1c3790f8cf96cb41dfcb0.tar.gz
test_vsyscall: Add intcc32 test case to try int 0xcc from a 32-bit CS
-rw-r--r--test_vsyscall.cc96
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);