1/*
2 * Copyright © 2015 Raspberry Pi Foundation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  The copyright holders make no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
19 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
20 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21 * SOFTWARE.
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <pixman-config.h>
27#endif
28
29#include "utils.h"
30
31
32#if FENCE_MALLOC_ACTIVE && defined (HAVE_SIGACTION)
33
34#include <stdlib.h>
35#include <stdio.h>
36#include <stdarg.h>
37#include <errno.h>
38#include <signal.h>
39#include <unistd.h>
40#include <sys/types.h>
41#include <sys/wait.h>
42
43pixman_bool_t verbose;
44
45static void
46segv_handler (int sig, siginfo_t *si, void *unused)
47{
48    _exit (EXIT_SUCCESS);
49}
50
51static void
52die (const char *msg, int err)
53{
54    if (err)
55        perror (msg);
56    else
57        fprintf (stderr, "%s\n", msg);
58
59    abort ();
60}
61
62static void
63prinfo (const char *fmt, ...)
64{
65    va_list ap;
66
67    if (!verbose)
68        return;
69
70    va_start (ap, fmt);
71    vfprintf (stderr, fmt, ap);
72    va_end (ap);
73}
74
75static void
76do_expect_signal (void (*fn)(void *), void *data)
77{
78    struct sigaction sa;
79
80    sa.sa_flags = SA_SIGINFO;
81    sigemptyset (&sa.sa_mask);
82    sa.sa_sigaction = segv_handler;
83    if (sigaction (SIGSEGV, &sa, NULL) == -1)
84        die ("sigaction failed", errno);
85    if (sigaction (SIGBUS, &sa, NULL) == -1)
86        die ("sigaction failed", errno);
87
88    (*fn)(data);
89
90    _exit (EXIT_FAILURE);
91}
92
93/* Check that calling fn(data) causes a segmentation fault.
94 *
95 * You cannot portably return from a SIGSEGV handler in any way,
96 * so we fork, and do the test in the child process. Child's
97 * exit status will reflect the result. Its SEGV handler causes it
98 * to exit with success, and return failure otherwise.
99 */
100static pixman_bool_t
101expect_signal (void (*fn)(void *), void *data)
102{
103    pid_t pid, wp;
104    int status;
105
106    pid = fork ();
107    if (pid == -1)
108        die ("fork failed", errno);
109
110    if (pid == 0)
111        do_expect_signal (fn, data); /* never returns */
112
113    wp = waitpid (pid, &status, 0);
114    if (wp != pid)
115        die ("waitpid did not work", wp == -1 ? errno : 0);
116
117    if (WIFEXITED (status) && WEXITSTATUS (status) == EXIT_SUCCESS)
118        return TRUE;
119
120    return FALSE;
121}
122
123static void
124read_u8 (void *data)
125{
126    volatile uint8_t *p = data;
127
128    *p;
129}
130
131static pixman_bool_t
132test_read_fault (uint8_t *p, int offset)
133{
134    prinfo ("*(uint8_t *)(%p + %d)", p, offset);
135
136    if (expect_signal (read_u8, p + offset))
137    {
138        prinfo ("\tsignal OK\n");
139
140        return TRUE;
141    }
142
143    prinfo ("\tFAILED\n");
144
145    return FALSE;
146}
147
148static void
149test_read_ok (uint8_t *p, int offset)
150{
151    prinfo ("*(uint8_t *)(%p + %d)", p, offset);
152
153    /* If fails, SEGV. */
154    read_u8 (p + offset);
155
156    prinfo ("\tOK\n");
157}
158
159static pixman_bool_t
160test_read_faults (pixman_image_t *image)
161{
162    pixman_bool_t ok = TRUE;
163    pixman_format_code_t format = pixman_image_get_format (image);
164    int width = pixman_image_get_width (image);
165    int height = pixman_image_get_height (image);
166    int stride = pixman_image_get_stride (image);
167    uint8_t *p = (void *)pixman_image_get_data (image);
168    int row_bytes = width * PIXMAN_FORMAT_BPP (format) / 8;
169
170    prinfo ("%s %dx%d, row %d B, stride %d B:\n",
171            format_name (format), width, height, row_bytes, stride);
172
173    assert (height > 3);
174
175    test_read_ok (p, 0);
176    test_read_ok (p, row_bytes - 1);
177    test_read_ok (p, stride);
178    test_read_ok (p, stride + row_bytes - 1);
179    test_read_ok (p, 2 * stride);
180    test_read_ok (p, 2 * stride + row_bytes - 1);
181    test_read_ok (p, 3 * stride);
182    test_read_ok (p, (height - 1) * stride + row_bytes - 1);
183
184    ok &= test_read_fault (p, -1);
185    ok &= test_read_fault (p, row_bytes);
186    ok &= test_read_fault (p, stride - 1);
187    ok &= test_read_fault (p, stride + row_bytes);
188    ok &= test_read_fault (p, 2 * stride - 1);
189    ok &= test_read_fault (p, 2 * stride + row_bytes);
190    ok &= test_read_fault (p, 3 * stride - 1);
191    ok &= test_read_fault (p, height * stride);
192
193    return ok;
194}
195
196static pixman_bool_t
197test_image_faults (pixman_format_code_t format, int min_width, int height)
198{
199    pixman_bool_t ok;
200    pixman_image_t *image;
201
202    image = fence_image_create_bits (format, min_width, height, TRUE);
203    ok = test_read_faults (image);
204    pixman_image_unref (image);
205
206    return ok;
207}
208
209int
210main (int argc, char **argv)
211{
212    pixman_bool_t ok = TRUE;
213
214    if (getenv ("VERBOSE") != NULL)
215        verbose = TRUE;
216
217    ok &= test_image_faults (PIXMAN_a8r8g8b8, 7, 5);
218    ok &= test_image_faults (PIXMAN_r8g8b8, 7, 5);
219    ok &= test_image_faults (PIXMAN_r5g6b5, 7, 5);
220    ok &= test_image_faults (PIXMAN_a8, 7, 5);
221    ok &= test_image_faults (PIXMAN_a4, 7, 5);
222    ok &= test_image_faults (PIXMAN_a1, 7, 5);
223
224    if (ok)
225        return EXIT_SUCCESS;
226
227    return EXIT_FAILURE;
228}
229
230#else /* FENCE_MALLOC_ACTIVE */
231
232int
233main (int argc, char **argv)
234{
235    /* Automake return code for test SKIP. */
236    return 77;
237}
238
239#endif /* FENCE_MALLOC_ACTIVE */
240