Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1317323 > unrolled thread
| Started by | Andy Lutomirski <luto@kernel.org> |
|---|---|
| First post | 2016-01-25 22:40 +0100 |
| Last post | 2016-01-26 21:00 +0100 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v3 0/4] x86: sigcontext fixes, again Andy Lutomirski <luto@kernel.org> - 2016-01-25 22:40 +0100
[PATCH v3 4/4] selftests/x86: Add tests for UC_SIGCONTEXT_SS and UC_STRICT_RESTORE_SS Andy Lutomirski <luto@kernel.org> - 2016-01-25 22:40 +0100
Re: [PATCH v3 0/4] x86: sigcontext fixes, again Cyrill Gorcunov <gorcunov@gmail.com> - 2016-01-26 09:30 +0100
Re: [PATCH v3 0/4] x86: sigcontext fixes, again Cyrill Gorcunov <gorcunov@gmail.com> - 2016-01-26 21:00 +0100
| From | Andy Lutomirski <luto@kernel.org> |
|---|---|
| Date | 2016-01-25 22:40 +0100 |
| Subject | [PATCH v3 0/4] x86: sigcontext fixes, again |
| Message-ID | <qUX1F-1E8-19@gated-at.bofh.it> |
This is take 2 at fixing x86 64-bit signals wrt SS. After a lot of thought, this is not controlled by any flags -- I would much prefer to avoid opt-in behavior. Instead, it just tries hard to avoid triggering the cases that break DOSEMU. Stas, this now seems to pass the test you sent me. It works with stock dosemu2 (I haven't tested classic dosemu because I can't get it to work regardless). It also works with a patched dosemu2 that bypasses the userspace trampoline: https://github.com/amluto/dosemu2/commit/571b4d08dc885b7a133e444a2ad23e0d21366206 With this applied, all of the x86 selftests pass on x86_64. That wasn't the case before -- ldt_gdt_64 was broken. This is a bit risky, and another option would be to do nothing at all. Then we'd disable the problematic self-tests (sigh), and DOSEMU and similar tools will be stuck using gross hacks even on new kernels. Changes from v2: - Rebased (which wasn't quite trivial since the headers were rearranged) - Slightly improved a comment in patch 1. - Tidy up changelogs a bit. Changes from v1: - Comment fixes - Fix screwed up uaccess that broke things Andy Lutomirski (4): x86/signal/64: Add a comment about sigcontext->fs and gs x86/signal/64: Fix SS if needed when delivering a 64-bit signal x86/signal/64: Re-add support for SS in the 64-bit signal context selftests/x86: Add tests for UC_SIGCONTEXT_SS and UC_STRICT_RESTORE_SS arch/x86/include/asm/desc_defs.h | 23 +++ arch/x86/include/asm/sighandling.h | 1 - arch/x86/include/uapi/asm/sigcontext.h | 26 +++- arch/x86/include/uapi/asm/ucontext.h | 43 +++++- arch/x86/kernel/signal.c | 114 ++++++++++++--- tools/testing/selftests/x86/Makefile | 7 +- tools/testing/selftests/x86/sigreturn.c | 240 ++++++++++++++++++++++++++++---- 7 files changed, 394 insertions(+), 60 deletions(-) -- 2.5.0
[toc] | [next] | [standalone]
| From | Andy Lutomirski <luto@kernel.org> |
|---|---|
| Date | 2016-01-25 22:40 +0100 |
| Subject | [PATCH v3 4/4] selftests/x86: Add tests for UC_SIGCONTEXT_SS and UC_STRICT_RESTORE_SS |
| Message-ID | <qUX1G-1E8-31@gated-at.bofh.it> |
| In reply to | #1317323 |
This tests the two ABI-preserving cases that DOSEMU cares about, and
it also explicitly tests the new UC_SIGCONTEXT_SS and
UC_STRICT_RESTORE_SS flags.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
---
tools/testing/selftests/x86/sigreturn.c | 240 ++++++++++++++++++++++++++++----
1 file changed, 212 insertions(+), 28 deletions(-)
diff --git a/tools/testing/selftests/x86/sigreturn.c b/tools/testing/selftests/x86/sigreturn.c
index b5aa1bab7416..43e840470e32 100644
--- a/tools/testing/selftests/x86/sigreturn.c
+++ b/tools/testing/selftests/x86/sigreturn.c
@@ -55,6 +55,47 @@
#include <sys/user.h>
/*
+ * Copied from asm/ucontext.h, as asm/ucontext.h conflicts badly with the glibc
+ * headers.
+ */
+#ifdef __x86_64__
+/*
+ * UC_SAVED_SS will be set when delivering 64-bit or x32 signals on
+ * kernels that save SS in the sigcontext. Kernels that set UC_SAVED_SS
+ * allow signal handlers to set UC_RESTORE_SS; if UC_RESTORE_SS is set,
+ * then sigreturn will restore SS.
+ *
+ * For compatibility with old programs, the kernel will *not* set
+ * UC_RESTORE_SS when delivering signals.
+ */
+#define UC_SIGCONTEXT_SS 0x2
+#define UC_STRICT_RESTORE_SS 0x4
+#endif
+
+/* Access rights as returned by LAR */
+#define AR_TYPE_RODATA (0 * (1 << 9))
+#define AR_TYPE_RWDATA (1 * (1 << 9))
+#define AR_TYPE_RODATA_EXPDOWN (2 * (1 << 9))
+#define AR_TYPE_RWDATA_EXPDOWN (3 * (1 << 9))
+#define AR_TYPE_XOCODE (4 * (1 << 9))
+#define AR_TYPE_XRCODE (5 * (1 << 9))
+#define AR_TYPE_XOCODE_CONF (6 * (1 << 9))
+#define AR_TYPE_XRCODE_CONF (7 * (1 << 9))
+#define AR_TYPE_MASK (7 * (1 << 9))
+
+#define AR_DPL0 (0 * (1 << 13))
+#define AR_DPL3 (3 * (1 << 13))
+#define AR_DPL_MASK (3 * (1 << 13))
+
+#define AR_A (1 << 8) /* A means "accessed" */
+#define AR_S (1 << 12) /* S means "not system" */
+#define AR_P (1 << 15) /* P means "present" */
+#define AR_AVL (1 << 20) /* AVL does nothing */
+#define AR_L (1 << 21) /* L means "long mode" */
+#define AR_DB (1 << 22) /* D or B, depending on type */
+#define AR_G (1 << 23) /* G means "limit in pages" */
+
+/*
* In principle, this test can run on Linux emulation layers (e.g.
* Illumos "LX branded zones"). Solaris-based kernels reserve LDT
* entries 0-5 for their own internal purposes, so start our LDT
@@ -267,6 +308,9 @@ static gregset_t initial_regs, requested_regs, resulting_regs;
/* Instructions for the SIGUSR1 handler. */
static volatile unsigned short sig_cs, sig_ss;
static volatile sig_atomic_t sig_trapped, sig_err, sig_trapno;
+#ifdef __x86_64__
+static volatile sig_atomic_t sig_corrupt_final_ss;
+#endif
/* Abstractions for some 32-bit vs 64-bit differences. */
#ifdef __x86_64__
@@ -305,9 +349,105 @@ static greg_t *csptr(ucontext_t *ctx)
}
#endif
+/*
+ * Checks a given selector for its code bitness or returns -1 if it's not
+ * a usable code segment selector.
+ */
+int cs_bitness(unsigned short cs)
+{
+ uint32_t valid = 0, ar;
+ asm ("lar %[cs], %[ar]\n\t"
+ "jnz 1f\n\t"
+ "mov $1, %[valid]\n\t"
+ "1:"
+ : [ar] "=r" (ar), [valid] "+rm" (valid)
+ : [cs] "r" (cs));
+
+ if (!valid)
+ return -1;
+
+ bool db = (ar & (1 << 22));
+ bool l = (ar & (1 << 21));
+
+ if (!(ar & (1<<11)))
+ return -1; /* Not code. */
+
+ if (l && !db)
+ return 64;
+ else if (!l && db)
+ return 32;
+ else if (!l && !db)
+ return 16;
+ else
+ return -1; /* Unknown bitness. */
+}
+
+/*
+ * Checks a given selector for its code bitness or returns -1 if it's not
+ * a usable code segment selector.
+ */
+bool is_valid_ss(unsigned short cs)
+{
+ uint32_t valid = 0, ar;
+ asm ("lar %[cs], %[ar]\n\t"
+ "jnz 1f\n\t"
+ "mov $1, %[valid]\n\t"
+ "1:"
+ : [ar] "=r" (ar), [valid] "+rm" (valid)
+ : [cs] "r" (cs));
+
+ if (!valid)
+ return false;
+
+ if ((ar & AR_TYPE_MASK) != AR_TYPE_RWDATA &&
+ (ar & AR_TYPE_MASK) != AR_TYPE_RWDATA_EXPDOWN)
+ return false;
+
+ return (ar & AR_P);
+}
+
/* Number of errors in the current test case. */
static volatile sig_atomic_t nerrs;
+static void validate_signal_ss(int sig, ucontext_t *ctx)
+{
+#ifdef __x86_64__
+ bool was_64bit = (cs_bitness(*csptr(ctx)) == 64);
+
+ if (!(ctx->uc_flags & UC_SIGCONTEXT_SS)) {
+ printf("[FAIL]\tUC_SIGCONTEXT_SS was not set\n");
+ nerrs++;
+
+ /*
+ * This happens on Linux 4.1. The rest will fail, too, so
+ * return now to reduce the noise.
+ */
+ return;
+ }
+
+ /* UC_STRICT_RESTORE_SS is set iff we came from 64-bit mode. */
+ if (!!(ctx->uc_flags & UC_STRICT_RESTORE_SS) != was_64bit) {
+ printf("[FAIL]\tUC_STRICT_RESTORE_SS was wrong in signal %d\n",
+ sig);
+ nerrs++;
+ }
+
+ if (is_valid_ss(*ssptr(ctx))) {
+ /*
+ * DOSEMU was written before 64-bit sigcontext had SS, and
+ * it tries to figure out the signal source SS by looking at
+ * the physical register. Make sure that keeps working.
+ */
+ unsigned short hw_ss;
+ asm ("mov %%ss, %0" : "=rm" (hw_ss));
+ if (hw_ss != *ssptr(ctx)) {
+ printf("[FAIL]\tHW SS didn't match saved SS\n");
+ nerrs++;
+ }
+ }
+#endif
+}
+
/*
* SIGUSR1 handler. Sets CS and SS as requested and points IP to the
* int3 trampoline. Sets SP to a large known value so that we can see
@@ -317,6 +457,8 @@ static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
+ validate_signal_ss(sig, ctx);
+
memcpy(&initial_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
*csptr(ctx) = sig_cs;
@@ -334,13 +476,16 @@ static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
}
/*
- * Called after a successful sigreturn. Restores our state so that
- * the original raise(SIGUSR1) returns.
+ * Called after a successful sigreturn (via int3) or from a failed
+ * sigreturn (directly by kernel). Restores our state so that the
+ * original raise(SIGUSR1) returns.
*/
static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
+ validate_signal_ss(sig, ctx);
+
sig_err = ctx->uc_mcontext.gregs[REG_ERR];
sig_trapno = ctx->uc_mcontext.gregs[REG_TRAPNO];
@@ -358,41 +503,62 @@ static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
memcpy(&resulting_regs, &ctx->uc_mcontext.gregs, sizeof(gregset_t));
memcpy(&ctx->uc_mcontext.gregs, &initial_regs, sizeof(gregset_t));
+#ifdef __x86_64__
+ if (sig_corrupt_final_ss) {
+ if (ctx->uc_flags & UC_STRICT_RESTORE_SS) {
+ printf("[FAIL]\tUC_STRICT_RESTORE_SS was set inappropriately\n");
+ nerrs++;
+ } else {
+ /*
+ * DOSEMU transitions from 32-bit to 64-bit mode by
+ * adjusting sigcontext, and it requires that this work
+ * even if the saved SS is bogus.
+ */
+ printf("\tCorrupting SS on return to 64-bit mode\n");
+ *ssptr(ctx) = 0;
+ }
+ }
+#endif
+
sig_trapped = sig;
}
-/*
- * Checks a given selector for its code bitness or returns -1 if it's not
- * a usable code segment selector.
- */
-int cs_bitness(unsigned short cs)
+#ifdef __x86_64__
+/* Tests recovery if !UC_STRICT_RESTORE_SS */
+static void sigusr2(int sig, siginfo_t *info, void *ctx_void)
{
- uint32_t valid = 0, ar;
- asm ("lar %[cs], %[ar]\n\t"
- "jnz 1f\n\t"
- "mov $1, %[valid]\n\t"
- "1:"
- : [ar] "=r" (ar), [valid] "+rm" (valid)
- : [cs] "r" (cs));
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
- if (!valid)
- return -1;
+ if (!(ctx->uc_flags & UC_STRICT_RESTORE_SS)) {
+ printf("[FAIL]\traise(2) didn't set UC_STRICT_RESTORE_SS\n");
+ nerrs++;
+ return; /* We can't do the rest. */
+ }
- bool db = (ar & (1 << 22));
- bool l = (ar & (1 << 21));
+ ctx->uc_flags &= ~UC_STRICT_RESTORE_SS;
+ *ssptr(ctx) = 0;
- if (!(ar & (1<<11)))
- return -1; /* Not code. */
+ /* Return. The kernel should recover without sending another signal. */
+}
- if (l && !db)
- return 64;
- else if (!l && db)
- return 32;
- else if (!l && !db)
- return 16;
- else
- return -1; /* Unknown bitness. */
+static int test_nonstrict_ss(void)
+{
+ clearhandler(SIGUSR1);
+ clearhandler(SIGTRAP);
+ clearhandler(SIGSEGV);
+ clearhandler(SIGILL);
+ sethandler(SIGUSR2, sigusr2, 0);
+
+ nerrs = 0;
+
+ printf("[RUN]\tClear UC_STRICT_RESTORE_SS and corrupt SS\n");
+ raise(SIGUSR2);
+ if (!nerrs)
+ printf("[OK]\tIt worked\n");
+
+ return nerrs;
}
+#endif
/* Finds a usable code segment of the requested bitness. */
int find_cs(int bitness)
@@ -576,6 +742,12 @@ static int test_bad_iret(int cs_bits, unsigned short ss, int force_cs)
errdesc, strsignal(sig_trapped));
return 0;
} else {
+ /*
+ * This also implicitly tests UC_STRICT_RESTORE_SS:
+ * We check that these signals set UC_STRICT_RESTORE_SS and,
+ * if UC_STRICT_RESTORE_SS doesn't cause strict behavior,
+ * then we won't get SIGSEGV.
+ */
printf("[FAIL]\tDid not get SIGSEGV\n");
return 1;
}
@@ -632,6 +804,14 @@ int main()
GDT3(gdt_data16_idx));
}
+#ifdef __x86_64__
+ /* Nasty ABI case: check SS corruption handling. */
+ sig_corrupt_final_ss = 1;
+ total_nerrs += test_valid_sigreturn(32, false, -1);
+ total_nerrs += test_valid_sigreturn(32, true, -1);
+ sig_corrupt_final_ss = 0;
+#endif
+
/*
* We're done testing valid sigreturn cases. Now we test states
* for which sigreturn itself will succeed but the subsequent
@@ -680,5 +860,9 @@ int main()
if (gdt_npdata32_idx)
test_bad_iret(32, GDT3(gdt_npdata32_idx), -1);
+#ifdef __x86_64__
+ total_nerrs += test_nonstrict_ss();
+#endif
+
return total_nerrs ? 1 : 0;
}
--
2.5.0
[toc] | [prev] | [next] | [standalone]
| From | Cyrill Gorcunov <gorcunov@gmail.com> |
|---|---|
| Date | 2016-01-26 09:30 +0100 |
| Message-ID | <qV7aH-1qe-39@gated-at.bofh.it> |
| In reply to | #1317323 |
On Mon, Jan 25, 2016 at 01:34:11PM -0800, Andy Lutomirski wrote: > This is take 2 at fixing x86 64-bit signals wrt SS. After a lot of > thought, this is not controlled by any flags -- I would much prefer > to avoid opt-in behavior. Instead, it just tries hard to avoid > triggering the cases that break DOSEMU. > > Stas, this now seems to pass the test you sent me. It works with > stock dosemu2 (I haven't tested classic dosemu because I can't get it > to work regardless). It also works with a patched dosemu2 that bypasses > the userspace trampoline: > > https://github.com/amluto/dosemu2/commit/571b4d08dc885b7a133e444a2ad23e0d21366206 > > With this applied, all of the x86 selftests pass on x86_64. That > wasn't the case before -- ldt_gdt_64 was broken. I've been testing this series already. I guess ;) Anyway, gonna try it one more shot at the evening, just to make sure. From a glance everything looks just great!
[toc] | [prev] | [next] | [standalone]
| From | Cyrill Gorcunov <gorcunov@gmail.com> |
|---|---|
| Date | 2016-01-26 21:00 +0100 |
| Message-ID | <qVhWq-uq-5@gated-at.bofh.it> |
| In reply to | #1317628 |
On Tue, Jan 26, 2016 at 11:22:43AM +0300, Cyrill Gorcunov wrote: > > > > With this applied, all of the x86 selftests pass on x86_64. That > > wasn't the case before -- ldt_gdt_64 was broken. > > I've been testing this series already. I guess ;) Anyway, gonna try > it one more shot at the evening, just to make sure. From a glance > everything looks just great! FWIW, I built latest vanilla repo with the series applied, and it works fine for current criu. ... | Test: zdtm/live/static/maps00, Result: PASS | ZDTM tests PASS. ... So great for me Tested-by: Cyrill Gorcunov <gorcunov@openvz.org>
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web