| /* |
| * 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 */ |