Confirming this on different hardware, and I believe I have the root
cause.
System: Intel i7-3770 (Ivy Bridge, HD Graphics 4000, gen7), AnduinOS 2.0.1
(Ubuntu resolute base), kernel 7.0.0-29-generic, Mesa 26.0.3 (crocus).
Same signature as the original report - four hard freezes in three days, every
one of them with gnome-system-monitor as the faulting task:
BUG: unable to handle page fault for address: fffff65289800008
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
Oops: Oops: 0000 [#1] SMP NOPTI
CPU: 4 UID: 1000 PID: 5892 Comm: gnome-system-mo Not tainted
7.0.0-29-generic #29-Ubuntu
RIP: 0010:kfree+0x60/0x400
RBX: 00000000000002d8 R14: 0000000000000310
Call Trace:
<TASK>
? eb_parse+0x3d6/0x680 [i915]
kvfree+0x32/0x40
eb_relocate_parse_slow+0x217/0x370 [i915]
i915_gem_do_execbuffer+0x638/0x13a0 [i915]
i915_gem_execbuffer2_ioctl+0x137/0x260 [i915]
drm_ioctl_kernel+0xb5/0x110
drm_ioctl+0x309/0x5f0
__x64_sys_ioctl+0xa3/0x100
do_syscall_64+0x105/0x5a0
entry_SYSCALL_64_after_hwframe+0x76/0x7e
note: gnome-system-mo[5892] exited with irqs disabled
The kernel dies with IRQs disabled, which is why the machine is completely
unresponsive and needs a power cycle.
ROOT CAUSE
i915_gem_execbuffer2_ioctl() allocates two spare slots but copies in only
count:
exec2_list = kvmalloc_array(count + 2, eb_element_size(),
__GFP_NOWARN | GFP_KERNEL);
...
copy_from_user(exec2_list, u64_to_user_ptr(args->buffers_ptr),
sizeof(*exec2_list) * count)
The comment above that allocation explains the extra slots are "for use by the
command parser", and then argues in the same paragraph that zeroing is
unnecessary "because the exec2_list part doesn't need to be, as it's
immediately overwritten by user data a few lines below". That is not true of
the spare slots the allocation was enlarged to provide - copy_from_user()
covers only count entries, so exec[count] keeps whatever was in the heap.
eb_parse() then makes that slot reachable:
eb->batches[0] = &eb->vma[eb->buffer_count++];
After that increment, every loop bounded by eb->buffer_count runs one entry
past the user-supplied data. The cleanup loop at the tail of
eb_relocate_parse_slow() is one of them:
out:
if (have_copy) {
const unsigned int count = eb->buffer_count;
for (i = 0; i < count; i++) {
const struct drm_i915_gem_exec_object2 *entry = &eb->exec[i];
if (!entry->relocation_count)
continue;
relocs = u64_to_ptr(typeof(*relocs), entry->relocs_ptr);
kvfree(relocs);
}
}
If the stale contents of that slot happen to have a non-zero
relocation_count, kvfree() is handed a garbage relocs_ptr. That is the fault.
The register state in the oops confirms the index.
sizeof(struct drm_i915_gem_exec_object2) is 56 (0x38):
R14 = 0x310 -> 784 / 56 = 14 = eb->buffer_count
RBX = 0x2d8 -> 728 / 56 = 13 = the faulting index, i.e. the appended slot
and eb_parse is still on the stack, which is what incremented the count.
This reproduces only on gen7/gen7.5 because those are the only platforms that
combine both preconditions: userspace still using GEM relocations (crocus),
which is what sets have_copy, and an enabled command parser, which is what
bumps buffer_count. The randomness comes from whatever previously occupied the
heap slot.
Note that the eb.vma half of this same allocation was fixed by commit
"drm/i915/gem: Zero-initialize the eb.vma array in i915_gem_do_execbuffer".
i915_gem_do_execbuffer() memsets eb.vma immediately after computing it, but
nothing memsets the exec2_list half. That fix addressed one half of the
problem and left the other.
TWO NOTES ON THE EARLIER TRIAGE IN THIS BUG
Testing 7.2 will not help. I diffed
drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c between v7.0 and 7.2-rc7: the
only changes are a redundant "else vma = NULL;" removal and a
dma_fence_array_create() signature change. The bug is present unchanged in
current mainline.
The 7.0.0-14 vs 7.0.0-27/-28 boundary is probably coincidence. I unpacked the
Ubuntu deltas for both linux_7.0.0-14.14.diff.gz and linux_7.0.0-29.29.diff.gz:
i915_gem_execbuffer.c is not patched in either, so this file is byte-identical
across that range. Given that the trigger depends on stale heap contents, a
long clean run on 7.0.0-14 is consistent with luck rather than a code
difference.
PATCH
Zero the spare slot, and bound the relocation cleanup by args->buffer_count,
since only user-supplied buffers can ever own a relocation array. Either hunk
alone stops the crash.
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1870,7 +1870,12 @@ err:
out:
if (have_copy) {
- const unsigned int count = eb->buffer_count;
+ /*
+ * Only buffers passed in by userspace can own a relocation
+ * array; eb_parse() may have appended entries beyond them by
+ * incrementing eb->buffer_count.
+ */
+ const unsigned int count = eb->args->buffer_count;
unsigned int i;
for (i = 0; i < count; i++) {
@@ -3363,6 +3368,11 @@ i915_gem_do_execbuffer(struct drm_device *dev,
eb.exec = exec;
eb.vma = (struct eb_vma *)(exec + args->buffer_count + 1);
memset(eb.vma, 0, (args->buffer_count + 1) * sizeof(struct eb_vma));
+ /*
+ * The spare exec slot is not covered by the copy_from_user() in
+ * i915_gem_execbuffer2_ioctl(), but eb_parse() makes it reachable by
+ * bumping eb.buffer_count. Do not let stale heap data through.
+ */
+ memset(exec + args->buffer_count, 0, sizeof(*exec));
eb.batch_pool = NULL;
The memset is in bounds: the exec region spans indices 0..count and eb.vma
starts at exec + count + 1.
I have not yet run a patched module - I will follow up with test
results.
Workaround in the meantime: avoid gnome-system-monitor (btop/htop do not enter
this path), or run it with LIBGL_ALWAYS_SOFTWARE=1.
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2161098
Title:
Random hard freezes caused by i915 kernel page fault in
eb_relocate_parse_slow on MacBookAir6,2 (Intel HD 5000)acBookAir6,2
(Intel HD 5000)
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2161098/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs