The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v2 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths
@ 2026-07-12 12:36 Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Francesco Magazzu @ 2026-07-12 12:36 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich
  Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst

This series deals with the three places in the nouveau clk pstate code
where the list_for_each_entry() cursor is used after the loop, plus one
unrelated fix.  They are exactly the three sites Dan Carpenter listed in
2022; his patch for the first one was never applied and the bug is still
present in drm-misc-next.

Link: https://lore.kernel.org/dri-devel/YvSkKAdk8Pe0g2K9@kili/

Only patch 1 fixes a bug that can actually be triggered:
nvkm_clk_ustate_update() takes an arbitrary pstate id from the user and
never checks that a matching entry exists, so if it does not the cursor
ends up pointing at the list head and the code reads past it.  It carries
a Fixes: tag and the credit for spotting it goes to Dan.

Patches 2 and 3 fix the same pattern in nvkm_pstate_prog() and
nvkm_control_mthd_pstate_attr(), but neither is triggerable as the code
stands: the callers of nvkm_pstate_prog() clamp the index against
clk->state_nr first, and nvkm_control_mthd_pstate_attr() already rejects
args->v0.state >= clk->state_nr before the loop.  Both are hardening, not
bug fixes, and they carry no Fixes: tag on purpose.  The point is to stop
the two functions from being correct only by virtue of what their callers
do.

Patch 4 is unrelated and is a real bug, though a modest one:
nvkm_cstate_prog() overwrites the reclock status in 'ret' with the status
of the voltage/fan restore calls it makes afterwards.  The only consumer of
the return value is an error message in nvkm_pstate_work(), so the
observable effect is that a failing reclock is never reported in dmesg.

Compile-tested only.  All the affected paths are reachable only by root,
through the 'pstate' debugfs file, so I could not exercise them in any
other way.

v2: v1 (2 patches) was sent on 2026-07-11 with a mail client that mangled
the patch text, so it did not apply; it is resent here with git send-email.
This version also adds the two remaining sites from Dan's list, and the
Fixes: tags to the two real fixes.

Link to v1:
https://lore.kernel.org/nouveau/CAHwn+vnw=DD=j7umFVdcr8-F6yUz=iM+3yfA+HcHKXJwpNrHRA@mail.gmail.com/

Francesco Magazzu (4):
  drm/nouveau/clk: fix list cursor use after loop in
    nvkm_clk_ustate_update
  drm/nouveau/clk: don't use the pstate cursor after the loop
  drm/nouveau/device: don't use the pstate cursor after the loop
  drm/nouveau/clk: don't clobber reclock status when restoring volt/fan

 .../gpu/drm/nouveau/nvkm/engine/device/ctrl.c |  8 ++++-
 .../gpu/drm/nouveau/nvkm/subdev/clk/base.c    | 32 +++++++++++++------
 2 files changed, 29 insertions(+), 11 deletions(-)


base-commit: a284476db2653ae893f46cbea408eb412db54eb0
-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update
  2026-07-12 12:36 [PATCH v2 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
@ 2026-07-12 12:36 ` Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop Francesco Magazzu
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Francesco Magazzu @ 2026-07-12 12:36 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich
  Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst

If the requested pstate id is not present in the state list (or the
list is empty, e.g. broken/missing perf tables), the list_for_each_entry
cursor runs off the end of the list and the subsequent
pstate->pstate != req check dereferences the list head cast to a
struct nvkm_pstate, which is an out-of-bounds read.

Track whether the entry was actually found instead of inspecting the
cursor after the loop.

Fixes: 7c8565220697 ("drm/nouveau/clk: implement power state and engine clock control in core")
Signed-off-by: Francesco Magazzu <postadelmaga@gmail.com>
---
Note: essentially the same fix was posted by Dan Carpenter in 2022 and
never picked up; the bug is still present in drm-misc-next.  Credit for
spotting it goes to him.
Link: https://lore.kernel.org/dri-devel/YvSkKAdk8Pe0g2K9@kili/

 drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
index 572e63846..42f3709e0 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
@@ -479,13 +479,17 @@ nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
 		return -ENOSYS;
 
 	if (req != -1 && req != -2) {
+		bool found = false;
+
 		list_for_each_entry(pstate, &clk->states, head) {
-			if (pstate->pstate == req)
+			if (pstate->pstate == req) {
+				found = true;
 				break;
+			}
 			i++;
 		}
 
-		if (pstate->pstate != req)
+		if (!found)
 			return -EINVAL;
 		req = i;
 	}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop
  2026-07-12 12:36 [PATCH v2 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
@ 2026-07-12 12:36 ` Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 3/4] drm/nouveau/device: " Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan Francesco Magazzu
  3 siblings, 0 replies; 5+ messages in thread
From: Francesco Magazzu @ 2026-07-12 12:36 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich
  Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst

nvkm_pstate_prog() walks clk->states looking for the entry at index
'pstatei' and then keeps using the list_for_each_entry cursor after the
loop.  This is not triggerable today: every caller clamps the index
against clk->state_nr before calling, so the loop always breaks on a real
entry.  It is safe by virtue of what the callers happen to do, not by
anything the function itself checks.

Should a caller ever pass an index that is not on the list, the cursor
would point at the list head rather than at a pstate, and the
pstate->base.domain[] and pstate->fanspeed accesses that follow would read
past it.  Rather than leave that trap in place for the next caller, track
whether the entry was found and return -EINVAL if it was not.

No functional change.

Signed-off-by: Francesco Magazzu <postadelmaga@gmail.com>
---
 drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
index 42f3709e0..4d546b07f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
@@ -270,13 +270,19 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
 	struct nvkm_fb *fb = subdev->device->fb;
 	struct nvkm_pci *pci = subdev->device->pci;
 	struct nvkm_pstate *pstate;
+	bool found = false;
 	int ret, idx = 0;
 
 	list_for_each_entry(pstate, &clk->states, head) {
-		if (idx++ == pstatei)
+		if (idx++ == pstatei) {
+			found = true;
 			break;
+		}
 	}
 
+	if (!found)
+		return -EINVAL;
+
 	nvkm_debug(subdev, "setting performance state %d\n", pstatei);
 	clk->pstate = pstatei;
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 3/4] drm/nouveau/device: don't use the pstate cursor after the loop
  2026-07-12 12:36 [PATCH v2 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop Francesco Magazzu
@ 2026-07-12 12:36 ` Francesco Magazzu
  2026-07-12 12:36 ` [PATCH v2 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan Francesco Magazzu
  3 siblings, 0 replies; 5+ messages in thread
From: Francesco Magazzu @ 2026-07-12 12:36 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich
  Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst

nvkm_control_mthd_pstate_attr() looks up the pstate at the index supplied
by userspace by walking clk->states, and then keeps using the
list_for_each_entry cursor after the loop.  This is not triggerable today:
the function already rejects args->v0.state >= clk->state_nr before the
loop, and clk->state_nr is kept in sync with the number of entries on
clk->states, so the lookup always breaks on a real entry.

Should the loop ever run to completion, the cursor would point at the list
head rather than at a pstate, and the pstate->base.domain[] read and the
walk of pstate->list that follow would read past it.  Rather than leave
that trap in place, track whether the entry was found and return -EINVAL if
it was not, like the other lookup failures in this function.

No functional change.

Signed-off-by: Francesco Magazzu <postadelmaga@gmail.com>
---
 drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c
index f2e9a0626..28702741a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c
@@ -74,6 +74,7 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
 	const struct nvkm_domain *domain;
 	struct nvkm_pstate *pstate;
 	struct nvkm_cstate *cstate;
+	bool found = false;
 	int i = 0, j = -1;
 	u32 lo, hi;
 	int ret = -ENOSYS;
@@ -104,10 +105,15 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
 
 	if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
 		list_for_each_entry(pstate, &clk->states, head) {
-			if (i++ == args->v0.state)
+			if (i++ == args->v0.state) {
+				found = true;
 				break;
+			}
 		}
 
+		if (!found)
+			return -EINVAL;
+
 		lo = pstate->base.domain[domain->name];
 		hi = lo;
 		list_for_each_entry(cstate, &pstate->list, head) {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan
  2026-07-12 12:36 [PATCH v2 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
                   ` (2 preceding siblings ...)
  2026-07-12 12:36 ` [PATCH v2 3/4] drm/nouveau/device: " Francesco Magazzu
@ 2026-07-12 12:36 ` Francesco Magazzu
  3 siblings, 0 replies; 5+ messages in thread
From: Francesco Magazzu @ 2026-07-12 12:36 UTC (permalink / raw)
  To: Lyude Paul, Danilo Krummrich
  Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst

nvkm_cstate_prog() reuses 'ret' for the voltage and fan-speed restore
calls it makes after reprogramming the clocks.  Those calls almost always
succeed, so the status of the reclock itself is overwritten and the
function reports success even when clk->func->calc() or clk->func->prog()
failed.  The converse is also true: a successful reclock is reported as an
error if the final restore call fails, even though that failure is only
logged and otherwise ignored.

The only consumer of the return value is the error message in
nvkm_pstate_work(), so in practice a failing reclock is simply never
reported.  Nothing else changes, but a function that returns success on
failure is a trap for the next caller.

Keep the calc/prog status in 'ret' and use a separate local for the
restore calls.

Fixes: 3eca809b3c05 ("drm/nouveau/clk: cosmetic changes")
Signed-off-by: Francesco Magazzu <postadelmaga@gmail.com>
---
 drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
index 4d546b07f..05336fc7d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
@@ -199,16 +199,18 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
 	}
 
 	if (volt) {
-		ret = nvkm_volt_set_id(volt, cstate->voltage,
-				       pstate->base.voltage, clk->temp, -1);
-		if (ret && ret != -ENODEV)
-			nvkm_error(subdev, "failed to lower voltage: %d\n", ret);
+		int err = nvkm_volt_set_id(volt, cstate->voltage,
+					   pstate->base.voltage, clk->temp, -1);
+
+		if (err && err != -ENODEV)
+			nvkm_error(subdev, "failed to lower voltage: %d\n", err);
 	}
 
 	if (therm) {
-		ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
-		if (ret && ret != -ENODEV)
-			nvkm_error(subdev, "failed to lower fan speed: %d\n", ret);
+		int err = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
+
+		if (err && err != -ENODEV)
+			nvkm_error(subdev, "failed to lower fan speed: %d\n", err);
 	}
 
 	return ret;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-12 12:37 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-12 12:36 [PATCH v2 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
2026-07-12 12:36 ` [PATCH v2 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
2026-07-12 12:36 ` [PATCH v2 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop Francesco Magazzu
2026-07-12 12:36 ` [PATCH v2 3/4] drm/nouveau/device: " Francesco Magazzu
2026-07-12 12:36 ` [PATCH v2 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan Francesco Magazzu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox