/* * 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 #endif #include "utils.h" #if FENCE_MALLOC_ACTIVE && defined (HAVE_SIGACTION) #include #include #include #include #include #include #include #include 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 */