Add xonly tests, initially written by deraadt@. Tweaked by me to fit and
authoranton <anton@openbsd.org>
Wed, 18 Jan 2023 19:18:49 +0000 (19:18 +0000)
committeranton <anton@openbsd.org>
Wed, 18 Jan 2023 19:18:49 +0000 (19:18 +0000)
the regress framework and allowing the expected outcome to be enumerated
per architecture. Currently limited to amd64 and arm64.

regress/sys/kern/Makefile
regress/sys/kern/xonly/Makefile [new file with mode: 0644]
regress/sys/kern/xonly/xonly.c [new file with mode: 0644]

index 108c011..b85e588 100644 (file)
@@ -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 (file)
index 0000000..882b472
--- /dev/null
@@ -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 <bsd.regress.mk>
diff --git a/regress/sys/kern/xonly/xonly.c b/regress/sys/kern/xonly/xonly.c
new file mode 100644 (file)
index 0000000..89268d9
--- /dev/null
@@ -0,0 +1,239 @@
+/*     $OpenBSD: xonly.c,v 1.1 2023/01/18 19:18:49 anton Exp $ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#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;
+}