Skip to contentSkip to conversation

x86_64,signal: Fix the set of saved segment registers

PATCH In review Andy Lutomirski 4 participants 16 messages Jul 11, 2014 – Mar 9, 2015 x86

lore
Share & tools
lore
#0 lore raw

cover x86_64,signal: Fix the set of saved segment registers

The x86_64 signal code claims to save and restore CS, FS, and GS, and it further claims that this is the minimal set that's needed.

Neither of these claims is true. The code does not, and AFAICT never has, saved or restored FS and GS, nor does it need to. On the other hand, all 64-bit syscalls (and rt_sigreturn in particular) clobber SS, making it impossible for signal handlers to correctly restore SS without using a trampoline or ptracing themselves.

This patchset saves and restores SS in the __pad0 slot and renames the FS and GS slots __pad1 and __pad2 to more accurately document their purpose.

I discovered this while writing a test for espfix64.

Andy Lutomirski (2):
x86_64,signal: Save and restore SS in signal frames x86_64,signal: Remove 'fs' and 'gs' from sigcontext

 arch/x86/include/asm/sigcontext.h      |  6 +++---
 arch/x86/include/uapi/asm/sigcontext.h |  6 +++---
 arch/x86/kernel/signal.c               | 12 +++---------
 3 files changed, 9 insertions(+), 15 deletions(-)
Signature · 1 line
1.9.3
#1 lore raw

PATCH 1/2 x86_64,signal: Save and restore SS in signal frames

The comment in the signal code says that apps can save/restore other segments on their own. It's true that apps can *save* SS on their own, but there's no way for apps to restore it: SYSCALL effectively resets SS to __USER_DS, so any value that user code tries to load into SS gets lost on entry to sigreturn.

This recycles two padding bytes in the segment selector area for SS.

I suspect that 64-bit programs that try to run 16-bit code and uses signals will have a lot of trouble without this.

---
 arch/x86/include/asm/sigcontext.h      | 2 +-
 arch/x86/include/uapi/asm/sigcontext.h | 2 +-
 arch/x86/kernel/signal.c               | 8 +-------
 3 files changed, 3 insertions(+), 9 deletions(-)
arch/x86/include/asm/sigcontext.h +1 -1
index 9dfce4e..f910cdc 100644
@@ -59,7 +59,7 @@ struct sigcontext {
 	unsigned short cs;
 	unsigned short gs;
 	unsigned short fs;
-	unsigned short __pad0;
+	unsigned short ss;
 	unsigned long err;
 	unsigned long trapno;
 	unsigned long oldmask;
arch/x86/include/uapi/asm/sigcontext.h +1 -1
index d8b9f90..076b11f 100644
@@ -179,7 +179,7 @@ struct sigcontext {
 	__u16 cs;
 	__u16 gs;
 	__u16 fs;
-	__u16 __pad0;
+	__u16 ss;
 	__u64 err;
 	__u64 trapno;
 	__u64 oldmask;
arch/x86/kernel/signal.c +1 -7
index 2851d63..08d7a9e 100644
@@ -94,15 +94,8 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
 		COPY(r15);
 #endif /* CONFIG_X86_64 */
 
-#ifdef CONFIG_X86_32
 		COPY_SEG_CPL3(cs);
 		COPY_SEG_CPL3(ss);
-#else /* !CONFIG_X86_32 */
-		/* Kernel saves and restores only the CS segment register on signals,
-		 * which is the bare minimum needed to allow mixed 32/64-bit code.
-		 * App's signal handler can save/restore other segments if needed. */
-		COPY_SEG_CPL3(cs);
-#endif /* CONFIG_X86_32 */
 
 		get_user_ex(tmpflags, &sc->flags);
 		regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
@@ -164,6 +157,7 @@ int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
 		put_user_ex(regs->cs, &sc->cs);
 		put_user_ex(0, &sc->gs);
 		put_user_ex(0, &sc->fs);
+		put_user_ex(regs->ss, &sc->ss);
 #endif /* CONFIG_X86_32 */
 
 		put_user_ex(fpstate, &sc->fpstate);
Signature · 1 line
1.9.3
#2 lore raw

Re: [PATCH 1/2] x86_64,signal: Save and restore SS in signal frames

Hi Ingo,

Here's a blast from the past. I asked Andi today, and he seems conceptually okay with this. Could you consider applying it? Aside from being a bug fix IMO, it will allow my sigreturn test to work reasonably well as a native 64-bit program.

I checked: it still applies cleanly.

--Andy

Quoted 75 lines · Andy Lutomirski

The comment in the signal code says that apps can save/restore other segments on their own. It's true that apps can *save* SS on their own, but there's no way for apps to restore it: SYSCALL effectively resets SS to __USER_DS, so any value that user code tries to load into SS gets lost on entry to sigreturn.

This recycles two padding bytes in the segment selector area for SS.

I suspect that 64-bit programs that try to run 16-bit code and uses signals will have a lot of trouble without this.

---
 arch/x86/include/asm/sigcontext.h      | 2 +-
 arch/x86/include/uapi/asm/sigcontext.h | 2 +-
 arch/x86/kernel/signal.c               | 8 +-------
 3 files changed, 3 insertions(+), 9 deletions(-)
arch/x86/include/asm/sigcontext.h +1 -1
index 9dfce4e..f910cdc 100644
@@ -59,7 +59,7 @@ struct sigcontext {
        unsigned short cs;
        unsigned short gs;
        unsigned short fs;
-       unsigned short __pad0;
+       unsigned short ss;
        unsigned long err;
        unsigned long trapno;
        unsigned long oldmask;
arch/x86/include/uapi/asm/sigcontext.h +1 -1
index d8b9f90..076b11f 100644
@@ -179,7 +179,7 @@ struct sigcontext {
        __u16 cs;
        __u16 gs;
        __u16 fs;
-       __u16 __pad0;
+       __u16 ss;
        __u64 err;
        __u64 trapno;
        __u64 oldmask;
arch/x86/kernel/signal.c +1 -8
index 2851d63..08d7a9e 100644
@@ -94,15 +94,8 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
                COPY(r15);
 #endif /* CONFIG_X86_64 */
 
-#ifdef CONFIG_X86_32
                COPY_SEG_CPL3(cs);
                COPY_SEG_CPL3(ss);
-#else /* !CONFIG_X86_32 */
-               /* Kernel saves and restores only the CS segment register on signals,
-                * which is the bare minimum needed to allow mixed 32/64-bit code.
-                * App's signal handler can save/restore other segments if needed. */
-               COPY_SEG_CPL3(cs);
-#endif /* CONFIG_X86_32 */
 
                get_user_ex(tmpflags, &sc->flags);
                regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS);
@@ -164,6 +157,7 @@ int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
                put_user_ex(regs->cs, &sc->cs);
                put_user_ex(0, &sc->gs);
                put_user_ex(0, &sc->fs);
+               put_user_ex(regs->ss, &sc->ss);
 #endif /* CONFIG_X86_32 */
 
                put_user_ex(fpstate, &sc->fpstate);
--

1.9.3

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#3 lore raw

PATCH 2/2 x86_64,signal: Remove 'fs' and 'gs' from sigcontext

In reply to Andy Lutomirski

As far as I can tell, these fields have been set to zero on save and ignored on restore since Linux was imported into git. Rename them '__pad1' and '__pad2' to avoid confusion and to allow them to be recycled some day.

I'm intentionally avoiding calling either of them __pad0: the field formerly known as __pad0 is now ss.

---
 arch/x86/include/asm/sigcontext.h      | 4 ++--
 arch/x86/include/uapi/asm/sigcontext.h | 4 ++--
 arch/x86/kernel/signal.c               | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)
arch/x86/include/asm/sigcontext.h +2 -2
index f910cdc..5f0ef11 100644
@@ -57,8 +57,8 @@ struct sigcontext {
 	unsigned long ip;
 	unsigned long flags;
 	unsigned short cs;
-	unsigned short gs;
-	unsigned short fs;
+	unsigned short __pad2;	/* Was called gs, but was always zero. */
+	unsigned short __pad1;	/* Was called gs, but was always zero. */
 	unsigned short ss;
 	unsigned long err;
 	unsigned long trapno;
arch/x86/include/uapi/asm/sigcontext.h +2 -2
index 076b11f..df9908b 100644
@@ -177,8 +177,8 @@ struct sigcontext {
 	__u64 rip;
 	__u64 eflags;		/* RFLAGS */
 	__u16 cs;
-	__u16 gs;
-	__u16 fs;
+	__u16 __pad2;		/* Was called gs, but was always zero. */
+	__u16 __pad1;		/* Was called fs, but was always zero. */
 	__u16 ss;
 	__u64 err;
 	__u64 trapno;
arch/x86/kernel/signal.c +2 -2
index 08d7a9e..a1867ce 100644
@@ -155,8 +155,8 @@ int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
 #else /* !CONFIG_X86_32 */
 		put_user_ex(regs->flags, &sc->flags);
 		put_user_ex(regs->cs, &sc->cs);
-		put_user_ex(0, &sc->gs);
-		put_user_ex(0, &sc->fs);
+		put_user_ex(0, &sc->__pad2);
+		put_user_ex(0, &sc->__pad1);
 		put_user_ex(regs->ss, &sc->ss);
 #endif /* CONFIG_X86_32 */
 
Signature · 1 line
1.9.3
#4 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

arch/x86/include/uapi/asm/sigcontext.h

Quoted 4 lines
arch/x86/include/uapi/asm/sigcontext.h +0 -0
index 076b11f..df9908b 100644

I don't think renaming fields in uapi/asm is acceptable. These are likely used by user programs and you'll break compiles.

-Andi

Quoted 11 lines
arch/x86/include/uapi/asm/sigcontext.h +2 -2
@@ -177,8 +177,8 @@ struct sigcontext {
 	__u64 rip;
 	__u64 eflags;		/* RFLAGS */
 	__u16 cs;
-	__u16 gs;
-	__u16 fs;
+	__u16 __pad2;		/* Was called gs, but was always zero. */
+	__u16 __pad1;		/* Was called fs, but was always zero. */
 	__u16 ss;
 	__u64 err;
 	__u64 trapno;
Signature · 1 line
[email protected] -- Speaking for myself only.
#5 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Quoted 7 lines · Andi Kleen

arch/x86/include/uapi/asm/sigcontext.h

Quoted 4 lines
arch/x86/include/uapi/asm/sigcontext.h +0 -0
index 076b11f..df9908b 100644

I don't think renaming fields in uapi/asm is acceptable. These are likely used by user programs and you'll break compiles.

Hmm. That's a fair point. On the other hand, any user code that uses these fields explicitly may already be broken, since the current names of these fields rather strongly imply that they do something.

Is there any clear policy on minor API breaks in the UAPI headers that don't affect ABI?

--Andy

#6 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Quoted 9 lines · Andy Lutomirski
Quoted 7 lines · Andi Kleen

arch/x86/include/uapi/asm/sigcontext.h

Quoted 4 lines
arch/x86/include/uapi/asm/sigcontext.h +0 -0
index 076b11f..df9908b 100644

I don't think renaming fields in uapi/asm is acceptable. These are likely used by user programs and you'll break compiles.

Quoted 7 lines

Hmm. That's a fair point. On the other hand, any user code that uses these fields explicitly may already be broken, since the current names of these fields rather strongly imply that they do something.

Is there any clear policy on minor API breaks in the UAPI headers that don't affect ABI?

There really isn't, and this *definitely* a boundary case: as you state, it is very likely that anyone currently using them are doing so incorrectly, but it does induce potential source-level breakage.

Linus, do you have any guidance here?

-hpa

#7 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

In reply to Andy Lutomirski
Quoted 5 lines · Andy Lutomirski
arch/x86/include/uapi/asm/sigcontext.h +0 -0
index 076b11f..df9908b 100644

arch/x86/include/uapi/asm/sigcontext.h struct sigcontext { · line 184

Quoted 10 lines
arch/x86/include/uapi/asm/sigcontext.h +2 -2
@@ -177,8 +177,8 @@ struct sigcontext {
 	__u64 rip;
 	__u64 eflags;		/* RFLAGS */
 	__u16 cs;
-	__u16 gs;
-	__u16 fs;
+	__u16 __pad2;		/* Was called gs, but was always zero. */
+	__u16 __pad1;		/* Was called fs, but was always zero. */
 	__u16 ss;
 	__u64 err;
 	__u64 trapno;

I'm just wondering if this is likely to cause compile error in existing code. I guess worst case we can just revert this patch...

-hpa

#8 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

In reply to Andy Lutomirski
Quoted 4 lines · Andy Lutomirski

As far as I can tell, these fields have been set to zero on save and ignored on restore since Linux was imported into git. Rename them '__pad1' and '__pad2' to avoid confusion and to allow them to be recycled some day.

Shouldn't we actually try to save/restore gs/fs properly? Admittedly, changing them in the signal handler would be insane, but still.. See our context switching code with the whole segment selector vs base save/restore code. Hmm?

Linus

#9 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Our current handling of fs/vs is really weird. The best may very well be to explicitly save and restore both fs and he as part of Andi's patchset to support wrxsbase.

Quoted 13 lines · Linus Torvalds
Quoted 4 lines · Andy Lutomirski

As far as I can tell, these fields have been set to zero on save and ignored on restore since Linux was imported into git. Rename them '__pad1' and '__pad2' to avoid confusion and to allow them to be recycled some day.

Shouldn't we actually try to save/restore gs/fs properly? Admittedly, changing them in the signal handler would be insane, but still.. See our context switching code with the whole segment selector vs base save/restore code. Hmm?

Linus

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#10 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

In reply to Linus Torvalds
Quoted 11 lines · Linus Torvalds
Quoted 4 lines · Andy Lutomirski

As far as I can tell, these fields have been set to zero on save and ignored on restore since Linux was imported into git. Rename them '__pad1' and '__pad2' to avoid confusion and to allow them to be recycled some day.

Shouldn't we actually try to save/restore gs/fs properly? Admittedly, changing them in the signal handler would be insane, but still.. See our context switching code with the whole segment selector vs base save/restore code. Hmm?

This seems like it's asking for trouble. I think wxe'd have to separately save the selectors and the base registers to avoid breaking something, especially once wrgsbase, etc are enabled.

Why would this be needed anyway?

Does anyone implement makecontext, etc using raise/sigreturn? If so, they might be in for a surprise when their gs starts getting saved, too.

Linus, for context, the other patch in this series saves and restores SS. Without that, 64-bit sigreturn to a nondefault stack segment is basically impossible. But I don't see why any other segments (besides CS) are needed for 64-bit sigreturn.

--Andy

Quoted 2 lines

Linus

#11 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Quoted 3 lines

This seems like it's asking for trouble. I think wxe'd have to separately save the selectors and the base registers to avoid breaking something, especially once wrgsbase, etc are enabled.

I agree, it would likely break existing code.

Quoted 5 lines

Linus, for context, the other patch in this series saves and restores SS.

Without that, 64-bit sigreturn to a nondefault stack segment is basically impossible.

Why would you ever want to do that?

-Andi

#12 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Because you are doing something weird (like Pin, for example) and take an asynchronous fault?

Quoted 16 lines · Andi Kleen
Quoted 2 lines

This seems like it's asking for trouble. I think wxe'd have to separately save the selectors and the base registers to avoid

breaking

Quoted 1 line

something, especially once wrgsbase, etc are enabled.

I agree, it would likely break existing code.

Quoted 5 lines

Linus, for context, the other patch in this series saves and restores SS.

Without that, 64-bit sigreturn to a nondefault stack segment is basically impossible.

Why would you ever want to do that?

-Andi

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#13 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Quoted 1 line · H. Peter Anvin

Because you are doing something weird (like Pin, for example) and take an asynchronous fault?

But even for pin that would need executing 16 bit code, or really weird 32bit code. AFAIK for 32bit the only good use case was NX emulation (and old virtualization) which are both completely obsolete.

I don't think it's worth messing with the signal handlers for 16bit code. If there's any problem with saving/restoring state that emulator can always handle it by itself.

-Andi

#14 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Quoted 6 lines · Andi Kleen
Quoted 1 line · H. Peter Anvin

Because you are doing something weird (like Pin, for example) and take an asynchronous fault?

But even for pin that would need executing 16 bit code, or really weird 32bit code. AFAIK for 32bit the only good use case was NX emulation (and old virtualization) which are both completely obsolete.

Nothing particularly weird is needed. Set a non-default stack segment (e.g. any 16-bit ss) and take *any* fault. This could be something asynchronous or even a normal synchronous fault. Return from the signal handler: boom.

We know that people use 16-bit stack segments: it's what prompted the whole espfix64 thing.

Quoted 5 lines

I don't think it's worth messing with the signal handlers for 16bit code. If there's any problem with saving/restoring state that emulator can always handle it by itself.

How?

I can think of at least two vaguely feasible ways. The program could ptrace itself, trap sigreturn, and fix ss. Or it could restore registers itself and return using far ret or iret. Both suck.

--Andy

#15 lore raw

Re: [PATCH 2/2] x86_64,signal: Remove 'fs' and 'gs' from sigcontext

Quoted 22 lines · Andy Lutomirski
Quoted 6 lines · Andi Kleen
Quoted 1 line · H. Peter Anvin

Because you are doing something weird (like Pin, for example) and take an asynchronous fault?

But even for pin that would need executing 16 bit code, or really weird 32bit code. AFAIK for 32bit the only good use case was NX emulation (and old virtualization) which are both completely obsolete.

Nothing particularly weird is needed. Set a non-default stack segment (e.g. any 16-bit ss) and take *any* fault. This could be something asynchronous or even a normal synchronous fault. Return from the signal handler: boom.

We know that people use 16-bit stack segments: it's what prompted the whole espfix64 thing.

Quoted 5 lines

I don't think it's worth messing with the signal handlers for 16bit code. If there's any problem with saving/restoring state that emulator can always handle it by itself.

Quoted 5 lines

How?

I can think of at least two vaguely feasible ways. The program could ptrace itself, trap sigreturn, and fix ss. Or it could restore registers itself and return using far ret or iret. Both suck.

You can't even hack around this with ptrace -- ptrace silently fails to write ss for non-TIF_IA32 tasks.

--Andy