From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f52.google.com (mail-wr1-f52.google.com [209.85.221.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C612207E00 for ; Sun, 16 Aug 2026 13:09:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.52 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786885754; cv=none; b=ldUhlXLnT/vXaTTl83F7FLGsddMNPn20nuiOyXW7+ZIEDLdaBmWQi8VRWi6KMiIkS1p4wu1j59hdW3bxm074unXXy6j6fq42jQEG0kDiRoYTOLWZ5ipscYBkqFAGugT/mlvCwdI7OVt3apmgZW74E9MzyujLckRT74GyTI3N0Qc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786885754; c=relaxed/simple; bh=BkKEIFoFIBEMMDWnpGcr+nU6K9UCuNKCtXVpCqnRVZ0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=aG8CCE+tUd21rNAVErt1BhllXMEOfYLIMro9zEcq+t2DXiU41u7k+tJ07ghGVLRPFzWQtjIPyexrRAuKRpZsvUQKgEJuu3Z7qCOyPDV2vlPeZc/a+84P/ky6oDJgoNl16Zo/ruJR2n1kozl1c9oeg+qk63eGkQk7FlA6zFG8RSQ= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nrtWEGdo; arc=none smtp.client-ip=209.85.221.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nrtWEGdo" Received: by mail-wr1-f52.google.com with SMTP id ffacd0b85a97d-47fe45db360so311404f8f.2 for ; Sun, 16 Aug 2026 06:09:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786885751; x=1787490551; darn=vger.kernel.org; h=mime-version:content-transfer-encoding:content-type:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to:content-type; bh=N7ModForEGzdaliJRz1t1lN04ZfOQCHIzWf+H/xEqzc=; b=nrtWEGdoeGH9xssWF3g+0naLYEDy+RW2gJEEytPAhWIa6XThvYyXnEJKEz8G6XKgZL a6vqoJuoioUrka2MFi3rMhn143DIyHtniWjNur0/AyzhwTiGoHFWhbtgF+t40kMXeN3Q Acrq6VutWbdL8NUN2Zbhz479T/MZfj5RUhI60JyVCT2XOR1bWdpXq1LqflqkKoQFObJu 3p96pSwOtipxRFHguKox1RD5HPgMVd27ODv/lKVc7cg3NvfPfjSQGZ4aJnFuLaJy0AOT ppWOTJVeS04PjfH99kiQHZA/8355XIkiR5Twl2rVZNJi7nMbg5E3gyop/IstH/oXusnM lPHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786885751; x=1787490551; h=mime-version:content-transfer-encoding:content-type:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=N7ModForEGzdaliJRz1t1lN04ZfOQCHIzWf+H/xEqzc=; b=iEbgUb0YFA64hQwgt7Qq0x4MvkFBK5cspTQOFTzhZySftzVnzp/wQcWF734BYW1KzC wqOZObXskX5e4Fp0F+L/hceKGMlDMqF7f7t0xmPppk2mJIkNOrBOuhjHzZGXi2PMS1Vw jLOzXEapj7mmJtXrCFjfLFuIUQvrhGp9xXl9r30kBmLGC0x7AD6J80kbQgPjOw8UTBkb eZyFVJmx5VoU0idJb/6xDLj3ZPAANx1Uc/hIh9yVhvUfKEi9kayJWsCazmAlXc6VWp0r WToKl48k4qlS0HIYdmMd1rNcG3H6iOOJ8y8O0r7JF17iC7ng561GGL3JDpzIPxZ9ixRR lyHg== X-Gm-Message-State: AOJu0YwhMj2Zw87Hk8gO+mxXAU500AcG1m6pTLbUX9SjgQpXTJ2TZf2O /EIh/yl1NliuRkQAAELszBuCYFLb35VEc//1GdIX43RJ4xCEUVpAesm9 X-Gm-Gg: AR+sD10H3rk2GQfBJDJdaouqXyjAw+/l5WG5lJ10WPL5h7pYM5g1T3bXlduEV4LqwqD Pf7zWOeBL/hasxLN4Abgh9MXik99nAKdC3XBoBwZyAlSc7qkEaw7YAlji8UBjGjzb3Nd0jcDauZ hHLV+jEyNtg+PRBHv1oY5z2t7JCprSYgoWj8qFfrquxIO9zVgTjkmn3B9ekzioUuqEP+a0H7o/Y I+l5MZmagFFytX55R7Ds32362UuTZooBu9XIsEzqy+JZFP+rHAHwOfMlsI8Aq8zzcWCVoElw+7G /k8Xr5YKCb0CpoZd215Wrubhl0UoQM+Aema6dIoRrvlSU4HtLuKei9ZukGPwrQUoYQMEYl4Bg60 GaIEzK9KLVt91z21+ndOVQV1Z4yKibTRcyk0rHpkkSsVBQvgcXFgRIRBRAhUbD63uB2sJKumKNF h2FBVGOfSeYFtAoyV6oVUzQIwaaKYCniU1Dbk5vAKMEtCOHy94wsZckkZrqQkMzzSZat0nAe6OY R3cfgxknK1+mt0lP7EpWoNJ0cNnNmiZSXXVhODbIw== X-Received: by 2002:a05:600c:1385:b0:496:c249:ddb1 with SMTP id 5b1f17b1804b1-499879759bbmr135946405e9.4.1786885751312; Sun, 16 Aug 2026 06:09:11 -0700 (PDT) Received: from [127.0.0.1] (ip-109-193-028-127.um39.pools.vodafone-ip.de. [109.193.28.127]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49996105c0bsm66548375e9.4.2026.08.16.06.09.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 16 Aug 2026 06:09:10 -0700 (PDT) From: Marek Czernohous To: nouveau@lists.freedesktop.org, dri-devel@lists.freedesktop.org Cc: linux-kernel@vger.kernel.org, Danilo Krummrich , Lyude Paul , David Airlie , Simona Vetter Subject: [PATCH v4 2/2] drm/nouveau/kms: guard NULL crtc in nv50_sor_atomic_disable() Date: Sun, 16 Aug 2026 15:09:04 +0200 Message-ID: <178688574402.522643.5471719764843371376@gmail.com> X-Mailer: python-smtplib In-Reply-To: <178688574400.522643.6695278742335367229@gmail.com> References: <178688574400.522643.6695278742335367229@gmail.com> Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Marek Czernohous nv50_sor_atomic_disable() unconditionally computes nv50_head(nv_encoder->crtc) and dereferences the result a few lines later. nv_encoder->crtc is nouveau's own shadow pointer, set in .atomic_enable and cleared at the end of .atomic_disable. Commit f575f2bdb6c3 ("drm/nouveau/kms/nv50-: Remove (nv_encoder->crtc) checks in ->disable callbacks") removed the NULL check here, reasoning that the atomic helpers never call ->disable without a crtc. On NVAC (MCP79) under Wayland sessions (observed with Weston's DRM backend and with labwc/wlroots) we have hit the NULL case in practice during session teardown and VT switches: disable runs without (or after) its matching enable, and because nv50_head() is container_of(), the NULL does not stay NULL but becomes a bogus non-NULL pointer, so the subsequent head dereferences fault and the kernel oopses. Restore the guard, as drm_WARN_ON_ONCE() instead of a silent return: a NULL crtc here still indicates a state-tracking inconsistency that should stay visible. Return without touching the output; in this path either enable never ran (nothing to tear down) or an earlier disable already did the teardown, and the encoder release is handled by the commit_tail release loop in both cases. (That loop then rejects the release of a never-acquired output with -EINVAL in the nvif layer, which is harmless; the vanilla code oopsed before ever reaching it.) The same inconsistent-state path can also leave the encoder without an old connector state, in which case nv50_outp_get_old_connector() returns NULL while the backlight teardown dereferenced it unconditionally, so the oops would only have moved there. Hoist the guard above all of that and look at the old connector only after checking it. Fixes: f575f2bdb6c3 ("drm/nouveau/kms/nv50-: Remove (nv_encoder->crtc) checks in ->disable callbacks") Cc: Tested-by: Fab Stz Assisted-by: Claude:claude-opus-4-7 Assisted-by: Claude:claude-opus-4-8 Assisted-by: Claude:claude-opus-5 Signed-off-by: Marek Czernohous --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 30 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 364227f5456f..f532b0ed8880 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1565,16 +1565,36 @@ static void nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_commit *state) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nv50_head *head = nv50_head(nv_encoder->crtc); + struct nv50_head *head; #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT - struct nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder); + struct nouveau_connector *nv_connector; struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nouveau_backlight *backlight = nv_connector->backlight; - struct drm_dp_aux *aux = &nv_connector->aux; + struct nouveau_backlight *backlight; int ret; +#endif + /* nv_encoder->crtc is the driver's shadow pointer, set in + * .atomic_enable (and by the boot-time hardware readback) and + * cleared at the end of this function. NULL here + * means disable-without-enable or a double disable; bail before + * container_of() turns it into a bogus head pointer (checking the + * result would not work, container_of(NULL) is never NULL). The + * encoder release is handled by the commit_tail release loop, so + * there is nothing to clean up here. + */ + if (drm_WARN_ON_ONCE(encoder->dev, !nv_encoder->crtc)) + return; + head = nv50_head(nv_encoder->crtc); + +#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT + /* The same inconsistent-state path can leave us without an old + * connector state, so check before touching it. + */ + nv_connector = nv50_outp_get_old_connector(state, nv_encoder); + backlight = nv_connector ? nv_connector->backlight : NULL; if (backlight && backlight->uses_dpcd) { - ret = drm_edp_backlight_disable(aux, &backlight->edp_info); + ret = drm_edp_backlight_disable(&nv_connector->aux, + &backlight->edp_info); if (ret < 0) NV_ERROR(drm, "Failed to disable backlight on [CONNECTOR:%d:%s]: %d\n", nv_connector->base.base.id, nv_connector->base.name, ret); -- 2.54.0