minix/tests/lib/libc/gen/t_siginfo.c
Lionel Sambuc 11be35a165 Importing NetBSD "Kyua" test framework
To do so, a few dependencies have been imported:

 * external/bsd/lutok
 * external/mit/lua
 * external/public-domain/sqlite
 * external/public-domain/xz

The Kyua framework is the new generation of ATF (Automated Test
Framework), it is composed of:

 * external/bsd/atf
 * external/bsd/kyua-atf-compat
 * external/bsd/kyua-cli
 * external/bsd/kyua-tester
 * tests

Kyua/ATF being written in C++, it depends on libstdc++ which is
provided by GCC. As this is not part of the sources, Kyua is only
compiled when the native GCC utils are installed.

To install Kyua do the following:

 * In a cross-build enviromnent, add the following to the build.sh
   commandline: -V MKBINUTILS=yes -V MKGCCCMDS=yes

WARNING:
  At this point the import is still experimental, and not supported
  on native builds (a.k.a make build).

Change-Id: I26aee23c5bbd2d64adcb7c1beb98fe0d479d7ada
2013-07-23 20:43:41 +02:00

498 lines
12 KiB
C

/* $NetBSD: t_siginfo.c,v 1.20 2013/04/12 17:30:50 christos Exp $ */
/*-
* Copyright (c) 2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* 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 <atf-c.h>
#include <atf-c/config.h>
#include <sys/inttypes.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/ucontext.h>
#include <sys/wait.h>
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <setjmp.h>
#include <float.h>
#ifdef _FLOAT_IEEE754
#include <ieeefp.h>
#endif
#include "isqemu.h"
/* for sigbus */
volatile char *addr;
/* for sigchild */
pid_t child;
int code;
int status;
/* for sigfpe */
sig_atomic_t fltdiv_signalled = 0;
sig_atomic_t intdiv_signalled = 0;
static void
sig_debug(int signo, siginfo_t *info, ucontext_t *ctx)
{
unsigned int i;
printf("%d %p %p\n", signo, info, ctx);
if (info != NULL) {
printf("si_signo=%d\n", info->si_signo);
printf("si_errno=%d\n", info->si_errno);
printf("si_code=%d\n", info->si_code);
printf("si_value.sival_int=%d\n", info->si_value.sival_int);
}
if (ctx != NULL) {
printf("uc_flags 0x%x\n", ctx->uc_flags);
printf("uc_link %p\n", ctx->uc_link);
for (i = 0; i < __arraycount(ctx->uc_sigmask.__bits); i++)
printf("uc_sigmask[%d] 0x%x\n", i,
ctx->uc_sigmask.__bits[i]);
printf("uc_stack %p %lu 0x%x\n", ctx->uc_stack.ss_sp,
(unsigned long)ctx->uc_stack.ss_size,
ctx->uc_stack.ss_flags);
for (i = 0; i < __arraycount(ctx->uc_mcontext.__gregs); i++)
printf("uc_mcontext.greg[%d] 0x%lx\n", i,
(long)ctx->uc_mcontext.__gregs[i]);
}
}
static void
sigalrm_action(int signo, siginfo_t *info, void *ptr)
{
sig_debug(signo, info, (ucontext_t *)ptr);
ATF_REQUIRE_EQ(info->si_signo, SIGALRM);
ATF_REQUIRE_EQ(info->si_code, SI_TIMER);
ATF_REQUIRE_EQ(info->si_value.sival_int, ITIMER_REAL);
atf_tc_pass();
/* NOTREACHED */
}
ATF_TC(sigalarm);
ATF_TC_HEAD(sigalarm, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGALRM handler");
}
ATF_TC_BODY(sigalarm, tc)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sigalrm_action;
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
for (;;) {
alarm(1);
sleep(1);
}
atf_tc_fail("SIGALRM handler wasn't called");
}
static void
sigchild_action(int signo, siginfo_t *info, void *ptr)
{
if (info != NULL) {
printf("info=%p\n", info);
printf("ptr=%p\n", ptr);
printf("si_signo=%d\n", info->si_signo);
printf("si_errno=%d\n", info->si_errno);
printf("si_code=%d\n", info->si_code);
printf("si_uid=%d\n", info->si_uid);
printf("si_pid=%d\n", info->si_pid);
printf("si_status=%d\n", info->si_status);
printf("si_utime=%lu\n", (unsigned long int)info->si_utime);
printf("si_stime=%lu\n", (unsigned long int)info->si_stime);
}
ATF_REQUIRE_EQ(info->si_code, code);
ATF_REQUIRE_EQ(info->si_signo, SIGCHLD);
ATF_REQUIRE_EQ(info->si_uid, getuid());
ATF_REQUIRE_EQ(info->si_pid, child);
if (WIFEXITED(info->si_status))
ATF_REQUIRE_EQ(WEXITSTATUS(info->si_status), status);
else if (WIFSTOPPED(info->si_status))
ATF_REQUIRE_EQ(WSTOPSIG(info->si_status), status);
else if (WIFSIGNALED(info->si_status))
ATF_REQUIRE_EQ(WTERMSIG(info->si_status), status);
}
static void
setchildhandler(void (*action)(int, siginfo_t *, void *))
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = action;
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
}
static void
sigchild_setup(void)
{
sigset_t set;
struct rlimit rlim;
(void)getrlimit(RLIMIT_CORE, &rlim);
rlim.rlim_cur = rlim.rlim_max;
(void)setrlimit(RLIMIT_CORE, &rlim);
setchildhandler(sigchild_action);
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
sigprocmask(SIG_BLOCK, &set, NULL);
}
ATF_TC(sigchild_normal);
ATF_TC_HEAD(sigchild_normal, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGCHLD handler "
"when child exits normally");
}
ATF_TC_BODY(sigchild_normal, tc)
{
sigset_t set;
sigchild_setup();
status = 25;
code = CLD_EXITED;
switch ((child = fork())) {
case 0:
sleep(1);
exit(status);
case -1:
atf_tc_fail("fork failed");
default:
sigemptyset(&set);
sigsuspend(&set);
}
}
ATF_TC(sigchild_dump);
ATF_TC_HEAD(sigchild_dump, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGCHLD handler "
"when child segfaults");
}
ATF_TC_BODY(sigchild_dump, tc)
{
sigset_t set;
sigchild_setup();
status = SIGSEGV;
code = CLD_DUMPED;
switch ((child = fork())) {
case 0:
sleep(1);
*(volatile long *)0 = 0;
atf_tc_fail("Child did not segfault");
/* NOTREACHED */
case -1:
atf_tc_fail("fork failed");
default:
sigemptyset(&set);
sigsuspend(&set);
}
}
ATF_TC(sigchild_kill);
ATF_TC_HEAD(sigchild_kill, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGCHLD handler "
"when child is killed");
}
ATF_TC_BODY(sigchild_kill, tc)
{
sigset_t set;
sigchild_setup();
status = SIGPIPE;
code = CLD_KILLED;
switch ((child = fork())) {
case 0:
sigemptyset(&set);
sigsuspend(&set);
break;
case -1:
atf_tc_fail("fork failed");
default:
kill(child, SIGPIPE);
sigemptyset(&set);
sigsuspend(&set);
}
}
static sigjmp_buf sigfpe_flt_env;
static void
sigfpe_flt_action(int signo, siginfo_t *info, void *ptr)
{
sig_debug(signo, info, (ucontext_t *)ptr);
if (fltdiv_signalled++ != 0)
atf_tc_fail("FPE handler called more than once");
ATF_REQUIRE_EQ(info->si_signo, SIGFPE);
ATF_REQUIRE_EQ(info->si_code, FPE_FLTDIV);
ATF_REQUIRE_EQ(info->si_errno, 0);
siglongjmp(sigfpe_flt_env, 1);
}
ATF_TC(sigfpe_flt);
ATF_TC_HEAD(sigfpe_flt, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGFPE handler "
"for floating div-by-zero");
}
ATF_TC_BODY(sigfpe_flt, tc)
{
struct sigaction sa;
double d = strtod("0", NULL);
if (isQEMU())
atf_tc_skip("Test does not run correctly under QEMU");
if (strcmp(atf_config_get("atf_arch"),"powerpc") == 0)
atf_tc_skip("Test not valid on powerpc");
if (sigsetjmp(sigfpe_flt_env, 0) == 0) {
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sigfpe_flt_action;
sigemptyset(&sa.sa_mask);
sigaction(SIGFPE, &sa, NULL);
#ifdef _FLOAT_IEEE754
fpsetmask(FP_X_INV|FP_X_DZ|FP_X_OFL|FP_X_UFL|FP_X_IMP);
#endif
printf("%g\n", 1 / d);
}
if (fltdiv_signalled == 0)
atf_tc_fail("FPE signal handler was not invoked");
}
static sigjmp_buf sigfpe_int_env;
static void
sigfpe_int_action(int signo, siginfo_t *info, void *ptr)
{
sig_debug(signo, info, (ucontext_t *)ptr);
if (intdiv_signalled++ != 0)
atf_tc_fail("INTDIV handler called more than once");
ATF_REQUIRE_EQ(info->si_signo, SIGFPE);
ATF_REQUIRE_EQ(info->si_code, FPE_INTDIV);
atf_tc_expect_pass();
ATF_REQUIRE_EQ(info->si_errno, 0);
siglongjmp(sigfpe_int_env, 1);
}
ATF_TC(sigfpe_int);
ATF_TC_HEAD(sigfpe_int, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGFPE handler "
"for integer div-by-zero (PR port-i386/43655)");
}
ATF_TC_BODY(sigfpe_int, tc)
{
struct sigaction sa;
long l = strtol("0", NULL, 10);
if (strcmp(atf_config_get("atf_arch"),"powerpc") == 0)
atf_tc_skip("Test not valid on powerpc");
if (sigsetjmp(sigfpe_int_env, 0) == 0) {
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sigfpe_int_action;
sigemptyset(&sa.sa_mask);
sigaction(SIGFPE, &sa, NULL);
#ifdef _FLOAT_IEEE754
fpsetmask(FP_X_INV|FP_X_DZ|FP_X_OFL|FP_X_UFL|FP_X_IMP);
#endif
printf("%ld\n", 1 / l);
}
if (intdiv_signalled == 0)
atf_tc_fail("FPE signal handler was not invoked");
}
static void
sigsegv_action(int signo, siginfo_t *info, void *ptr)
{
sig_debug(signo, info, (ucontext_t *)ptr);
ATF_REQUIRE_EQ(info->si_signo, SIGSEGV);
ATF_REQUIRE_EQ(info->si_errno, 0);
ATF_REQUIRE_EQ(info->si_code, SEGV_MAPERR);
ATF_REQUIRE_EQ(info->si_addr, (void *)0);
atf_tc_pass();
/* NOTREACHED */
}
ATF_TC(sigsegv);
ATF_TC_HEAD(sigsegv, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGSEGV handler");
}
ATF_TC_BODY(sigsegv, tc)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sigsegv_action;
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
*(volatile long *)0 = 0;
atf_tc_fail("Test did not fault as expected");
}
static void
sigbus_action(int signo, siginfo_t *info, void *ptr)
{
printf("si_addr = %p\n", info->si_addr);
sig_debug(signo, info, (ucontext_t *)ptr);
ATF_REQUIRE_EQ(info->si_signo, SIGBUS);
ATF_REQUIRE_EQ(info->si_errno, 0);
ATF_REQUIRE_EQ(info->si_code, BUS_ADRALN);
if (strcmp(atf_config_get("atf_arch"), "i386") == 0 ||
strcmp(atf_config_get("atf_arch"), "x86_64") == 0) {
atf_tc_expect_fail("x86 architecture does not correctly "
"report the address where the unaligned access occured");
}
ATF_REQUIRE_EQ(info->si_addr, (volatile void *)addr);
atf_tc_pass();
/* NOTREACHED */
}
ATF_TC(sigbus_adraln);
ATF_TC_HEAD(sigbus_adraln, tc)
{
atf_tc_set_md_var(tc, "descr",
"Checks that signal trampoline correctly calls SIGBUS handler "
"for invalid address alignment");
}
ATF_TC_BODY(sigbus_adraln, tc)
{
const char *arch = atf_config_get("atf_arch");
struct sigaction sa;
if (strcmp(arch, "alpha") == 0) {
int rv, val;
size_t len = sizeof(val);
rv = sysctlbyname("machdep.unaligned_sigbus", &val, &len,
NULL, 0);
ATF_REQUIRE(rv == 0);
if (val == 0)
atf_tc_skip("SIGBUS signal not enabled for"
" unaligned accesses");
}
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = sigbus_action;
sigemptyset(&sa.sa_mask);
sigaction(SIGBUS, &sa, NULL);
/* Enable alignement checks for x86. 0x40000 is PSL_AC. */
#if defined(__i386__)
__asm__("pushf; orl $0x40000, (%esp); popf");
#elif defined(__amd64__)
__asm__("pushf; orl $0x40000, (%rsp); popf");
#endif
addr = calloc(2, sizeof(int));
ATF_REQUIRE(addr != NULL);
if (isQEMU())
atf_tc_expect_fail("QEMU fails to trap unaligned accesses");
/* Force an unaligned access */
addr++;
printf("now trying to access unaligned address %p\n", addr);
ATF_REQUIRE_EQ(*(volatile int *)addr, 0);
atf_tc_fail("Test did not fault as expected");
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, sigalarm);
ATF_TP_ADD_TC(tp, sigchild_normal);
ATF_TP_ADD_TC(tp, sigchild_dump);
ATF_TP_ADD_TC(tp, sigchild_kill);
ATF_TP_ADD_TC(tp, sigfpe_flt);
ATF_TP_ADD_TC(tp, sigfpe_int);
ATF_TP_ADD_TC(tp, sigsegv);
ATF_TP_ADD_TC(tp, sigbus_adraln);
return atf_no_error();
}