tests/gem_reloc_overflow: Add gen8+ specifc tests
[platform/upstream/intel-gpu-tools.git] / tests / gem_reloc_overflow.c
1 /*
2  * Copyright © 2013 Google
3  * Copyright © 2013 Intel Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Kees Cook <keescook@chromium.org>
26  *    Daniel Vetter <daniel.vetter@ffwll.ch>
27  *    Rafael Barbalho <rafael.barbalho@intel.com>
28  *
29  */
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <stdint.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <inttypes.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <malloc.h>
40 #include <limits.h>
41 #include <sys/ioctl.h>
42 #include <sys/stat.h>
43 #include <sys/time.h>
44 #include <sys/types.h>
45 #include "drm.h"
46 #include "i915_drm.h"
47 #include "drmtest.h"
48 #include "intel_gpu_tools.h"
49
50 /*
51  * Testcase: Kernel relocation overflows are caught.
52  */
53
54 int fd, entries, num;
55 size_t reloc_size;
56 uint32_t *handles;
57 struct drm_i915_gem_exec_object2 *execobjs;
58 struct drm_i915_gem_execbuffer2 execbuf = { 0 };
59 struct drm_i915_gem_relocation_entry *reloc;
60
61 uint32_t handle;
62 uint32_t batch_handle;
63
64 static void source_offset_tests(int devid, bool reloc_gtt)
65 {
66         struct drm_i915_gem_relocation_entry single_reloc;
67         char *dst_gtt;
68         char *relocation_type;
69
70         igt_fixture {
71                 handle = gem_create(fd, 8192);
72
73                 execobjs[1].handle = batch_handle;
74                 execobjs[1].relocation_count = 0;
75                 execobjs[1].relocs_ptr = 0;
76
77                 execobjs[0].handle = handle;
78                 execobjs[0].relocation_count = 1;
79                 execobjs[0].relocs_ptr = (uintptr_t) &single_reloc;
80                 execbuf.buffer_count = 2;
81
82                 if (reloc_gtt) {
83                         dst_gtt = gem_mmap(fd, handle, 8192, PROT_READ | PROT_WRITE);
84                         igt_assert(dst_gtt != MAP_FAILED);
85                         gem_set_domain(fd, handle, I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
86                         memset(dst_gtt, 0, 8192);
87                         munmap(dst_gtt, 8192);
88                         relocation_type = "reloc-gtt";
89                 } else {
90                         relocation_type = "reloc-cpu";
91                 }
92         }
93
94         if (intel_gen(devid) >= 8) {
95                 igt_subtest_f("source-offset-page-stradle-gen8+-%s", relocation_type) {
96                         single_reloc.offset = 4096 - 4;
97                         single_reloc.delta = 0;
98                         single_reloc.target_handle = handle;
99                         single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
100                         single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
101                         single_reloc.presumed_offset = 0;
102
103                         igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) == 0);
104                         single_reloc.delta = 1024;
105                         igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) == 0);
106                 }
107
108                 igt_subtest_f("source-offset-end-gen8+-%s", relocation_type) {
109                         single_reloc.offset = 8192 - 8;
110                         single_reloc.delta = 0;
111                         single_reloc.target_handle = handle;
112                         single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
113                         single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
114                         single_reloc.presumed_offset = 0;
115
116                         igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) == 0);
117                 }
118
119                 igt_subtest_f("source-offset-overflow-gen8+-%s", relocation_type) {
120                         single_reloc.offset = 8192 - 4;
121                         single_reloc.delta = 0;
122                         single_reloc.target_handle = handle;
123                         single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
124                         single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
125                         single_reloc.presumed_offset = 0;
126
127                         igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) != 0);
128                         igt_assert(errno == EINVAL);
129                 }
130         } else {
131                 igt_subtest_f("source-offset-end-%s", relocation_type) {
132                         single_reloc.offset = 8192 - 4;
133                         single_reloc.delta = 0;
134                         single_reloc.target_handle = handle;
135                         single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
136                         single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
137                         single_reloc.presumed_offset = 0;
138
139                         igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) == 0);
140                 }
141         }
142
143         igt_subtest_f("source-offset-big-%s", relocation_type) {
144                 single_reloc.offset = 8192;
145                 single_reloc.delta = 0;
146                 single_reloc.target_handle = handle;
147                 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
148                 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
149                 single_reloc.presumed_offset = 0;
150
151                 igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) != 0);
152                 igt_assert(errno == EINVAL);
153         }
154
155         igt_subtest_f("source-offset-negative-%s", relocation_type) {
156                 single_reloc.offset = (int64_t) -4;
157                 single_reloc.delta = 0;
158                 single_reloc.target_handle = handle;
159                 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
160                 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
161                 single_reloc.presumed_offset = 0;
162
163                 igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) != 0);
164                 igt_assert(errno == EINVAL);
165         }
166
167         igt_subtest_f("source-offset-unaligned-%s", relocation_type) {
168                 single_reloc.offset = 1;
169                 single_reloc.delta = 0;
170                 single_reloc.target_handle = handle;
171                 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
172                 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
173                 single_reloc.presumed_offset = 0;
174
175                 igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) != 0);
176                 igt_assert(errno == EINVAL);
177         }
178
179         igt_fixture {
180                 gem_close(fd, handle);
181         }
182 }
183
184 static void reloc_tests(void)
185 {
186         int i;
187         unsigned int total_unsigned = 0;
188
189         igt_subtest("invalid-address") {
190                 /* Attempt unmapped single entry. */
191                 execobjs[0].relocation_count = 1;
192                 execobjs[0].relocs_ptr = 0;
193                 execbuf.buffer_count = 1;
194
195                 errno = 0;
196                 ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
197                 igt_assert(errno == EFAULT);
198         }
199
200         igt_subtest("single-overflow") {
201                 /* Attempt single overflowed entry. */
202                 execobjs[0].relocation_count = (1 << 31);
203                 execobjs[0].relocs_ptr = (uintptr_t)reloc;
204                 execbuf.buffer_count = 1;
205
206                 errno = 0;
207                 ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
208                 igt_assert(errno == EINVAL);
209         }
210
211         igt_fixture {
212                 execobjs[0].handle = batch_handle;
213                 execobjs[0].relocation_count = 0;
214                 execobjs[0].relocs_ptr = 0;
215
216                 execbuf.buffer_count = 1;
217         }
218
219         igt_subtest("batch-start-unaligned") {
220                 execbuf.batch_start_offset = 1;
221                 execbuf.batch_len = 8;
222
223                 igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) != 0);
224                 igt_assert(errno == EINVAL);
225         }
226
227         igt_subtest("batch-end-unaligned") {
228                 execbuf.batch_start_offset = 0;
229                 execbuf.batch_len = 7;
230
231                 igt_assert(ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf) != 0);
232                 igt_assert(errno == EINVAL);
233         }
234
235         igt_fixture {
236                 /* Undo damage for next tests. */
237                 execbuf.batch_start_offset = 0;
238                 execbuf.batch_len = 8;
239         }
240
241         igt_subtest("wrapped-overflow") {
242                 /* Attempt wrapped overflow entries. */
243                 for (i = 0; i < num; i++) {
244                         struct drm_i915_gem_exec_object2 *obj = &execobjs[i];
245                         obj->handle = handles[i];
246
247                         if (i == num - 1) {
248                                 /* Wraps to 1 on last count. */
249                                 obj->relocation_count = 1 - total_unsigned;
250                                 obj->relocs_ptr = (uintptr_t)reloc;
251                         } else {
252                                 obj->relocation_count = entries;
253                                 obj->relocs_ptr = (uintptr_t)reloc;
254                         }
255
256                         total_unsigned += obj->relocation_count;
257                 }
258                 execbuf.buffer_count = num;
259
260                 errno = 0;
261                 ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
262                 igt_assert(errno == EINVAL);
263         }
264 }
265
266 static void buffer_count_tests(void)
267 {
268         igt_subtest("buffercount-overflow") {
269                 for (int i = 0; i < num; i++) {
270                         execobjs[i].relocation_count = 0;
271                         execobjs[i].relocs_ptr = 0;
272                         execobjs[i].handle = handles[i];
273                 }
274
275                 execobjs[0].relocation_count = 0;
276                 execobjs[0].relocs_ptr = 0;
277                 /* We only have num buffers actually, but the overflow will make
278                  * sure we blow up the kernel before we blow up userspace. */
279                 execbuf.buffer_count = num;
280
281                 /* Put a real batch at the end. */
282                 execobjs[num - 1].handle = batch_handle;
283
284                 /* Make sure the basic thing would work first ... */
285                 errno = 0;
286                 ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
287                 igt_assert(errno == 0);
288
289                 /* ... then be evil: Overflow of the pointer table (which has a
290                  * bit of lead datastructures, so no + 1 needed to overflow). */
291                 execbuf.buffer_count = INT_MAX / sizeof(void *);
292
293                 errno = 0;
294                 ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
295                 igt_assert(errno == EINVAL);
296
297                 /* ... then be evil: Copying/allocating the array. */
298                 execbuf.buffer_count = UINT_MAX / sizeof(execobjs[0]) + 1;
299
300                 errno = 0;
301                 ioctl(fd, DRM_IOCTL_I915_GEM_EXECBUFFER2, &execbuf);
302                 igt_assert(errno == EINVAL);
303         }
304 }
305
306 igt_main
307 {
308         int devid = 0;
309
310         igt_fixture {
311                 int ring;
312                 uint32_t batch_data [2] = { MI_NOOP, MI_BATCH_BUFFER_END };
313
314                 fd = drm_open_any();
315
316                 devid = intel_get_drm_devid(fd);
317
318                 /* Create giant reloc buffer area. */
319                 num = 257;
320                 entries = ((1ULL << 32) / (num - 1));
321                 reloc_size = entries * sizeof(struct drm_i915_gem_relocation_entry);
322                 reloc = mmap(NULL, reloc_size, PROT_READ | PROT_WRITE,
323                              MAP_PRIVATE | MAP_ANON, -1, 0);
324                 igt_assert(reloc != MAP_FAILED);
325
326                 /* Allocate the handles we'll need to wrap. */
327                 handles = calloc(num, sizeof(*handles));
328                 for (int i = 0; i < num; i++)
329                         handles[i] = gem_create(fd, 4096);
330
331                 if (intel_gen(devid) >= 6)
332                         ring = I915_EXEC_BLT;
333                 else
334                         ring = 0;
335
336                 /* Create relocation objects. */
337                 execobjs = calloc(num, sizeof(*execobjs));
338                 execbuf.buffers_ptr = (uintptr_t)execobjs;
339                 execbuf.batch_start_offset = 0;
340                 execbuf.batch_len = 8;
341                 execbuf.cliprects_ptr = 0;
342                 execbuf.num_cliprects = 0;
343                 execbuf.DR1 = 0;
344                 execbuf.DR4 = 0;
345                 execbuf.flags = ring;
346                 i915_execbuffer2_set_context_id(execbuf, 0);
347                 execbuf.rsvd2 = 0;
348
349                 batch_handle = gem_create(fd, 4096);
350
351                 gem_write(fd, batch_handle, 0, batch_data, sizeof(batch_data));
352         }
353
354         reloc_tests();
355
356         source_offset_tests(devid, false);
357         source_offset_tests(devid, true);
358
359         buffer_count_tests();
360
361         igt_fixture {
362                 gem_close(fd, batch_handle);
363                 close(fd);
364         }
365 }