I sometimes play video games on Steam under Linux. Recently I bought a new CPU (based on Zen3) and one of the games - Dirt Rally - started segfaulting.
After a quick fight with attaching strace to a running process under Steam (it was crashing v. quickly, so some racy script was needed), it turned out it crashes with
SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_PKUERR, si_addr=0x7ff88440e7b8}
A quick session with kernel sources, revealed it's about Memory Protection Keys (this causes SEGV_PKUERR), which seemed like something that game was not using at all, because it's quite a new feature. After another hour or two, I found out what was the cause. The game was mmaping a PROT_EXEC memory segment without the PROT_READ flag, and was trying to read something from there with a 'mov' assembler instruction. mprotect(0x7ff884400000, 139264, PROT_EXEC) = 0
And under older i386/x86-64 CPUs it implicitly means PROT_EXEC|PROT_READ, because there is no way to make something executable, but readable. Under newer CPUs, the Linux kernel uses Memory Protection Keys to make the memory region actually executable-only.After creating a quick'n'dirty LD_PRELOAD'able lib, the problem went away. Though, I wonder about Linus Torvald's mantra here, that the Linux kernel shouldn't break the userspace :)
$ cat game.c
#include <sys/mman.h>
#include <unistd.h>
#include <sys/syscall.h>
int mprotect(void *addr, size_t len, int prot) {
if (prot == PROT_EXEC) {
prot |= PROT_READ;
}
return syscall(__NR_mprotect, addr, len, prot);
}
$ gcc game.c -shared -o game.so
$ LD_PRELOAD=./game.so steam