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 <[email protected]>
26 * Daniel Vetter <[email protected]>
27 * Rafael Barbalho <[email protected]>
28 *
29 */
30
31 #include "igt.h"
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <fcntl.h>
37 #include <inttypes.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <malloc.h>
41 #include <limits.h>
42 #include <sys/ioctl.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include "drm.h"
47
48 IGT_TEST_DESCRIPTION("Check that kernel relocation overflows are caught.");
49
50 /*
51 * Testcase: Kernel relocation overflows are caught.
52 */
53
54 int fd, entries, num;
55 struct drm_i915_gem_exec_object2 *obj;
56 struct drm_i915_gem_execbuffer2 execbuf;
57 struct drm_i915_gem_relocation_entry *reloc;
58
target_handle(void)59 static uint32_t target_handle(void)
60 {
61 return execbuf.flags & I915_EXEC_HANDLE_LUT ? 0 : obj[0].handle;
62 }
63
source_offset_tests(int devid,bool reloc_gtt)64 static void source_offset_tests(int devid, bool reloc_gtt)
65 {
66 struct drm_i915_gem_relocation_entry single_reloc;
67 const char *relocation_type;
68
69 if (reloc_gtt)
70 relocation_type = "reloc-gtt";
71 else
72 relocation_type = "reloc-cpu";
73
74 igt_fixture {
75 obj[1].relocation_count = 0;
76 obj[1].relocs_ptr = 0;
77
78 obj[0].relocation_count = 1;
79 obj[0].relocs_ptr = to_user_pointer(&single_reloc);
80 execbuf.buffer_count = 2;
81
82 if (reloc_gtt) {
83 gem_set_domain(fd, obj[0].handle, I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
84 relocation_type = "reloc-gtt";
85 } else {
86 gem_set_domain(fd, obj[0].handle, I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
87 relocation_type = "reloc-cpu";
88 }
89 }
90
91 /* Special tests for 64b relocs. */
92 igt_subtest_f("source-offset-page-stradle-gen8-%s", relocation_type) {
93 igt_require(intel_gen(devid) >= 8);
94 single_reloc.offset = 4096 - 4;
95 single_reloc.delta = 0;
96 single_reloc.target_handle = target_handle();
97 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
98 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
99 single_reloc.presumed_offset = -1;
100 gem_execbuf(fd, &execbuf);
101
102 single_reloc.delta = 1024;
103 gem_execbuf(fd, &execbuf);
104 }
105
106 igt_subtest_f("source-offset-end-gen8-%s", relocation_type) {
107 igt_require(intel_gen(devid) >= 8);
108 single_reloc.offset = 8192 - 8;
109 single_reloc.delta = 0;
110 single_reloc.target_handle = target_handle();
111 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
112 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
113 single_reloc.presumed_offset = -1;
114 gem_execbuf(fd, &execbuf);
115 }
116
117 igt_subtest_f("source-offset-overflow-gen8-%s", relocation_type) {
118 igt_require(intel_gen(devid) >= 8);
119 single_reloc.offset = 8192 - 4;
120 single_reloc.delta = 0;
121 single_reloc.target_handle = target_handle();
122 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
123 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
124 single_reloc.presumed_offset = -1;
125 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
126 }
127
128 /* Tests for old 4byte relocs on pre-gen8. */
129 igt_subtest_f("source-offset-end-%s", relocation_type) {
130 igt_require(intel_gen(devid) < 8);
131 single_reloc.offset = 8192 - 4;
132 single_reloc.delta = 0;
133 single_reloc.target_handle = target_handle();
134 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
135 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
136 single_reloc.presumed_offset = -1;
137 gem_execbuf(fd, &execbuf);
138 }
139
140 igt_subtest_f("source-offset-big-%s", relocation_type) {
141 single_reloc.offset = 8192;
142 single_reloc.delta = 0;
143 single_reloc.target_handle = target_handle();
144 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
145 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
146 single_reloc.presumed_offset = -1;
147 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
148 }
149
150 igt_subtest_f("source-offset-negative-%s", relocation_type) {
151 single_reloc.offset = (int64_t) -4;
152 single_reloc.delta = 0;
153 single_reloc.target_handle = target_handle();
154 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
155 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
156 single_reloc.presumed_offset = -1;
157 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
158 }
159
160 igt_subtest_f("source-offset-unaligned-%s", relocation_type) {
161 single_reloc.offset = 1;
162 single_reloc.delta = 0;
163 single_reloc.target_handle = target_handle();
164 single_reloc.read_domains = I915_GEM_DOMAIN_RENDER;
165 single_reloc.write_domain = I915_GEM_DOMAIN_RENDER;
166 single_reloc.presumed_offset = -1;
167 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
168 }
169 }
170
reloc_tests(const char * suffix)171 static void reloc_tests(const char *suffix)
172 {
173 uint64_t max_relocations;
174 int i;
175
176 max_relocations = min(ULONG_MAX, SIZE_MAX);
177 max_relocations /= sizeof(struct drm_i915_gem_relocation_entry);
178 igt_debug("Maximum allocable relocations: %'llu\n",
179 (long long)max_relocations);
180
181 igt_subtest_f("invalid-address%s", suffix) {
182 /* Attempt unmapped single entry. */
183 obj[0].relocation_count = 1;
184 obj[0].relocs_ptr = 0;
185 execbuf.buffer_count = 1;
186
187 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
188 }
189
190 igt_subtest_f("single-fault%s", suffix) {
191 obj[0].relocation_count = entries + 1;
192 execbuf.buffer_count = 1;
193
194 /* out-of-bounds after */
195 obj[0].relocs_ptr = to_user_pointer(reloc);
196 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
197
198 /* out-of-bounds before */
199 obj[0].relocs_ptr = to_user_pointer((reloc - 1));
200 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
201 }
202
203 igt_fixture {
204 obj[0].relocation_count = 0;
205 obj[0].relocs_ptr = 0;
206
207 execbuf.buffer_count = 1;
208
209 /* Make sure the batch would succeed except for the thing we're
210 * testing. */
211 igt_require(__gem_execbuf(fd, &execbuf) == 0);
212 }
213
214 igt_subtest_f("batch-start-unaligned%s", suffix) {
215 execbuf.batch_start_offset = 1;
216 execbuf.batch_len = 8;
217 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
218 }
219
220 igt_subtest_f("batch-end-unaligned%s", suffix) {
221 execbuf.batch_start_offset = 0;
222 execbuf.batch_len = 7;
223 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
224 }
225
226 igt_subtest_f("batch-both-unaligned%s", suffix) {
227 execbuf.batch_start_offset = 1;
228 execbuf.batch_len = 7;
229 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
230 }
231
232 igt_fixture {
233 /* Undo damage for next tests. */
234 execbuf.batch_start_offset = 0;
235 execbuf.batch_len = 0;
236 igt_require(__gem_execbuf(fd, &execbuf) == 0);
237 }
238
239 igt_subtest_f("single-overflow%s", suffix) {
240 if (*suffix) {
241 igt_require_f(intel_get_avail_ram_mb() >
242 sizeof(struct drm_i915_gem_relocation_entry) * entries / (1024*1024),
243 "Test requires at least %'llu MiB, but only %'llu MiB of RAM available\n",
244 (long long)sizeof(struct drm_i915_gem_relocation_entry) * entries / (1024*1024),
245 (long long)intel_get_avail_ram_mb());
246 }
247
248 obj[0].relocs_ptr = to_user_pointer(reloc);
249 obj[0].relocation_count = entries;
250 execbuf.buffer_count = 1;
251 gem_execbuf(fd, &execbuf);
252
253 /* Attempt single overflowed entry. */
254 obj[0].relocation_count = -1;
255 igt_debug("relocation_count=%u\n",
256 obj[0].relocation_count);
257 if (max_relocations <= obj[0].relocation_count)
258 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
259 else
260 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
261
262 if (max_relocations + 1 < obj[0].relocation_count) {
263 obj[0].relocation_count = max_relocations + 1;
264 igt_debug("relocation_count=%u\n",
265 obj[0].relocation_count);
266 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
267
268 obj[0].relocation_count = max_relocations - 1;
269 igt_debug("relocation_count=%u\n",
270 obj[0].relocation_count);
271 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
272 }
273 }
274
275 igt_subtest_f("wrapped-overflow%s", suffix) {
276 if (*suffix) {
277 igt_require_f(intel_get_avail_ram_mb() >
278 sizeof(struct drm_i915_gem_relocation_entry) * entries * num / (1024*1024),
279 "Test requires at least %'llu MiB, but only %'llu MiB of RAM available\n",
280 (long long)sizeof(struct drm_i915_gem_relocation_entry) * entries * num / (1024*1024),
281 (long long)intel_get_avail_ram_mb());
282 }
283
284 for (i = 0; i < num; i++) {
285 struct drm_i915_gem_exec_object2 *o = &obj[i];
286
287 o->relocs_ptr = to_user_pointer(reloc);
288 o->relocation_count = entries;
289 }
290 execbuf.buffer_count = i;
291 gem_execbuf(fd, &execbuf);
292
293 obj[i-1].relocation_count = -1;
294 igt_debug("relocation_count[%d]=%u\n",
295 i-1, obj[i-1].relocation_count);
296 if (max_relocations <= obj[i-1].relocation_count)
297 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
298 else
299 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
300
301 if (max_relocations < obj[i-1].relocation_count) {
302 obj[i-1].relocation_count = max_relocations;
303 igt_debug("relocation_count[%d]=%u\n",
304 i-1, obj[i-1].relocation_count);
305 /* Whether the kernel reports the EFAULT for the
306 * invalid relocation array or EINVAL for the overflow
307 * in array size depends upon the order of the
308 * individual tests. From a consistency perspective
309 * EFAULT is preferred (i.e. using that relocation
310 * array by itself would cause EFAULT not EINVAL).
311 */
312 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
313
314 obj[i-1].relocation_count = max_relocations - 1;
315 igt_debug("relocation_count[%d]=%u\n",
316 i-1, obj[i-1].relocation_count);
317 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
318 }
319
320 obj[i-1].relocation_count = entries + 1;
321 igt_debug("relocation_count[%d]=%u\n",
322 i-1, obj[i-1].relocation_count);
323 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
324
325 obj[0].relocation_count = -1;
326 if (max_relocations < obj[0].relocation_count) {
327 execbuf.buffer_count = 1;
328 gem_execbuf(fd, &execbuf);
329
330 /* As outlined above, this is why EFAULT is preferred */
331 obj[0].relocation_count = max_relocations;
332 igt_debug("relocation_count[0]=%u\n",
333 obj[0].relocation_count);
334 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EFAULT);
335 }
336 }
337 }
338
buffer_count_tests(void)339 static void buffer_count_tests(void)
340 {
341 igt_subtest("buffercount-overflow") {
342 igt_skip_on(SIZE_MAX / sizeof(*obj) >= UINT_MAX);
343
344 for (int i = 0; i < num; i++) {
345 obj[i].relocation_count = 0;
346 obj[i].relocs_ptr = 0;
347 }
348
349 /* We only have num buffers actually, but the overflow will make
350 * sure we blow up the kernel before we blow up userspace. */
351 execbuf.buffer_count = num;
352
353 /* Make sure the basic thing would work first ... */
354 gem_execbuf(fd, &execbuf);
355
356 /* ... then be evil: Overflow of the pointer table (which has a
357 * bit of lead datastructures, so no + 1 needed to overflow). */
358 execbuf.buffer_count = INT_MAX / sizeof(void *);
359 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
360
361 /* ... then be evil: Copying/allocating the array. */
362 execbuf.buffer_count = UINT_MAX / sizeof(obj[0]) + 1;
363 igt_assert_eq(__gem_execbuf(fd, &execbuf), -EINVAL);
364 }
365 }
366
367 igt_main
368 {
369 int devid = 0;
370
371 igt_fixture {
372 uint32_t bbe = MI_BATCH_BUFFER_END;
373 size_t reloc_size;
374
375 fd = drm_open_driver(DRIVER_INTEL);
376 igt_require_gem(fd);
377 devid = intel_get_drm_devid(fd);
378
379 /* Create giant reloc buffer area. */
380 num = 257;
381 entries = ((1ULL << 32) / (num - 1));
382 reloc_size = entries * sizeof(struct drm_i915_gem_relocation_entry);
383 igt_assert((reloc_size & 4095) == 0);
384 reloc = mmap(NULL, reloc_size + 2*4096, PROT_READ | PROT_WRITE,
385 MAP_PRIVATE | MAP_ANON, -1, 0);
386 igt_assert(reloc != MAP_FAILED);
387 igt_require_f(mlock(reloc, reloc_size) == 0,
388 "Tests require at least %'llu MiB of available memory\n",
389 (long long unsigned)reloc_size / (1024*1024));
390
391 /* disable access before + after */
392 mprotect(reloc, 4096, 0);
393 reloc = (struct drm_i915_gem_relocation_entry *)((char *)reloc + 4096);
394 mprotect(reloc + entries, 4096, 0);
395
396 /* Allocate the handles we'll need to wrap. */
397 intel_require_memory(num+1, 4096, CHECK_RAM);
398 obj = calloc(num, sizeof(*obj));
399 igt_assert(obj);
400
401 /* First object is used for page crossing tests */
402 obj[0].handle = gem_create(fd, 8192);
403 gem_write(fd, obj[0].handle, 0, &bbe, sizeof(bbe));
404 for (int i = 1; i < num; i++) {
405 obj[i].handle = gem_create(fd, 4096);
406 gem_write(fd, obj[i].handle, 0, &bbe, sizeof(bbe));
407 }
408
409 /* Create relocation objects. */
410 memset(&execbuf, 0, sizeof(execbuf));
411 execbuf.buffers_ptr = to_user_pointer(obj);
412 execbuf.buffer_count = 1;
413 execbuf.flags = I915_EXEC_HANDLE_LUT;
414 if (__gem_execbuf(fd, &execbuf))
415 execbuf.flags = 0;
416
417 for (int i = 0; i < entries; i++) {
418 reloc[i].target_handle = target_handle();
419 reloc[i].offset = 1024;
420 reloc[i].read_domains = I915_GEM_DOMAIN_INSTRUCTION;
421 reloc[i].write_domain = 0;
422 }
423 }
424
425 reloc_tests("");
426 igt_fixture
427 igt_disable_prefault();
428 reloc_tests("-noprefault");
429 igt_fixture
430 igt_enable_prefault();
431
432 source_offset_tests(devid, false);
433 source_offset_tests(devid, true);
434
435 buffer_count_tests();
436 }
437