blob: c80b3cf310c8132fca2147d9df874fe1469f6b74 [file] [log] [blame]
/*
* Copyright © 2015 Raspberry Pi Foundation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. The copyright holders make no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "utils.h"
#if FENCE_MALLOC_ACTIVE && defined (HAVE_SIGACTION)
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
pixman_bool_t verbose;
static void
segv_handler (int sig, siginfo_t *si, void *unused)
{
_exit (EXIT_SUCCESS);
}
static void
die (const char *msg, int err)
{
if (err)
perror (msg);
else
fprintf (stderr, "%s\n", msg);
abort ();
}
static void
prinfo (const char *fmt, ...)
{
va_list ap;
if (!verbose)
return;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
}
static void
do_expect_signal (void (*fn)(void *), void *data)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset (&sa.sa_mask);
sa.sa_sigaction = segv_handler;
if (sigaction (SIGSEGV, &sa, NULL) == -1)
die ("sigaction failed", errno);
if (sigaction (SIGBUS, &sa, NULL) == -1)
die ("sigaction failed", errno);
(*fn)(data);
_exit (EXIT_FAILURE);
}
/* Check that calling fn(data) causes a segmentation fault.
*
* You cannot portably return from a SIGSEGV handler in any way,
* so we fork, and do the test in the child process. Child's
* exit status will reflect the result. Its SEGV handler causes it
* to exit with success, and return failure otherwise.
*/
static pixman_bool_t
expect_signal (void (*fn)(void *), void *data)
{
pid_t pid, wp;
int status;
pid = fork ();
if (pid == -1)
die ("fork failed", errno);
if (pid == 0)
do_expect_signal (fn, data); /* never returns */
wp = waitpid (pid, &status, 0);
if (wp != pid)
die ("waitpid did not work", wp == -1 ? errno : 0);
if (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_SUCCESS)
return TRUE;
return FALSE;
}
static void
read_u8 (void *data)
{
volatile uint8_t *p = data;
*p;
}
static pixman_bool_t
test_read_fault (uint8_t *p, int offset)
{
prinfo ("*(uint8_t *)(%p + %d)", p, offset);
if (expect_signal (read_u8, p + offset))
{
prinfo ("\tsignal OK\n");
return TRUE;
}
prinfo ("\tFAILED\n");
return FALSE;
}
static void
test_read_ok (uint8_t *p, int offset)
{
prinfo ("*(uint8_t *)(%p + %d)", p, offset);
/* If fails, SEGV. */
read_u8 (p + offset);
prinfo ("\tOK\n");
}
static pixman_bool_t
test_read_faults (pixman_image_t *image)
{
pixman_bool_t ok = TRUE;
pixman_format_code_t format = pixman_image_get_format (image);
int width = pixman_image_get_width (image);
int height = pixman_image_get_height (image);
int stride = pixman_image_get_stride (image);
uint8_t *p = (void *)pixman_image_get_data (image);
int row_bytes = width * PIXMAN_FORMAT_BPP (format) / 8;
prinfo ("%s %dx%d, row %d B, stride %d B:\n",
format_name (format), width, height, row_bytes, stride);
assert (height > 3);
test_read_ok (p, 0);
test_read_ok (p, row_bytes - 1);
test_read_ok (p, stride);
test_read_ok (p, stride + row_bytes - 1);
test_read_ok (p, 2 * stride);
test_read_ok (p, 2 * stride + row_bytes - 1);
test_read_ok (p, 3 * stride);
test_read_ok (p, (height - 1) * stride + row_bytes - 1);
ok &= test_read_fault (p, -1);
ok &= test_read_fault (p, row_bytes);
ok &= test_read_fault (p, stride - 1);
ok &= test_read_fault (p, stride + row_bytes);
ok &= test_read_fault (p, 2 * stride - 1);
ok &= test_read_fault (p, 2 * stride + row_bytes);
ok &= test_read_fault (p, 3 * stride - 1);
ok &= test_read_fault (p, height * stride);
return ok;
}
static pixman_bool_t
test_image_faults (pixman_format_code_t format, int min_width, int height)
{
pixman_bool_t ok;
pixman_image_t *image;
image = fence_image_create_bits (format, min_width, height, TRUE);
ok = test_read_faults (image);
pixman_image_unref (image);
return ok;
}
int
main (int argc, char **argv)
{
pixman_bool_t ok = TRUE;
if (getenv ("VERBOSE") != NULL)
verbose = TRUE;
ok &= test_image_faults (PIXMAN_a8r8g8b8, 7, 5);
ok &= test_image_faults (PIXMAN_r8g8b8, 7, 5);
ok &= test_image_faults (PIXMAN_r5g6b5, 7, 5);
ok &= test_image_faults (PIXMAN_a8, 7, 5);
ok &= test_image_faults (PIXMAN_a4, 7, 5);
ok &= test_image_faults (PIXMAN_a1, 7, 5);
if (ok)
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
#else /* FENCE_MALLOC_ACTIVE */
int
main (int argc, char **argv)
{
/* Automake return code for test SKIP. */
return 77;
}
#endif /* FENCE_MALLOC_ACTIVE */