diff options
author | Pekka Paalanen <pekka.paalanen@collabora.co.uk> | 2015-05-07 17:16:05 +0300 |
---|---|---|
committer | Pekka Paalanen <pekka.paalanen@collabora.co.uk> | 2015-09-03 14:00:32 +0300 |
commit | 07006853828a59b5e0cd7d7d058d03db4e23e6ec (patch) | |
tree | e28907c364cf8794691221592d708951e2fb83d5 | |
parent | 13d93aa12050ce99643d56b0c730404294f46c2f (diff) |
test: add fence-image-self-test
Tests that fence_malloc and fence_image_create_bits actually work: that
out-of-bounds and out-of-row (unused stride area) accesses trigger
SIGSEGV.
If fence_malloc is a dummy (FENCE_MALLOC_ACTIVE not defined), this test
is skipped.
Changes in v2:
- check FENCE_MALLOC_ACTIVE value, not whether it is defined
- test that reading bytes near the fence pages does not cause a
segmentation fault
Changes in v3:
- Do not print progress messages unless VERBOSE environment variable is
set. Avoid spamming the terminal output of 'make check' on some
versions of autotools.
Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Ben Avison <bavison@riscosopen.org>
-rw-r--r-- | test/Makefile.sources | 1 | ||||
-rw-r--r-- | test/fence-image-self-test.c | 237 |
2 files changed, 238 insertions, 0 deletions
diff --git a/test/Makefile.sources b/test/Makefile.sources index d0d7928..dd7582a 100644 --- a/test/Makefile.sources +++ b/test/Makefile.sources @@ -3,6 +3,7 @@ TESTPROGRAMS = \ oob-test \ infinite-loop \ trap-crasher \ + fence-image-self-test \ region-translate-test \ fetch-test \ a1-trap-test \ diff --git a/test/fence-image-self-test.c b/test/fence-image-self-test.c new file mode 100644 index 0000000..c883038 --- /dev/null +++ b/test/fence-image-self-test.c @@ -0,0 +1,237 @@ +/* + * 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_segv (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); + + (*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_segv (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_segv (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_segv (read_u8, p + offset)) + { + prinfo ("\tSEGV 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 */ |