All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] um: Fix stack pointer alignment
@ 2021-04-19 15:32 YiFei Zhu
  2021-04-19 19:41 ` Johannes Berg
  0 siblings, 1 reply; 7+ messages in thread
From: YiFei Zhu @ 2021-04-19 15:32 UTC (permalink / raw)
  To: linux-um
  Cc: Jeff Dike, Richard Weinberger, Anton Ivanov, Johannes Berg,
	Benjamin Berg, YiFei Zhu

GCC assumes that stack is aligned to 16-byte on call sites [1].
Since GCC 8, GCC began using 16-byte aligned SSE instructions to
implement assignments to structs on stack. This affects
os-Linux/sigio.c, write_sigio_thread:

  struct pollfds *fds, tmp;
  tmp = current_poll;

Note that struct pollfds is exactly 16 bytes in size.
GCC 8+ generates assembly similar to:

  movdqa (%rdi),%xmm0
  movaps %xmm0,-0x50(%rbp)

This is an issue, because movaps will #GP if -0x50(%rbp) is not
aligned to 16 bytes [2], and how rbp gets assigned to is via glibc
clone thread_start, then function prologue, going though execution
trace similar to (showing only relevant instructions):

  sub    $0x10,%rsi
  mov    %rcx,0x8(%rsi)
  mov    %rdi,(%rsi)
  syscall
  pop    %rax
  pop    %rdi
  callq  *%rax
  push   %rbp
  mov    %rsp,%rbp

The stack pointer always points to the topmost element on stack,
rather then the space right above the topmost. On push, the
pointer decrements first before writing to the memory pointed to
by it. Therefore, there is no need to have the stack pointer
pointer always point to valid memory unless the stack is poped;
so the `- sizeof(void *)` in the code is unnecessary.

On the other hand, glibc reserves the 16 bytes it needs on stack
and pops itself, so by the call instruction the stack pointer
is exactly the caller-supplied sp. It then push the 16 bytes of
the return address and the saved stack pointer, so the base
pointer will be 16-byte aligned if and only if the caller
supplied sp is 16-byte aligned. Therefore, the caller must supply
a 16-byte aligned pointer, which `stack + UM_KERN_PAGE_SIZE`
already satisfies.

On a side note, musl is unaffected by this issue because it forces
16 byte alignment via `and $-16,%rsi` in its clone wrapper.
Similarly, glibc i386 is also uneffected because it has
`andl $0xfffffff0, %ecx`.

To reproduce this bug, enable CONFIG_UML_RTC. uml_rtc will call
add_sigio_fd which will then cause write_sigio_thread to go
into segfault loop.

Similarly, signal stacks will be aligned by the host kernel upon
signal delivery. `- sizeof(void *)` to sigaltstack is
unconventional and extraneous.

On a related note, initialization of longjmp buffers do require
`- sizeof(void *)`. This is to account for the return address
that would have been pushed to the stack at the call site.

The reason for uml to respect 16-byte alignment, rather than
telling GCC to assume 8-byte alignment like the host kernel since
commit d9b0cde91c60 ("x86-64, gcc: Use
-mpreferred-stack-boundary=3 if supported"), is because uml links
against libc. There is no reason to assume libc is also compiled
with that flag and assumes 8-byte alignment rather than 16-byte.

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838
[2] https://c9x.me/x86/html/file_module_x86_id_180.html

Signed-off-by: YiFei Zhu <zhuyifei1999@gmail.com>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
---
 arch/um/drivers/ubd_kern.c      | 3 +--
 arch/um/kernel/skas/clone.c     | 2 +-
 arch/um/os-Linux/helper.c       | 4 ++--
 arch/um/os-Linux/signal.c       | 2 +-
 arch/um/os-Linux/skas/process.c | 2 +-
 5 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
index 8e0b43cf089f..cbd4f00fe77e 100644
--- a/arch/um/drivers/ubd_kern.c
+++ b/arch/um/drivers/ubd_kern.c
@@ -1242,8 +1242,7 @@ static int __init ubd_driver_init(void){
 		 * enough. So use anyway the io thread. */
 	}
 	stack = alloc_stack(0, 0);
-	io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *),
-				 &thread_fd);
+	io_pid = start_io_thread(stack + PAGE_SIZE, &thread_fd);
 	if(io_pid < 0){
 		printk(KERN_ERR
 		       "ubd : Failed to start I/O thread (errno = %d) - "
diff --git a/arch/um/kernel/skas/clone.c b/arch/um/kernel/skas/clone.c
index 592cdb138441..5afac0fef24e 100644
--- a/arch/um/kernel/skas/clone.c
+++ b/arch/um/kernel/skas/clone.c
@@ -29,7 +29,7 @@ stub_clone_handler(void)
 	long err;
 
 	err = stub_syscall2(__NR_clone, CLONE_PARENT | CLONE_FILES | SIGCHLD,
-			    (unsigned long)data + UM_KERN_PAGE_SIZE / 2 - sizeof(void *));
+			    (unsigned long)data + UM_KERN_PAGE_SIZE / 2);
 	if (err) {
 		data->parent_err = err;
 		goto done;
diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c
index 9fa6e4187d4f..32e88baf18dd 100644
--- a/arch/um/os-Linux/helper.c
+++ b/arch/um/os-Linux/helper.c
@@ -64,7 +64,7 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
 		goto out_close;
 	}
 
-	sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
+	sp = stack + UM_KERN_PAGE_SIZE;
 	data.pre_exec = pre_exec;
 	data.pre_data = pre_data;
 	data.argv = argv;
@@ -120,7 +120,7 @@ int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
 	if (stack == 0)
 		return -ENOMEM;
 
-	sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
+	sp = stack + UM_KERN_PAGE_SIZE;
 	pid = clone(proc, (void *) sp, flags, arg);
 	if (pid < 0) {
 		err = -errno;
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index 96f511d1aabe..e283f130aadc 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -129,7 +129,7 @@ void set_sigstack(void *sig_stack, int size)
 	stack_t stack = {
 		.ss_flags = 0,
 		.ss_sp = sig_stack,
-		.ss_size = size - sizeof(void *)
+		.ss_size = size
 	};
 
 	if (sigaltstack(&stack, NULL) != 0)
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index fba674fac8b7..43d6ec2a4bea 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -327,7 +327,7 @@ int start_userspace(unsigned long stub_stack)
 	}
 
 	/* set stack pointer to the end of the stack page, so it can grow downwards */
-	sp = (unsigned long) stack + UM_KERN_PAGE_SIZE - sizeof(void *);
+	sp = (unsigned long)stack + UM_KERN_PAGE_SIZE;
 
 	flags = CLONE_FILES | SIGCHLD;
 
-- 
2.31.1


_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] um: Fix stack pointer alignment
  2021-04-19 15:32 [PATCH v2] um: Fix stack pointer alignment YiFei Zhu
@ 2021-04-19 19:41 ` Johannes Berg
  2021-04-19 20:36   ` YiFei Zhu
  0 siblings, 1 reply; 7+ messages in thread
From: Johannes Berg @ 2021-04-19 19:41 UTC (permalink / raw)
  To: YiFei Zhu, linux-um
  Cc: Jeff Dike, Richard Weinberger, Anton Ivanov, Benjamin Berg

On Mon, 2021-04-19 at 10:32 -0500, YiFei Zhu wrote:
> 
> On the other hand, glibc reserves the 16 bytes it needs on stack
> and pops itself, so by the call instruction the stack pointer
> is exactly the caller-supplied sp. It then push the 16 bytes of
> the return address and the saved stack pointer, so the base
> pointer will be 16-byte aligned if and only if the caller
> supplied sp is 16-byte aligned. Therefore, the caller must supply
> a 16-byte aligned pointer, which `stack + UM_KERN_PAGE_SIZE`
> already satisfies.
> 
> On a side note, musl is unaffected by this issue because it forces
> 16 byte alignment via `and $-16,%rsi` in its clone wrapper.
> Similarly, glibc i386 is also uneffected because it has
> `andl $0xfffffff0, %ecx`.

I wonder if this isn't really a glibc bug?

After all, the man page states no alignment restrictions, except when
documenting the error codes:

EINVAL
stack is not aligned to a suitable boundary for  this  architecture. 
For example, on aarch64, stack must be a multiple of 16.

> To reproduce this bug, enable CONFIG_UML_RTC. uml_rtc will call
> add_sigio_fd which will then cause write_sigio_thread to go
> into segfault loop.

It must also depend on the glibc version, because I've definitely been
testing UML_RTC on 64-bit, on Fedora 32 at the time.


That said, I agree with pretty much everything else you said here.

> 
> Similarly, signal stacks will be aligned by the host kernel upon
> signal delivery. `- sizeof(void *)` to sigaltstack is
> unconventional and extraneous.
> 
> On a related note, initialization of longjmp buffers do require
> `- sizeof(void *)`. This is to account for the return address
> that would have been pushed to the stack at the call site.
> 
> The reason for uml to respect 16-byte alignment, rather than
> telling GCC to assume 8-byte alignment like the host kernel since
> commit d9b0cde91c60 ("x86-64, gcc: Use
> -mpreferred-stack-boundary=3 if supported"), is because uml links
> against libc. There is no reason to assume libc is also compiled
> with that flag and assumes 8-byte alignment rather than 16-byte.
> 
> [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838
> [2] https://c9x.me/x86/html/file_module_x86_id_180.html
> 
> Signed-off-by: YiFei Zhu <zhuyifei1999@gmail.com>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")

Reviewed-by: Johannes Berg <johannes@sipsolutions.net>

johannes


_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] um: Fix stack pointer alignment
  2021-04-19 19:41 ` Johannes Berg
@ 2021-04-19 20:36   ` YiFei Zhu
  2021-04-20  5:47     ` YiFei Zhu
  2021-04-20  6:50     ` Johannes Berg
  0 siblings, 2 replies; 7+ messages in thread
From: YiFei Zhu @ 2021-04-19 20:36 UTC (permalink / raw)
  To: Johannes Berg, Colin Ian King
  Cc: linux-um, Jeff Dike, Richard Weinberger, Anton Ivanov, Benjamin Berg

On Mon, Apr 19, 2021 at 2:41 PM Johannes Berg <johannes@sipsolutions.net> wrote:
> On Mon, 2021-04-19 at 10:32 -0500, YiFei Zhu wrote:
> > On a side note, musl is unaffected by this issue because it forces
> > 16 byte alignment via `and $-16,%rsi` in its clone wrapper.
> > Similarly, glibc i386 is also uneffected because it has

I realize I forgot to fix the typo here. Can you amend it or shall I send a v3?

> > `andl $0xfffffff0, %ecx`.
>
> I wonder if this isn't really a glibc bug?
>
> After all, the man page states no alignment restrictions, except when
> documenting the error codes:
>
> EINVAL
> stack is not aligned to a suitable boundary for  this  architecture.
> For example, on aarch64, stack must be a multiple of 16.

This could be considered a glibc bug that it doesn't force alignment,
yeah, considering musl does it for both x86_32 and x86_64, and glibc
does it for only x86_32 and not x86_64. However, I'm unaware that
anywhere saying something like "it's libc's duty to align the stack
pointer to clone()"

Speaking of aarch64, it looks like that message might be out of date.
I was trying to find where this is being enforced, and could not
quickly find the code, so did a quick search on this and see commit
e6d9a5254333 ("arm64: do not enforce strict 16 byte alignment to stack
pointer"), and also two related discussions [1][2]. It seems that the
error entry to the man page was added around the same time as the
check got removed, and the check was there only because it would have
caused SIGBUS when the clone returns. Although in x86, non-16-byte
aligned push / pop would not SIGBUS, unlike aarch64...

[1] https://patchwork.kernel.org/project/linux-arm-kernel/patch/1462985814-16146-1-git-send-email-colin.king@canonical.com/
[2] https://lore.kernel.org/linux-man/571E731A.6050809@canonical.com/

> > To reproduce this bug, enable CONFIG_UML_RTC. uml_rtc will call
> > add_sigio_fd which will then cause write_sigio_thread to go
> > into segfault loop.
>
> It must also depend on the glibc version, because I've definitely been
> testing UML_RTC on 64-bit, on Fedora 32 at the time.
>

Hmm. Interesting. I can't seem to find anything suggesting Fedora has
a patch that would align the stack within clone() [3][4]. I also got a
Fedora 32 docker image and could not see the aligning from disassembly
of clone, and the gcc version installed by yum is 10.2.1-9.fc32, which
is supposed to be affected by this issue... weird. I would expect this
to fail outright. I'm considering compiling uml inside this container
to see what is going on.

[3] https://github.com/bminor/glibc/commits/master/sysdeps/unix/sysv/linux/x86_64/clone.S
[4] https://src.fedoraproject.org/rpms/glibc/tree/rawhide

YiFei Zhu

_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] um: Fix stack pointer alignment
  2021-04-19 20:36   ` YiFei Zhu
@ 2021-04-20  5:47     ` YiFei Zhu
  2021-04-20  6:20       ` YiFei Zhu
  2021-04-20  6:51       ` Johannes Berg
  2021-04-20  6:50     ` Johannes Berg
  1 sibling, 2 replies; 7+ messages in thread
From: YiFei Zhu @ 2021-04-20  5:47 UTC (permalink / raw)
  To: Johannes Berg, Colin Ian King
  Cc: linux-um, Jeff Dike, Richard Weinberger, Anton Ivanov, Benjamin Berg

On Mon, Apr 19, 2021 at 3:36 PM YiFei Zhu <zhuyifei1999@gmail.com> wrote:
> > > To reproduce this bug, enable CONFIG_UML_RTC. uml_rtc will call
> > > add_sigio_fd which will then cause write_sigio_thread to go
> > > into segfault loop.
> >
> > It must also depend on the glibc version, because I've definitely been
> > testing UML_RTC on 64-bit, on Fedora 32 at the time.
> >
>
> Hmm. Interesting. I can't seem to find anything suggesting Fedora has
> a patch that would align the stack within clone() [3][4]. I also got a
> Fedora 32 docker image and could not see the aligning from disassembly
> of clone, and the gcc version installed by yum is 10.2.1-9.fc32, which
> is supposed to be affected by this issue... weird. I would expect this
> to fail outright. I'm considering compiling uml inside this container
> to see what is going on.

It seems config related. I tested with my original config and it
segfault loops, but then I tested with a fresh defconfig, then enabled
RTC_CLASS and UML_RTC and it boots successfully, with the assembly as:

  movaps (%rdx),%xmm0
  movaps %xmm0,(%r12)

The move from xmm0 to stack is omitted.

After a bit of trial and error I found changing from
CC_OPTIMIZE_FOR_SIZE to CC_OPTIMIZE_FOR_PERFORMANCE alone makes the
difference. What's also interesting is that instead of
segfault-looping (as in, segfault recovery did not do anything to fix
the fault, so after sigreturn it segfaults again), it panics with
"Segfault with no mm" which seems more like the expected behavior.

I'll send a v3 to clarify CC_OPTIMIZE_FOR_PERFORMANCE.

YiFei Zhu

_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] um: Fix stack pointer alignment
  2021-04-20  5:47     ` YiFei Zhu
@ 2021-04-20  6:20       ` YiFei Zhu
  2021-04-20  6:51       ` Johannes Berg
  1 sibling, 0 replies; 7+ messages in thread
From: YiFei Zhu @ 2021-04-20  6:20 UTC (permalink / raw)
  To: Johannes Berg, Colin Ian King
  Cc: linux-um, Jeff Dike, Richard Weinberger, Anton Ivanov, Benjamin Berg

On Tue, Apr 20, 2021 at 12:47 AM YiFei Zhu <zhuyifei1999@gmail.com> wrote:
>   movaps (%rdx),%xmm0
>   movaps %xmm0,(%r12)
>
> The move from xmm0 to stack is omitted.

I looked into this a bit further and thought I should clarify, these
two instructions belong to `current_poll = next_poll`. AFAICT, `tmp =
current_poll` is compiled to:

  movabs $0x605198b0,%r12 ; <current_poll>
  [...]
  movabs 0x605198b8,%rax ; <current_poll+8>
  mov    (%r12),%rcx

and `next_poll = tmp` is:

  movabs %rax,0x605198a8 ; <next_poll+8>
  mov    %rcx,(%rdx)

So it uses two registers, rax and rcx to store tmp, rather than on the stack.

YiFei Zhu

_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] um: Fix stack pointer alignment
  2021-04-19 20:36   ` YiFei Zhu
  2021-04-20  5:47     ` YiFei Zhu
@ 2021-04-20  6:50     ` Johannes Berg
  1 sibling, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2021-04-20  6:50 UTC (permalink / raw)
  To: YiFei Zhu, Colin Ian King
  Cc: linux-um, Jeff Dike, Richard Weinberger, Anton Ivanov, Benjamin Berg

Hi,

Sorry, went to sleep after sending the other mail last night :)

On Mon, 2021-04-19 at 15:36 -0500, YiFei Zhu wrote:
> On Mon, Apr 19, 2021 at 2:41 PM Johannes Berg <johannes@sipsolutions.net> wrote:
> > On Mon, 2021-04-19 at 10:32 -0500, YiFei Zhu wrote:
> > > On a side note, musl is unaffected by this issue because it forces
> > > 16 byte alignment via `and $-16,%rsi` in its clone wrapper.
> > > Similarly, glibc i386 is also uneffected because it has
> 
> I realize I forgot to fix the typo here. Can you amend it or shall I send a v3?
> 

Please resend - I most likely won't be the one applying the patch, but
Richard. Just easier that way.

> 
> This could be considered a glibc bug that it doesn't force alignment,
> yeah, considering musl does it for both x86_32 and x86_64, and glibc
> does it for only x86_32 and not x86_64. However, I'm unaware that
> anywhere saying something like "it's libc's duty to align the stack
> pointer to clone()"
> 

True. And the man page does document alignment restrictions at least in
some cases.

> Speaking of aarch64, it looks like that message might be out of date.

Might very well be. I found another reference to "126-bit alignment"
(and have since sent a patch to fix that to say "128-bit"), but I don't
know whether any of that is really accurate.
> > 
> > It must also depend on the glibc version, because I've definitely been
> > testing UML_RTC on 64-bit, on Fedora 32 at the time.
> > 
> 
> Hmm. Interesting. I can't seem to find anything suggesting Fedora has
> a patch that would align the stack within clone() [3][4]. I also got a
> Fedora 32 docker image and could not see the aligning from disassembly
> of clone, and the gcc version installed by yum is 10.2.1-9.fc32, which
> is supposed to be affected by this issue... weird. I would expect this
> to fail outright. I'm considering compiling uml inside this container
> to see what is going on.

Hm, wait. It could also be a *compiler* version thing, right?

And actually, most of my testing probably wasn't on Fedora 33, but
rather on a somewhat dated version of nixos, with gcc 9.3. I could even
give you the description file to reproduce the exact environment, but I
doubt it's really worthwhile. Let's just fix the bug?

johannes


_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] um: Fix stack pointer alignment
  2021-04-20  5:47     ` YiFei Zhu
  2021-04-20  6:20       ` YiFei Zhu
@ 2021-04-20  6:51       ` Johannes Berg
  1 sibling, 0 replies; 7+ messages in thread
From: Johannes Berg @ 2021-04-20  6:51 UTC (permalink / raw)
  To: YiFei Zhu, Colin Ian King
  Cc: linux-um, Jeff Dike, Richard Weinberger, Anton Ivanov, Benjamin Berg

> 
> It seems config related. I tested with my original config and it
> segfault loops, but then I tested with a fresh defconfig, then enabled
> RTC_CLASS and UML_RTC and it boots successfully, with the assembly as:
> 
>   movaps (%rdx),%xmm0
>   movaps %xmm0,(%r12)
> 
> The move from xmm0 to stack is omitted.
> 
> After a bit of trial and error I found changing from
> CC_OPTIMIZE_FOR_SIZE to CC_OPTIMIZE_FOR_PERFORMANCE alone makes the
> difference. What's also interesting is that instead of
> segfault-looping (as in, segfault recovery did not do anything to fix
> the fault, so after sigreturn it segfaults again), it panics with
> "Segfault with no mm" which seems more like the expected behavior.

Aha, interesting.

> 
> I'll send a v3 to clarify CC_OPTIMIZE_FOR_PERFORMANCE.

Cool, thanks for all the digging and fixes!

johannes


_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-04-20  6:51 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-19 15:32 [PATCH v2] um: Fix stack pointer alignment YiFei Zhu
2021-04-19 19:41 ` Johannes Berg
2021-04-19 20:36   ` YiFei Zhu
2021-04-20  5:47     ` YiFei Zhu
2021-04-20  6:20       ` YiFei Zhu
2021-04-20  6:51       ` Johannes Berg
2021-04-20  6:50     ` Johannes Berg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.