NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/60291: i915drmkms kernel panic when booting with no monitor connected
>Number: 60291
>Category: kern
>Synopsis: i915drmkms kernel panic when booting with no monitor connected
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun May 24 02:45:00 +0000 2026
>Originator: sergio lenzi
>Release: 10.1_STABLE
>Organization:
k1 sistemas
>Environment:
NetBSD desktop2.lenzicasa 10.1_STABLE NetBSD 10.1_STABLE (LZT_2048_USERS) #2: Fri May 22 12:18:56 -03 2026 NetBSD@lztdev.lenzicasa:/home/NetBSD/BUILD/10/amd64/OBJ/sys/arch/amd64/compile/GENERIC amd6
>Description:
i915drmkms kernel panic when booting with no monitor connected
==============================================================
Problem
-------
NetBSD 10 panics during boot on systems with an Intel GPU using the
i915drmkms driver when no monitor is connected. The panic is a NULL
pointer dereference in the framebuffer initialization path.
Root Cause
----------
The crash involves a race condition and missing NULL checks in the
NetBSD-specific DRM framebuffer glue code. The sequence is:
1. intel_fbdev_initial_config() calls drm_fb_helper_initial_config(),
which calls drm_fb_helper_single_fb_probe().
2. With no monitor connected, crtc_count is 0, so
drm_fb_helper_single_fb_probe() returns -EAGAIN *without* calling
the fb_probe callback (intelfb_create).
File: dist/drm/drm_fb_helper.c, lines 1641-1647
3. __drm_fb_helper_initial_config_and_unlock() catches -EAGAIN, sets
deferred_setup = true, and returns 0 (success).
File: dist/drm/drm_fb_helper.c, lines 1823-1826
4. Because intelfb_create() never ran:
- helper->fb is NULL
- ifbdev->vma is NULL
- No intelfb child device was created via config_found()
5. Any subsequent access to helper->fb or ifbdev->vma causes a NULL
pointer dereference and kernel panic.
There is also a race condition that can trigger even WITH a monitor:
In intelfb_create() (intel_fbdev.c, NetBSD section), config_found()
is called at line 268, which triggers intelfb_attach(), which
schedules the asynchronous intelfb_attach_task(). That task
dereferences helper->fb->pitches[0] at intelfb.c line 138. But
helper->fb is not set until line 276, AFTER config_found() returns.
If the async task runs before line 276 completes, it hits a NULL
pointer dereference.
Crash Points
------------
1. intelfb.c line 138 (intelfb_attach_task):
.da_fb_linebytes = ifa->ifa_fb_helper->fb->pitches[0],
Dereferences helper->fb without a NULL check. Crashes when
intelfb_create was never called (headless) or when the async task
wins the race against line 276 of intel_fbdev.c.
2. intelfb.c line 182 (intelfb_drmfb_mmapfb):
KASSERT(offset < vma->node.size);
Dereferences fbdev->vma without a NULL check. Crashes if mmap is
attempted before vma is initialized.
3. drmfb.c line 170 (drmfb_attach):
KASSERTMSG(error == 0, "genfb_attach failed, error=%d", error);
If genfb_attach() fails for any reason (e.g. zero-sized framebuffer),
this KASSERT panics the kernel instead of returning an error.
Fix
---
Four changes in three NetBSD-specific files. No changes to the
upstream-derived drm_fb_helper.c are needed; the deferred_setup
mechanism works correctly as designed.
Fix 1 - Eliminate the race condition (intel_fbdev.c)
File: dist/drm/i915/display/intel_fbdev.c, lines 258-276
(inside #ifdef __NetBSD__ block)
Move the assignment:
ifbdev->helper.fb = &ifbdev->fb->base;
from line 276 (after config_found) to before the config_found()
call at line 267. This ensures helper->fb is valid before the
async intelfb_attach_task can run.
If config_found() fails, reset helper->fb to NULL before taking
the error path.
Fix 2 - Add NULL check in intelfb_attach_task (intelfb.c)
File: i915drm/intelfb.c, lines 127-157
Before dereferencing helper->fb->pitches[0], check that helper->fb
is not NULL. If NULL, print an error and skip to the out label.
This is defense-in-depth for Fix 1.
Fix 3 - Replace KASSERT with error return in drmfb_attach (drmfb.c)
File: drm/drmfb.c, lines 98, 167-173
Replace:
KASSERTMSG(error == 0, "genfb_attach failed, error=%d", error);
with:
if (error) {
aprint_error_dev(da->da_dev,
"genfb_attach failed, error=%d\n", error);
return error;
}
Also remove __diagused from the error variable declaration at
line 98, since the variable is now unconditionally used.
Fix 4 - Add NULL guard in intelfb_drmfb_mmapfb (intelfb.c)
File: i915drm/intelfb.c, lines 167-186
After obtaining vma from fbdev->vma, check for NULL before the
KASSERT that dereferences vma->node.size. Return (paddr_t)-1
on NULL, which is the standard mmap failure value.
Expected Behavior After Fix
---------------------------
Headless boot (no monitor):
drm_fb_helper_initial_config returns 0 with deferred_setup=true.
No intelfb child device is created. VGA console remains active.
The system boots to multiuser without panic.
Monitor hot-plugged after headless boot:
intel_fbdev_output_poll_changed() detects deferred_setup==true,
triggers drm_fb_helper_hotplug_event(), which re-enters the
initial config path. This time a monitor is present, so
intelfb_create() runs successfully, helper->fb is set before
config_found() (Fix 1), and the intelfb child attaches cleanly.
Normal boot (monitor connected):
No change in behavior. Fix 1 eliminates the pre-existing race
condition. Fixes 2-4 add defensive checks that do not affect
the normal path.
Files Involved (all paths relative to sys/external/bsd/drm2/)
--------------------------------------------------------------
Modified:
dist/drm/i915/display/intel_fbdev.c Fix 1
i915drm/intelfb.c Fixes 2 and 4
drm/drmfb.c Fix 3
Reference only (not modified):
dist/drm/drm_fb_helper.c deferred_setup mechanism
i915drm/intelfb.h intelfb_attach_args struct
dist/drm/i915/display/intel_display_types.h intel_fbdev struct
>How-To-Repeat:
find a cpu with intel graphics do not connect a monitor, and boot, it panics and keep rebooting... until you connect a monitor
solution was to disable i915drmkms in /boot.cfg
Now AI point to a possible solution...
Please someone can take a look???
>Fix:
--- a/sys/external/bsd/drm2/dist/drm/i915/display/intel_fbdev.c
+++ b/sys/external/bsd/drm2/dist/drm/i915/display/intel_fbdev.c
@@ -255,7 +255,14 @@
ifa.ifa_fb_helper = helper;
ifa.ifa_fb_sizes = *sizes;
ifa.ifa_fb_vaddr = vaddr;
+ /*
+ * Set helper->fb before config_found(), because
+ * intelfb_attach_task (scheduled asynchronously from
+ * config_found -> intelfb_attach) dereferences
+ * helper->fb->pitches[0].
+ */
+ ifbdev->helper.fb = &ifbdev->fb->base;
/*
* XXX Should do this asynchronously, since we hold
* dev->struct_mutex.
@@ -268,9 +275,9 @@
if (helper->fbdev == NULL) {
DRM_ERROR("unable to attach intelfb\n");
+ ifbdev->helper.fb = NULL;
ret = -ENXIO;
goto out_unpin;
}
- ifbdev->helper.fb = &ifbdev->fb->base;
}
#else
info = drm_fb_helper_alloc_fbi(helper);
--- a/sys/external/bsd/drm2/i915drm/intelfb.c
+++ b/sys/external/bsd/drm2/i915drm/intelfb.c
@@ -127,17 +127,27 @@
static void
intelfb_attach_task(struct i915drmkms_task *task)
{
struct intelfb_softc *const sc = container_of(task,
struct intelfb_softc, sc_attach_task);
const struct intelfb_attach_args *const ifa = &sc->sc_ifa;
- const struct drmfb_attach_args da = {
- .da_dev = sc->sc_dev,
- .da_fb_helper = ifa->ifa_fb_helper,
- .da_fb_sizes = &ifa->ifa_fb_sizes,
- .da_fb_vaddr = ifa->ifa_fb_vaddr,
- .da_fb_linebytes = ifa->ifa_fb_helper->fb->pitches[0],
- .da_params = &intelfb_drmfb_params,
- };
+ struct drm_fb_helper *const helper = ifa->ifa_fb_helper;
+ struct drmfb_attach_args da;
int error;
+ if (helper->fb == NULL) {
+ aprint_error_dev(sc->sc_dev,
+ "no framebuffer allocated, cannot attach drmfb\n");
+ goto out;
+ }
+
+ da = (const struct drmfb_attach_args){
+ .da_dev = sc->sc_dev,
+ .da_fb_helper = helper,
+ .da_fb_sizes = &ifa->ifa_fb_sizes,
+ .da_fb_vaddr = ifa->ifa_fb_vaddr,
+ .da_fb_linebytes = helper->fb->pitches[0],
+ .da_params = &intelfb_drmfb_params,
+ };
+
error = drmfb_attach(&sc->sc_drmfb, &da);
if (error) {
aprint_error_dev(sc->sc_dev, "failed to attach drmfb: %d\n",
@@ -167,6 +177,9 @@
struct i915_ggtt *const ggtt = &i915->ggtt;
struct i915_vma *const vma = fbdev->vma;
+ if (vma == NULL)
+ return (paddr_t)-1;
+
KASSERT(0 <= offset);
KASSERT(offset < vma->node.size);
--- a/sys/external/bsd/drm2/drm/drmfb.c
+++ b/sys/external/bsd/drm2/drm/drmfb.c
@@ -95,7 +95,7 @@
static const struct genfb_ops zero_genfb_ops;
struct genfb_ops genfb_ops = zero_genfb_ops;
bool is_console;
- int error __diagused;
+ int error;
/* genfb requires this. */
KASSERTMSG((void *)&sc->sc_genfb == device_private(da->da_dev),
@@ -167,7 +167,11 @@
KERNEL_LOCK(1, NULL);
error = genfb_attach(&sc->sc_genfb, &genfb_ops);
KERNEL_UNLOCK_ONE(NULL);
- KASSERTMSG(error == 0, "genfb_attach failed, error=%d", error);
+ if (error) {
+ aprint_error_dev(da->da_dev,
+ "genfb_attach failed, error=%d\n", error);
+ return error;
+ }
/* Success! */
return 0;
Home |
Main Index |
Thread Index |
Old Index