tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Tests requiring MD hooks



Hi list,

(sorry to cross-post toolchain and userlevel, I don't know where
questions for ATF tests/ should go)

We already have some tests covering read/write permission under
lib/libc/sys/t_mprotect, so I started to write code for the --x counterpart.

That seems to be a bit tricky to write in a portable way: testing --x
right in memory pages requires to put/copy some payload in a page and
call it. I battled with GCC for a few hours trying to find a way to make
this in a MI way, and finally gave up and move to using "pure assembly"
code; neither volatile variables around C functions, nor mixing inline
assembly with C blocks makes it possible to "automagically" detect
beginning/end marker of a function code.

Attached is a proposal for the --x mprotect case; as I am on a route to
test other parts of memory too (like non-executable stack), I'd like to
have a trivial return_one() shellcode and expose it publicly. Although
the shellcode is not really libc-related, I added the i386/amd64 ones
under tests/lib/libc/arch/, with the associated header available under
tests/lib/common/exec_prot.h.

Only i386 and amd64 payloads are currently implemented. To make the test
a bit more helpful, all architectures are expected to inform the caller
(via exec_prot_support) whether they support --x rights or not.

Comments on the patch are welcomed, especially the placement of files.
tests/ are not big consumers of MD hooks currently, I'd like to have
clear directions regarding cases that do not really belong anywhere. For
example, non-executable stack tests are not really linked with either
libc or csu (perhaps crt?), yet I have to know where they should
eventually go.

Same goes for tests/lib/libc/arch vs tests/lib/arch/ for MD hooks. It's
hard to figure out a way to organize tests while remaining consistent
with current src/ layout.

Thanks!

-- 
Jean-Yves Migeon
jeanyves.migeon%free.fr@localhost
Index: tests/lib/libc/sys/Makefile
===================================================================
RCS file: /cvsroot/src/tests/lib/libc/sys/Makefile,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 Makefile
--- tests/lib/libc/sys/Makefile 7 Jul 2011 19:29:58 -0000       1.6
+++ tests/lib/libc/sys/Makefile 11 Jul 2011 01:08:21 -0000
@@ -4,6 +4,15 @@ MKMAN= no
 
 .include <bsd.own.mk>
 
+.if exists(${.CURDIR}/../arch/${MACHINE_CPU})
+ARCHDIR=       ${.CURDIR}/../arch/${MACHINE_CPU}
+.PATH:         ${ARCHDIR}
+.else
+.BEGIN:
+       @echo no ARCHDIR for ${MACHINE_CPU}
+       @false
+.endif
+
 TESTSDIR=              ${TESTSBASE}/lib/libc/sys
 
 TESTS_C+=              t_access
@@ -41,6 +50,8 @@ TESTS_C+=             t_truncate
 TESTS_C+=              t_umask
 TESTS_C+=              t_unlink
 
+SRCS.t_mprotect=       t_mprotect.c misc.c return_one.S
+
 LDADD.t_getpid+=        -lpthread
 LDADD.t_timer_create+=  -lpthread
 
Index: tests/lib/libc/sys/t_mprotect.c
===================================================================
RCS file: /cvsroot/src/tests/lib/libc/sys/t_mprotect.c,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 t_mprotect.c
--- tests/lib/libc/sys/t_mprotect.c     7 Jul 2011 06:57:54 -0000       1.1
+++ tests/lib/libc/sys/t_mprotect.c     11 Jul 2011 01:08:21 -0000
@@ -44,6 +44,8 @@ __RCSID("$NetBSD: t_mprotect.c,v 1.1 201
 
 #include <atf-c.h>
 
+#include "../../common/exec_prot.h"
+
 static long    page = 0;
 static int     pax_global = -1;
 static int     pax_enabled = -1;
@@ -158,10 +160,91 @@ ATF_TC_BODY(mprotect_err, tc)
        ATF_REQUIRE(errno == EINVAL);
 }
 
+ATF_TC(mprotect_exec);
+ATF_TC_HEAD(mprotect_exec, tc)
+{
+       atf_tc_set_md_var(tc, "descr",
+           "Test mprotect(2) executable space protections");
+}
+
+/*
+ * Trivial function -- should fit into a page
+ */
+ATF_TC_BODY(mprotect_exec, tc)
+{
+       pid_t pid;
+       void *map;
+       int sta, xp_support;
+
+       xp_support = exec_prot_support();
+
+       switch (xp_support) {
+       case NOTIMPL:
+               atf_tc_skip(
+                   "Execute protection callback check not implemented");
+               break;
+       case NO_XP:
+               atf_tc_skip(
+                   "Host does not support executable space protection");
+               break;
+       case PARTIAL_XP: case PERPAGE_XP: default:
+               break;
+       }
+
+       /*
+        * Map a page read/write and copy a trivial assembly function inside.
+        * We will then change the mapping rights:
+        * - first by setting the execution right, and check that we can
+        *   call the code found in the allocated page.
+        * - second by removing the execution right. This should generate
+        *   a SIGSEGV on architectures that can enforce --x permissions.
+        */
+
+       map = mmap(NULL, page, PROT_WRITE|PROT_READ, MAP_ANON, -1, 0);
+       ATF_REQUIRE(map != MAP_FAILED);
+
+       memcpy(map, (void *)return_one,
+           (uintptr_t)return_one_end - (uintptr_t)return_one);
+ 
+       /* give r-x rights then call code in page */
+       ATF_REQUIRE(mprotect(map, page, PROT_EXEC|PROT_READ) == 0);
+       ATF_REQUIRE(((int (*)(void))map)() == 1);
+
+       /* remove --x right */
+       ATF_REQUIRE(mprotect(map, page, PROT_READ) == 0);
+
+       pid = fork();
+       ATF_REQUIRE(pid >= 0);
+
+       if (pid == 0) {
+               ATF_REQUIRE(signal(SIGSEGV, sighandler) != SIG_ERR);
+               ATF_CHECK(((int (*)(void))map)() == 1);
+               _exit(0);
+       }
+
+       (void)wait(&sta);
+
+       ATF_REQUIRE(WIFEXITED(sta) != 0);
+
+       switch (xp_support) {
+       case PARTIAL_XP:
+               /* Partial protection might fail: indicate it */
+               ATF_CHECK_MSG(WEXITSTATUS(sta) == SIGSEGV,
+                   "Host only supports partial executable space protection");
+               break;
+       case PERPAGE_XP: default:
+               /* Per-page --x protection should not fail */
+               ATF_REQUIRE(WEXITSTATUS(sta) == SIGSEGV);
+               break;
+       }
+
+       ATF_REQUIRE(munmap(map, page) == 0);
+}
+
 ATF_TC(mprotect_pax);
 ATF_TC_HEAD(mprotect_pax, tc)
 {
-       atf_tc_set_md_var(tc, "descr", "PaX restrictions and mprotect(2),");
+       atf_tc_set_md_var(tc, "descr", "PaX restrictions and mprotect(2)");
        atf_tc_set_md_var(tc, "require.user", "root");
 }
 
@@ -224,7 +307,7 @@ out:
 ATF_TC(mprotect_write);
 ATF_TC_HEAD(mprotect_write, tc)
 {
-       atf_tc_set_md_var(tc, "descr", "Test mprotect(2) protections");
+       atf_tc_set_md_var(tc, "descr", "Test mprotect(2) write protections");
 }
 
 ATF_TC_BODY(mprotect_write, tc)
@@ -266,6 +349,7 @@ ATF_TP_ADD_TCS(tp)
 
        ATF_TP_ADD_TC(tp, mprotect_access);
        ATF_TP_ADD_TC(tp, mprotect_err);
+       ATF_TP_ADD_TC(tp, mprotect_exec);
        ATF_TP_ADD_TC(tp, mprotect_pax);
        ATF_TP_ADD_TC(tp, mprotect_write);
 
--- /dev/null   2011-07-11 02:58:19.000000000 +0200
+++ tests/lib/common/exec_prot.h        2011-07-10 20:39:07.000000000 +0200
@@ -0,0 +1,61 @@
+/* $NetBSD$ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jean-Yves Migeon.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TESTS_H_FUNC_H_
+#define _TESTS_H_FUNC_H_
+
+/*
+ * Prototype definitions of external helper functions for executable
+ * mapping tests.
+ */
+
+/*
+ * Trivial MD shellcode that justs returns 1.
+ */
+int return_one(void);     /* begin marker -- shellcode entry */
+int return_one_end(void); /* end marker */
+
+/*
+ * MD callback to verify whether host offers executable space protection.
+ * Returns execute protection level.
+ */
+int exec_prot_support(void);
+
+/* execute protection level */
+enum {
+       NOTIMPL = -1, /* callback not implemented */
+       NO_XP,        /* no execute protection */
+       PERPAGE_XP,   /* per-page execute protection */
+       PARTIAL_XP    /* partial execute protection. Depending on where the
+                        page is located in virtual memory, executable space
+                        protection may be enforced or not. */
+};
+#endif
--- /dev/null   2011-07-11 02:58:19.000000000 +0200
+++ tests/lib/libc/arch/i386/misc.c     2011-07-11 02:54:06.000000000 +0200
@@ -0,0 +1,65 @@
+/*      $NetBSD$ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jean-Yves Migeon.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <stdlib.h>
+#include <sys/sysctl.h>
+
+#include "../../../common/exec_prot.h"
+
+/*
+ * Support for executable space protection has always been erratic under i386.
+ * Originally IA-32 can't do per-page execute permission, so it is
+ * implemented using different executable segments for %cs (code segment).
+ * This only allows coarse grained protection, especially when memory starts
+ * being fragmented.
+ * Later, PAE was introduced together with a NX/XD bit in the page table
+ * entry to offer per-page permission.
+ */
+int
+exec_prot_support(void)
+{
+       int pae;
+       size_t pae_len = sizeof(pae);
+
+       if (sysctlbyname("machdep.pae", &pae, &pae_len, NULL, 0) == -1)
+               return PARTIAL_XP;
+
+       if (pae == 1) {
+               if (system("cpuctl identify 0 | grep -q NOX") == 0 ||
+                   system("cpuctl identify 0 | grep -q XD") == 0)
+                       return PERPAGE_XP;
+       }
+       
+       return PARTIAL_XP;
+}
--- /dev/null   2011-07-11 02:58:19.000000000 +0200
+++ tests/lib/libc/arch/i386/return_one.S       2011-07-11 02:45:38.000000000 
+0200
@@ -0,0 +1,38 @@
+/*     $NetBSD$ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jean-Yves Migeon.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+RCSID("$NetBSD$");
+
+_ENTRY(return_one)
+       movl    $0x1,%eax
+       ret
+LABEL(return_one_end)
--- /dev/null   2011-07-11 02:58:19.000000000 +0200
+++ tests/lib/libc/arch/x86_64/misc.c   2011-07-10 21:25:24.000000000 +0200
@@ -0,0 +1,50 @@
+/*      $NetBSD$ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jean-Yves Migeon.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <stdlib.h>
+
+#include "../../../common/exec_prot.h"
+
+/*
+ * When the NX/XD flag is present, the protection should be enabled.
+ */
+int
+exec_prot_support(void)
+{
+       if (system("cpuctl identify 0 | grep -q NOX") == 0 ||
+           system("cpuctl identify 0 | grep -q XD") == 0)
+               return PERPAGE_XP;
+       
+       return NO_XP;
+}
--- /dev/null   2011-07-11 02:58:19.000000000 +0200
+++ tests/lib/libc/arch/x86_64/return_one.S     2011-07-11 02:45:45.000000000 
+0200
@@ -0,0 +1,38 @@
+/*     $NetBSD$ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jean-Yves Migeon.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+RCSID("$NetBSD$");
+
+_ENTRY(return_one)
+       movq    $0x1, %rax
+       retq
+LABEL(return_one_end)


Home | Main Index | Thread Index | Old Index