Skip to content

Commit

Permalink
ntdll: Catch all syscalls at the lower addresses.
Browse files Browse the repository at this point in the history
CW-Bug-Id: #19085
  • Loading branch information
Paul Gofman authored and ivyl committed Sep 23, 2021
1 parent 752c1be commit c820fe0
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 33 deletions.
79 changes: 49 additions & 30 deletions dlls/ntdll/unix/signal_x86_64.c
Expand Up @@ -71,6 +71,7 @@
# include <linux/filter.h>
# include <linux/seccomp.h>
# include <sys/prctl.h>
# include <linux/audit.h>
#endif

#define NONAMELESSUNION
Expand Down Expand Up @@ -2490,59 +2491,71 @@ static void install_bpf(struct sigaction *sig_act)
# ifndef SECCOMP_SET_MODE_FILTER
# define SECCOMP_SET_MODE_FILTER 1
# endif
static const unsigned int flags = SECCOMP_FILTER_FLAG_SPEC_ALLOW;
static struct sock_filter filter[] =
static const BYTE syscall_trap_test[] =
{
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
(offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0xf000, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
0x48, 0x89, 0xc8, /* mov %rcx, %rax */
0x0f, 0x05, /* syscall */
0xc3, /* retq */
};
static struct sock_filter filter_rdr2[] =
{
/* Trap anything called from RDR2 or the launcher (0x140000000 - 0x150000000)*/
/* > 0x140000000 */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer) + 0),
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x40000000 /*lsb*/, 0, 7),
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer) + 4),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x1 /*msb*/, 0, 5),
static const unsigned int flags = SECCOMP_FILTER_FLAG_SPEC_ALLOW;

/* < 0x150000000 */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer) + 0),
BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, 0x50000000 /*lsb*/, 3, 0),
#define NATIVE_SYSCALL_ADDRESS_START 0x700000000000

static struct sock_filter filter[] =
{
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer) + 4),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x1 /*msb*/, 0, 1),
/* Native libs are loaded at high addresses. */
BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, NATIVE_SYSCALL_ADDRESS_START >> 32, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
/* Allow i386. */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
BPF_JUMP (BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
/* Allow wine64-preloader */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, instruction_pointer)),
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x7d400000, 1, 0),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 0x7d402000, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRAP),

/* Allow everything else */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
long (WINAPI *test_syscall)(long sc_number);
struct sock_fprog prog;
BOOL rdr2 = FALSE;
NTSTATUS status;

if ((ULONG_PTR)sc_seccomp < NATIVE_SYSCALL_ADDRESS_START
|| (ULONG_PTR)syscall < NATIVE_SYSCALL_ADDRESS_START)
{
ERR("Native libs are being loaded in low addresses, sc_seccomp %p, syscall %p, not installing seccomp.\n",
sc_seccomp, syscall);
ERR("The known reasons are /proc/sys/vm/legacy_va_layout set to 1 or 'ulimit -s' being 'unlimited'.\n");
return;
}

sig_act->sa_sigaction = sigsys_handler;
memset(&prog, 0, sizeof(prog));

{
const char *sgi = getenv("SteamGameId");
if (sgi && (!strcmp(sgi, "1174180") || !strcmp(sgi, "1404210")))
{
/* Use specific filter and signal handler for Red Dead Redemption 2 */
prog.len = ARRAY_SIZE(filter_rdr2);
prog.filter = filter_rdr2;
/* Use specific signal handler for Red Dead Redemption 2 */
sig_act->sa_sigaction = sigsys_handler_rdr2;
rdr2 = TRUE;
}
}

sigaction(SIGSYS, sig_act, NULL);

if (rdr2)
test_syscall = mmap((void *)0x600000000000, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (test_syscall != (void *)0x600000000000)
{
int ret;

ERR("Could not allocate test syscall, falling back to seccomp presence check, test_syscall %p, errno %d.\n",
test_syscall, errno);
if (test_syscall != MAP_FAILED) munmap(test_syscall, 0x1000);

if ((ret = prctl(PR_GET_SECCOMP, 0, NULL, 0, 0)))
{
if (ret == 2)
Expand All @@ -2554,7 +2567,10 @@ static void install_bpf(struct sigaction *sig_act)
}
else
{
if ((status = syscall(0xffff)) == STATUS_INVALID_PARAMETER)
memcpy(test_syscall, syscall_trap_test, sizeof(syscall_trap_test));
status = test_syscall(0xffff);
munmap(test_syscall, 0x1000);
if (status == STATUS_INVALID_PARAMETER)
{
TRACE("Seccomp filters already installed.\n");
return;
Expand All @@ -2564,10 +2580,13 @@ static void install_bpf(struct sigaction *sig_act)
ERR("Unexpected status %#x, errno %d.\n", status, errno);
return;
}
prog.len = ARRAY_SIZE(filter);
prog.filter = filter;
}

TRACE("Installing seccomp filters.\n");

prog.len = ARRAY_SIZE(filter);
prog.filter = filter;

if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
ERR("prctl(PR_SET_NO_NEW_PRIVS, ...): %s.\n", strerror(errno));
Expand Down
5 changes: 2 additions & 3 deletions tools/winebuild/import.c
Expand Up @@ -1607,7 +1607,6 @@ static void output_syscall_dispatcher( int count, const char *variant )
/* Legends of Runeterra hooks the first system call return instruction, and
* depends on us returning to it. Adjust the return address accordingly. */
output( "\tsubq $0xb,0x8(%%rbp)\n" );
output( "\tsubq $0xf000,%%rax\n" );
output( "\tmovq 0x8(%%rbp),%%rbx\n" );
output( "\tmovq %%rbx,-0x28(%%rbp)\n" );
output( "\tleaq 0x10(%%rbp),%%rbx\n" );
Expand Down Expand Up @@ -1873,7 +1872,7 @@ void output_syscalls( DLLSPEC *spec )
{
output( "\t.align %d\n", get_alignment( get_ptr_size() ) );
output( "%s\n", asm_globl(strmake( "__wine_syscall_nr_%s", get_link_name( syscalls[i] ) )) );
output( "\t.long %u\n", i + 0xf000 );
output( "\t.long %u\n", i );
}
return;
}
Expand Down Expand Up @@ -1912,7 +1911,7 @@ void output_syscalls( DLLSPEC *spec )
* validate that instruction, we can just put a jmp there instead. */
output( "\t.byte 0x4c,0x8b,0xd1\n" ); /* movq %rcx,%r10 */
output( "\t.byte 0xb8\n" ); /* movl $i,%eax */
output( "\t.long %u\n", 0xf000 + i );
output( "\t.long %u\n", i );
output( "\t.byte 0xf6,0x04,0x25,0x08,0x03,0xfe,0x7f,0x01\n" ); /* testb $1,0x7ffe0308 */
output( "\t.byte 0x75,0x03\n" ); /* jne 1f */
output( "\t.byte 0x0f,0x05\n" ); /* syscall */
Expand Down

0 comments on commit c820fe0

Please sign in to comment.