Changeset View
Standalone View
tests/sys/vm/page_fault_signal.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause | |||||
* | |||||
* Copyright (c) 2019 Jilles Tjoelker | |||||
* | |||||
* 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 AUTHOR 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 AUTHOR 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. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
#include <sys/mman.h> | |||||
#include <atf-c.h> | |||||
#include <fcntl.h> | |||||
#include <setjmp.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | |||||
static sigjmp_buf sig_env; | |||||
static volatile int last_sig, last_code; | |||||
static void | |||||
sighandler(int sig, siginfo_t *info, void *context __unused) | |||||
{ | |||||
last_sig = sig; | |||||
last_code = info->si_code; | |||||
siglongjmp(sig_env, 1); | |||||
} | |||||
static void | |||||
setup_signals(void) | |||||
{ | |||||
struct sigaction sa; | |||||
int r; | |||||
sa.sa_sigaction = sighandler; | |||||
sa.sa_flags = SA_RESTART | SA_RESETHAND | SA_SIGINFO; | |||||
r = sigfillset(&sa.sa_mask); | |||||
ATF_REQUIRE(r != -1); | |||||
kib: Why is this sigfillset needed ? | |||||
Done Inline ActionsTo prevent the signal handler being entered again between siglongjmp and sigsetjmp. This is not a very strong reason, but I need to initialize sa.sa_mask in any case. jilles: To prevent the signal handler being entered again between siglongjmp and sigsetjmp.
This is… | |||||
r = sigaction(SIGILL, &sa, NULL); | |||||
ATF_REQUIRE(r != -1); | |||||
r = sigaction(SIGBUS, &sa, NULL); | |||||
ATF_REQUIRE(r != -1); | |||||
r = sigaction(SIGSEGV, &sa, NULL); | |||||
ATF_REQUIRE(r != -1); | |||||
} | |||||
ATF_TC_WITHOUT_HEAD(page_fault_signal__segv_maperr_1); | |||||
ATF_TC_BODY(page_fault_signal__segv_maperr_1, tc) | |||||
{ | |||||
int *p; | |||||
int r; | |||||
int sz; | |||||
sz = getpagesize(); | |||||
p = mmap(NULL, sz, PROT_READ, MAP_ANON, -1, 0); | |||||
ATF_REQUIRE(p != MAP_FAILED); | |||||
r = munmap(p, sz); | |||||
ATF_REQUIRE(r != -1); | |||||
if (sigsetjmp(sig_env, 1) == 0) { | |||||
setup_signals(); | |||||
*(volatile int *)p = 1; | |||||
} | |||||
Not Done Inline ActionsPerhaps add volatile somewhere ? Or use C11 atomic_store relaxed. kib: Perhaps add volatile somewhere ? Or use C11 atomic_store relaxed. | |||||
Done Inline ActionsNot sure if that's needed, given that mmap() and munmap() are not magical the way malloc() and free() are, but I'm adding it anyway. jilles: Not sure if that's needed, given that `mmap()` and `munmap()` are not magical the way `malloc… | |||||
ATF_CHECK_EQ(SIGSEGV, last_sig); | |||||
ATF_CHECK_EQ(SEGV_MAPERR, last_code); | |||||
Not Done Inline ActionsI wonder if we want to be more formal there, and add a fence before accesses to last_sig and last_code. kib: I wonder if we want to be more formal there, and add a fence before accesses to last_sig and… | |||||
Done Inline Actionslast_sig and last_code are volatile, which should suffice for communication with a signal handler in the same thread. jilles: `last_sig` and `last_code` are volatile, which should suffice for communication with a signal… | |||||
Not Done Inline Actionsvolatile semantic for gcc and clang only guarantee that the access occurs, it does not provide any guarantee when. kib: volatile semantic for gcc and clang only guarantee that the access occurs, it does not provide… | |||||
} | |||||
ATF_TC_WITHOUT_HEAD(page_fault_signal__segv_accerr_1); | |||||
ATF_TC_BODY(page_fault_signal__segv_accerr_1, tc) | |||||
{ | |||||
int *p; | |||||
int sz; | |||||
sz = getpagesize(); | |||||
p = mmap(NULL, sz, PROT_READ, MAP_ANON, -1, 0); | |||||
ATF_REQUIRE(p != MAP_FAILED); | |||||
if (sigsetjmp(sig_env, 1) == 0) { | |||||
setup_signals(); | |||||
*(volatile int *)p = 1; | |||||
} | |||||
(void)munmap(p, sz); | |||||
ATF_CHECK_EQ(SIGSEGV, last_sig); | |||||
ATF_CHECK_EQ(SEGV_ACCERR, last_code); | |||||
} | |||||
ATF_TC_WITHOUT_HEAD(page_fault_signal__segv_accerr_2); | |||||
ATF_TC_BODY(page_fault_signal__segv_accerr_2, tc) | |||||
{ | |||||
int *p; | |||||
volatile int dummy; | |||||
int sz; | |||||
sz = getpagesize(); | |||||
p = mmap(NULL, sz, PROT_NONE, MAP_ANON, -1, 0); | |||||
ATF_REQUIRE(p != MAP_FAILED); | |||||
if (sigsetjmp(sig_env, 1) == 0) { | |||||
setup_signals(); | |||||
dummy = *p; | |||||
} | |||||
(void)munmap(p, sz); | |||||
ATF_CHECK_EQ(SIGSEGV, last_sig); | |||||
ATF_CHECK_EQ(SEGV_ACCERR, last_code); | |||||
} | |||||
ATF_TC_WITHOUT_HEAD(page_fault_signal__bus_objerr_1); | |||||
ATF_TC_BODY(page_fault_signal__bus_objerr_1, tc) | |||||
{ | |||||
int *p; | |||||
int fd; | |||||
int sz; | |||||
atf_tc_expect_fail("bug 211924"); | |||||
sz = getpagesize(); | |||||
fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, 0600); | |||||
ATF_REQUIRE(fd != -1); | |||||
p = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |||||
ATF_REQUIRE(p != MAP_FAILED); | |||||
if (sigsetjmp(sig_env, 1) == 0) { | |||||
setup_signals(); | |||||
*(volatile int *)p = 1; | |||||
} | |||||
Done Inline ActionsDon't you need to truncate shmfd before access ? kib: Don't you need to truncate shmfd before access ? | |||||
Done Inline ActionsI see, you did not extended shmfd at all. I think it would be more interesting to ensure that at least one page is allowed. kib: I see, you did not extended shmfd at all. I think it would be more interesting to ensure that… | |||||
(void)munmap(p, sz); | |||||
(void)close(fd); | |||||
ATF_CHECK_EQ(SIGBUS, last_sig); | |||||
ATF_CHECK_EQ(BUS_OBJERR, last_code); | |||||
} | |||||
ATF_TC_WITHOUT_HEAD(page_fault_signal__bus_objerr_2); | |||||
ATF_TC_BODY(page_fault_signal__bus_objerr_2, tc) | |||||
{ | |||||
int *p; | |||||
int fd; | |||||
int r; | |||||
int sz; | |||||
atf_tc_expect_fail("bug 211924"); | |||||
sz = getpagesize(); | |||||
fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, 0600); | |||||
ATF_REQUIRE(fd != -1); | |||||
r = ftruncate(fd, sz); | |||||
ATF_REQUIRE(r != -1); | |||||
p = mmap(NULL, sz * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |||||
ATF_REQUIRE(p != MAP_FAILED); | |||||
if (sigsetjmp(sig_env, 1) == 0) { | |||||
setup_signals(); | |||||
((volatile int *)p)[sz / sizeof(int)] = 1; | |||||
} | |||||
(void)munmap(p, sz * 2); | |||||
(void)close(fd); | |||||
ATF_CHECK_EQ(SIGBUS, last_sig); | |||||
ATF_CHECK_EQ(BUS_OBJERR, last_code); | |||||
} | |||||
ATF_TP_ADD_TCS(tp) | |||||
{ | |||||
ATF_TP_ADD_TC(tp, page_fault_signal__segv_maperr_1); | |||||
ATF_TP_ADD_TC(tp, page_fault_signal__segv_accerr_1); | |||||
ATF_TP_ADD_TC(tp, page_fault_signal__segv_accerr_2); | |||||
ATF_TP_ADD_TC(tp, page_fault_signal__bus_objerr_1); | |||||
ATF_TP_ADD_TC(tp, page_fault_signal__bus_objerr_2); | |||||
return (atf_no_error()); | |||||
} |
Why is this sigfillset needed ?