β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3811

intel_ddi_init error path kfrees intel_dig_port while a fully-registered DP connector still references it (OOM-gated UAF)

Summary

On a DP+HDMI port, init order is: DP connector registered -> HDMI connector alloc; if intel_ddi_init_hdmi_connector's intel_connector_alloc fails, goto err runs drm_encoder_cleanup + kfree(intel_dig_port) while the registered DP connector holds connector->encoder == &intel_dig_port->base and remains in mode_config.connector_list -> UAF on next HPD (encoder->hotplug was installed in the freed object), connector probe, or drm_mode_config_cleanup at unload. Kernel use-after-free (read + indirect call through freed intel_dig_port) gated on kernel memory exhaustion during i915 probe - reliability-gated memcorrupt. Fix: demote the failure to DP-only when a DP connector is alive (mirrors the non-fatal LSPCON failure just below).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3811 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.1 KB ↓ raw
VERDICT.md β€” 3.1 KB ↓ raw
verdict.json β€” 2.1 KB view raw
manifest.json β€” 837 B view raw
env.txt β€” 730 B view raw

DF-3811 β€” intel_ddi_init() error path frees intel_dig_port while a registered DP connector still references it (OOM-gated UAF)

File

sys/dev/drm/i915/intel_ddi.c:3863-3895 (intel_ddi_init)

Build / Run / Expected

Not built, not run β€” guest has no Intel graphics (see env.txt). Hypothetical trigger on real hardware: memory exhaustion exactly when intel_ddi_init_hdmi_connector()'s intel_connector_alloc() (intel_ddi.c:3694) fails after intel_ddi_init_dp_connector() already succeeded on a dual-purpose DP++/HDMI port (the common case β€” init_hdmi && init_dp). Expected: goto err runs drm_encoder_cleanup(encoder); kfree(intel_dig_port); while the DP connector registered inside intel_dp_init_connector() (drm_connector_init at intel_dp.c:6620, intel_connector_attach_encoder at intel_dp.c:6634) stays in dev->mode_config.connector_list holding connector->encoder == &intel_dig_port->base β†’ use-after-free on the next hotplug, connector probe, or drm_mode_config_cleanup() at unload.

Root cause

    if (init_dp) {
        if (!intel_ddi_init_dp_connector(intel_dig_port))
            goto err;                 /* nothing registered yet: this one is fine */
        intel_dig_port->hpd_pulse = intel_dp_hpd_pulse;
    }
    if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi) {
        if (!intel_ddi_init_hdmi_connector(intel_dig_port))
            goto err;                 /* DP connector LIVE at this point: not fine */
    }
    ...
err:
    drm_encoder_cleanup(encoder);
    kfree(intel_dig_port);            /* frees object the live connector references */

intel_ddi_init_hdmi_connector() (intel_ddi.c:3688-3702) returns NULL only when intel_connector_alloc() fails β€” its inner intel_hdmi_init_connector() is void β€” so the window is exactly a kmalloc failure during probe on a DP+HDMI port. Hence Low severity / OOM-gated, but the class is use-after-free.

Impact

Kernel UAF (dereference + indirect call through freed intel_dig_port) on any subsequent operation touching the surviving DP connector β€” HPD handling (encoder->hotplug == intel_ddi_hotplug was installed in the freed object), connector fill_modes/get_status, or final drm_mode_config_cleanup.

VERDICT.md
↓ download raw

DF-3811 β€” VERDICT

status: untested (poc enum) / not testable on this guest β€” no Intel GPU.

What was verified (source-level)

  1. intel_ddi_init_dp_connector() (intel_ddi.c:3525-3542) calls intel_dp_init_connector(); that function performs drm_connector_init() (intel_dp.c:6620 β€” registers the connector in DRM core lists) and intel_connector_attach_encoder() (intel_dp.c:6634 β€” stores intel_connector->encoder = intel_encoder, i.e. &intel_dig_port->base) before it can ever fail; it returns false only on the two early checks at intel_dp.c:6575 (max_lanes) and intel_dp.c:6611 (vlv/chv ePort), both before registration. Therefore a successful DP init leaves a live, DRM-registered connector that references intel_dig_port.
  2. The second goto err (intel_ddi.c:3873-3874) is reachable with that live connector when intel_connector_alloc() in intel_ddi_init_hdmi_connector() (intel_ddi.c:3694) returns NULL.
  3. err: (intel_ddi.c:3893-3895) then does drm_encoder_cleanup(encoder); kfree(intel_dig_port); without destroying/detaching the live DP connector (and its aux/i2c children registered by intel_dp_aux_init, intel_dp.c:6629).
  4. Consequence chain: connector remains in mode_config.connector_list; HPD work β†’ intel_encoder_hotplug/encoder->hotplug reads freed memory; drm_mode_config_cleanup() β†’ connector destroy β†’ freed-encoder deref.

Why not run

No Intel GPU on the audit guest; forcing the OOM inside a real probe would require the hardware plus a fault-injection kernel. The defect is a certain code-path property; the trigger is allocation-failure-gated, hence Low.

Fix

--- a/sys/dev/drm/i915/intel_ddi.c
+++ b/sys/dev/drm/i915/intel_ddi.c
@@ -3870,7 +3870,13 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
    /* In theory we don't need the encoder->type check, but leave it just in
     * case we have some really bad VBTs... */
    if (intel_encoder->type != INTEL_OUTPUT_EDP && init_hdmi) {
-       if (!intel_ddi_init_hdmi_connector(intel_dig_port))
+       if (intel_ddi_init_hdmi_connector(intel_dig_port) == NULL) {
+           /*
+            * The DP connector (if any) is registered and still
+            * references intel_dig_port β€” tearing down the encoder
+            * here would leave a UAF. Keep the DP-only port alive.
+            */
+           DRM_ERROR("HDMI connector alloc failed on port %c; continuing DP-only\n",
+                 port_name(port));
            goto err;
+       }
    }

(Net effect: demote the HDMI-only OOM failure to non-fatal when a DP connector already exists; alternatively fully tear down the DP connector + aux + i2c before kfree β€” the simple demotion matches how the LSPCON failure just below is already treated as non-fatal.)

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable: baseline never reproduced (no hardware)

per-fix-DF-3811

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-3811/VERDICT.md (registration-before-failure proof with citations), findings/poc/DF-3811/env.txt

PoC changes

no PoC written; source-level analysis only (no trigger hardware)

Verified recommended fix

Do not kfree intel_dig_port when a DP connector is already registered - demote HDMI connector alloc failure to non-fatal or fully destroy the DP connector first

Verdict

intel_ddi_init() reaches its err: label (kfree of intel_dig_port, intel_ddi.c:3893-3895) after intel_ddi_init_dp_connector() has fully registered a DP connector that stores a pointer to the encoder inside intel_dig_port (drm_connector_init + intel_connector_attach_encoder at intel_dp.c:6620/6634). The err path neither destroys nor detaches that connector, leaving a DRM-core-listed object referencing freed memory (UAF on next HPD/probe/unload). The only trigger for the second goto err is intel_connector_alloc() failure inside intel_ddi_init_hdmi_connector() (intel_ddi.c:3694), so exploitation is gated on kernel memory exhaustion during driver probe β€” hence Low severity. Not executed: guest has no Intel GPU; code-path property is certain from source.