Skip to content

Segfaults if CPU's PKU/PKRU feature is enabled #295

Open
@pipcet

Description

@pipcet

Hello!

This is about a bug affecting the msys2-docker-experimental images, reported at msys2/msys2-docker#18 . I've now found this bug and think it should be fixed in the gendef file.

I'm not sure whether this bug applies to Cygwin, too, but it's about this code in gendef:

	movl	\$0x0d,%eax
	xorl	%ecx,%ecx
	cpuid	# get necessary space for xsave
	movq	%rbx,%rcx
	addq	\$0x48,%rbx # 0x18 for alignment, 0x30 for additional space
	subq	%rbx,%rsp
	movl	%ebx,0x24(%rsp)
	xorq	%rax,%rax
	shrq	\$3,%rcx
	leaq	0x30(%rsp),%rdi
	rep	stosq
	xgetbv	# get XCR0 (ecx is 0 after rep)
	movl	%eax,0x28(%rsp)
	movl	%edx,0x2c(%rsp)
	notl	%ecx # set ecx non-zero
	movl	%ecx,0x20(%rsp)
	xsave64	0x30(%rsp)

When running in Wine, I'm seeing segfaults caused by the final xsave64 instruction. These are because if the CPU's PKU/PKRU flag is enabled by Linux (disabling it by passing nopku on the kernel command line prevents the bug), it is also enabled in wine binaries, and that will make the cpuid instruction report a value of 2440 in rbx and rcx. This value is not a multiple of 64.

This results in a misaligned stack pointer when we hit the xsave64 instruction; xsave64 requires a 64-byte aligned storage area, and in an actual run, %rsp is 0x7ffffb648. This means the store goes to 0x7ffffb678, which isn't 64-byte aligned, causing a segfault.

However, even if the xsave64 instruction were to succeed, we'd crash a bit later because the stack pointer isn't aligned to the 16-byte boundary required by the ABI.

Unfortunately, I don't know how to rebuild msys2 to fix this. I can work around it by using Wine-gdb (winedbg --gdb bash.exe -c ... is what I'm running) , though:

For me, the cpuid instruction above is at 0x18019c645, so the following instruction is at 0x18019c647. After the cpuid instruction, we inject a breakpoint which increases %rbx by 56, making it 64-byte aligned again:

b *0x18019c647
Breakpoint 1 at 0x18019c647
Wine-gdb> command 1
command 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>p $rbx += 56
>c
>end
Wine-gdb> c

This allows the program to execute normally even if the CPU's PKU/PKRU feature flag is in use. This suffices to convince me the bug is as I've described; sorry if the description is a bit terse, I'm happy to answer questions!

The proposed fix is to add something to the effect of %rbx += 63; %rbx &= -64; to gendef after the cpuid instruction but before the following mov: this will increase the reported size of the xsave area to the next 64-byte multiple, which will have the same effect as the GDB script above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions