@@ -35,7 +35,11 @@
#define X86_EFLAGS_AF 0x00000010
#define X86_EFLAGS_ZF 0x00000040
#define X86_EFLAGS_SF 0x00000080
+#define X86_EFLAGS_TF 0x00000100
+#define X86_EFLAGS_IF 0x00000200
+#define X86_EFLAGS_DF 0x00000400
#define X86_EFLAGS_OF 0x00000800
+#define X86_EFLAGS_NT 0x00004000
#define X86_EFLAGS_AC 0x00040000
#define X86_IA32_EFER 0xc0000080
@@ -20,9 +20,89 @@ static void test_syscall_lazy_load(void)
report("MSR_*STAR eager loading", true);
}
+/*
+ * test handling of TF in syscall/sysret: #DB is raised if TF
+ * is 1 at the *end* of syscall/sysret.
+ *
+ * This uses 32-bit syscall/sysret because KVM emulates it on Intel processors.
+ * However, the same bug happens with 64-bit syscall/sysret if two vCPUs
+ * "race" to force the emulation of syscall/sysret.
+ */
+
+static uint16_t code_segment_upon_db;
+static void handle_db(struct ex_regs *regs)
+{
+ code_segment_upon_db = regs->cs;
+ regs->rflags &= ~(1 << 8);
+}
+
+/* expects desired ring 3 flags in rax */
+asm("syscall32_target:\n"
+ " cmp $0, code_segment_upon_db(%rip)\n"
+ " jne back_to_test\n"
+ " mov %eax,%r11d\n"
+ " sysretl\n");
+
+/* 32-bit, ring-3 part of test_syscall_tf */
+asm(" .code32\n"
+ "syscall_tf_user32:\n"
+ " pushf\n"
+ " pop %eax\n"
+ " or $(1<<8),%eax\n"
+ " push %eax\n"
+ " popf\n"
+ " syscall\n" /* singlestep trap taken after syscall */
+ " syscall\n" /* jumps back to test_syscall_tf's body */
+ " .code64\n");
+
+static void test_syscall_tf(void)
+{
+ extern void syscall32_target();
+ extern void syscall_tf_user32();
+ ulong rcx;
+
+ wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
+ wrmsr(MSR_CSTAR, (ulong)syscall32_target);
+ wrmsr(MSR_STAR, ((uint64_t)USER_CS32 << 48) | ((uint64_t)KERNEL_CS64 << 32));
+ wrmsr(MSR_SYSCALL_MASK, X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_NT);
+ handle_exception(DB_VECTOR, handle_db);
+
+ /* good:
+ * sysret to syscall_tf_user32
+ * popf sets TF (singlestep starts on the next instruction)
+ * syscall to syscall32_target -> TF cleared and no singlestep
+ * sysretl sets TF
+ * handle_db sets code_segment_upon_db to USER_CS32 and clears TF
+ * syscall to syscall32_target
+ * syscall32_target jumps to back_to_test
+ *
+ * bad:
+ * sysret to syscall_tf_user32
+ * popf sets TF (singlestep starts on the next instruction)
+ * syscall to syscall32_target, TF cleared and wrong singlestep exception
+ * handle_db sets code_segment_upon_db to KERNEL_CS64
+ * syscall32_target jumps to back_to_test
+ */
+ rcx = (ulong)syscall_tf_user32;
+ asm volatile(" push %%rbp\n"
+ " pushf; pop %%rax\n" // expected by syscall32_target
+ " sysret\n"
+ "back_to_test:\n"
+ " pop %%rbp"
+ : "+c"(rcx) :
+ : "rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15");
+ if (code_segment_upon_db != USER_CS32) {
+ printf("wrong CS (%#04x)!\n", code_segment_upon_db);
+ }
+ report("syscall TF handling", code_segment_upon_db == USER_CS32);
+}
+
int main(int ac, char **av)
{
+ setup_idt();
test_syscall_lazy_load();
+ test_syscall_tf();
return report_summary();
}
@@ -153,6 +153,7 @@ file = sieve.flat
[syscall]
file = syscall.flat
+extra_params = -cpu Opteron_G1,vendor=AuthenticAMD
[tsc]
file = tsc.flat