From deeae2d57a5b67c29fc378ce6edd576805fe7ae7 Mon Sep 17 00:00:00 2001 From: anton Date: Wed, 18 Jan 2023 19:18:49 +0000 Subject: [PATCH] Add xonly tests, initially written by deraadt@. Tweaked by me to fit and the regress framework and allowing the expected outcome to be enumerated per architecture. Currently limited to amd64 and arm64. --- regress/sys/kern/Makefile | 3 +- regress/sys/kern/xonly/Makefile | 13 ++ regress/sys/kern/xonly/xonly.c | 239 ++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 regress/sys/kern/xonly/Makefile create mode 100644 regress/sys/kern/xonly/xonly.c diff --git a/regress/sys/kern/Makefile b/regress/sys/kern/Makefile index 108c011566f..b85e588d63c 100644 --- a/regress/sys/kern/Makefile +++ b/regress/sys/kern/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.103 2021/12/24 08:49:19 guenther Exp $ +# $OpenBSD: Makefile,v 1.104 2023/01/18 19:18:49 anton Exp $ SUBDIR+= accept access SUBDIR+= bind @@ -24,6 +24,7 @@ SUBDIR+= signal sosplice stackjmp stackpivot syscall syscall_segment SUBDIR+= sysvmsg sysvsem sysvshm SUBDIR+= unalign unfdpass unixsockets unveil unveil-unmount SUBDIR+= wait +SUBDIR+= xonly install: diff --git a/regress/sys/kern/xonly/Makefile b/regress/sys/kern/xonly/Makefile new file mode 100644 index 00000000000..882b4727512 --- /dev/null +++ b/regress/sys/kern/xonly/Makefile @@ -0,0 +1,13 @@ +# $OpenBSD: Makefile,v 1.1 2023/01/18 19:18:49 anton Exp $ + +.if ${MACHINE_ARCH} == "aarch64" || \ + ${MACHINE_ARCH} == "amd64" +PROG= xonly +WARNINGS= yes +.elif make(regress) || make(all) +regress: + @echo Cannot run on ${MACHINE_ARCH}. + @echo SKIPPED +.endif + +.include diff --git a/regress/sys/kern/xonly/xonly.c b/regress/sys/kern/xonly/xonly.c new file mode 100644 index 00000000000..89268d947c9 --- /dev/null +++ b/regress/sys/kern/xonly/xonly.c @@ -0,0 +1,239 @@ +/* $OpenBSD: xonly.c,v 1.1 2023/01/18 19:18:49 anton Exp $ */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNREADABLE 0 +#define READABLE 1 + +int main(void); +void sigsegv(int); +void setup_table(void); + +void *setup_ldso(void); +void *setup_mmap_xz(void); +void *setup_mmap_x(void); +void *setup_mmap_nrx(void); +void *setup_mmap_nwx(void); +void *setup_mmap_xnwx(void); + +struct outcome { + int uu; + int ku; +}; + +struct readable { + const char *name; + void *(*setup)(void); + int isfn; + void *addr; + int skip; + struct outcome got; +} readables[] = { + { "ld.so", setup_ldso, 1, NULL, 0, {} }, + { "mmap xz", setup_mmap_xz, 0, NULL, 0, {} }, + { "mmap x", setup_mmap_x, 0, NULL, 0, {} }, + { "mmap nrx", setup_mmap_nrx, 0, NULL, 0, {} }, + { "mmap nwx", setup_mmap_nwx, 0, NULL, 0, {} }, + { "mmap xnwx", setup_mmap_xnwx, 0, NULL, 0, {} }, + { "main", NULL, 1, &main, 0, {} }, + { "libc", NULL, 1, &open, 0, {} }, +}; + +static struct outcome expectations[2][8] = { +#if defined(__aarch64__) + [0] = { + /* ld.so */ { UNREADABLE, UNREADABLE }, + /* mmap xz */ { UNREADABLE, UNREADABLE }, + /* mmap x */ { UNREADABLE, UNREADABLE }, + /* mmap nrx */ { UNREADABLE, UNREADABLE }, + /* mmap nwx */ { UNREADABLE, UNREADABLE }, + /* mmap xnwx */ { UNREADABLE, UNREADABLE }, + /* main */ { UNREADABLE, UNREADABLE }, + /* libc */ { UNREADABLE, UNREADABLE }, + }, +#elif defined(__amd64__) + /* PKU not available. */ + [0] = { + /* ld.so */ { READABLE, READABLE }, + /* mmap xz */ { UNREADABLE, UNREADABLE }, + /* mmap x */ { READABLE, READABLE }, + /* mmap nrx */ { READABLE, READABLE }, + /* mmap nwx */ { READABLE, READABLE }, + /* mmap xnwx */ { READABLE, READABLE }, + /* main */ { READABLE, READABLE }, + /* libc */ { READABLE, READABLE }, + }, +#else +#error "unknown architecture" +#endif +}; + +jmp_buf fail; + +void +sigsegv(__unused int signo) +{ + longjmp(fail, 1); +} + +void * +setup_mmap_xz(void) +{ + return mmap(NULL, getpagesize(), PROT_EXEC, + MAP_ANON | MAP_PRIVATE, -1, 0); + /* no data written. tests read-fault of an unbacked exec-only page */ +} + +void * +setup_mmap_x(void) +{ + char *addr; + + addr = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + explicit_bzero(addr, getpagesize()); + mprotect(addr, getpagesize(), PROT_EXEC); + return addr; +} + +void * +setup_mmap_nrx(void) +{ + char *addr; + + addr = mmap(NULL, getpagesize(), PROT_NONE, + MAP_ANON | MAP_PRIVATE, -1, 0); + mprotect(addr, getpagesize(), PROT_READ | PROT_WRITE); + explicit_bzero(addr, getpagesize()); + mprotect(addr, getpagesize(), PROT_EXEC); + return addr; +} + +void * +setup_mmap_nwx(void) +{ + char *addr; + + addr = mmap(NULL, getpagesize(), PROT_NONE, + MAP_ANON | MAP_PRIVATE, -1, 0); + mprotect(addr, getpagesize(), PROT_WRITE); + explicit_bzero(addr, getpagesize()); + mprotect(addr, getpagesize(), PROT_EXEC); + return addr; +} + +void * +setup_mmap_xnwx(void) +{ + char *addr; + + addr = mmap(NULL, getpagesize(), PROT_EXEC, + MAP_ANON | MAP_PRIVATE, -1, 0); + mprotect(addr, getpagesize(), PROT_NONE); + mprotect(addr, getpagesize(), PROT_WRITE); + explicit_bzero(addr, getpagesize()); + mprotect(addr, getpagesize(), PROT_EXEC); + return addr; +} + +void * +setup_ldso(void) +{ + void *dlopenp, *handle; + + handle = dlopen("ld.so", RTLD_NOW); + if (handle == NULL) + errx(1, "dlopen"); + dlopenp = dlsym(handle, "dlopen"); + return dlopenp; +} + +void +setup_table(void) +{ + size_t i; + + for (i = 0; i < sizeof(readables)/sizeof(readables[0]); i++) { + if (setjmp(fail) == 0) { + if (readables[i].setup) + readables[i].addr = readables[i].setup(); + } else { + readables[i].skip = 1; + } +#ifdef __hppa__ + /* hppa ptable headers point at the instructions */ + if (readables[i].isfn) { + readables[i].addr = (void *)*(u_int *) + ((u_int)readables[i].addr & ~3); + } +#endif + } +} + +int +main(void) +{ + size_t i; + int p[2]; + int error = 0; + + signal(SIGSEGV, sigsegv); + signal(SIGBUS, sigsegv); + + setup_table(); + + for (i = 0; i < sizeof(readables)/sizeof(readables[0]); i++) { + struct readable *r = &readables[i]; + char c; + + if (r->skip) + continue; + pipe(p); + fcntl(p[0], F_SETFL, O_NONBLOCK); + + if (write(p[1], r->addr, 1) == 1 && read(p[0], &c, 1) == 1) + r->got.ku = 1; + + if (setjmp(fail) == 0) { + volatile int x = *(int *)(r->addr); + (void)x; + r->got.uu = 1; + } + + close(p[0]); + close(p[1]); + } + + printf("%-16s %18s userland kernel\n", "", ""); + for (i = 0; i < sizeof(readables)/sizeof(readables[0]); i++) { + struct readable *r = &readables[i]; + + if (r->skip) { + printf("%-16s %18p %-12s %-12s\n", r->name, r->addr, + "skipped", "skipped"); + } else { + const struct outcome *want = &expectations[0][i]; + + if (r->got.uu != want->uu || r->got.ku != want->ku) + error++; + + printf("%-16s %18p %s%-10s %s%-10s\n", + r->name, r->addr, + r->got.uu == want->uu ? "P " : "F ", + r->got.uu ? "readable" : "unreadable", + r->got.ku == want->ku ? "P " : "F ", + r->got.ku ? "readable" : "unreadable"); + } + } + return error; +} -- 2.20.1