* [PATCH v3 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths
@ 2026-09-18 13:16 Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 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; 6+ messages in thread
From: Francesco Magazzu @ 2026-09-18 13:16 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.
The only change in v3 is the one Lyude asked for: patch 1 is Dan's
original patch again, with his authorship and his Signed-off-by
restored. The code is unchanged from what he posted; I only expanded
the commit message and noted that below the ---.
His Signed-off-by is kept with the address he signed it with in 2022
(dan.carpenter@oracle.com); .mailmap already maps it to the address he
uses today, and I did not want to rewrite someone else's tag. Say the
word if you would rather have error27@gmail.com in there. Dan is Cc'd
at that address this time - the v2 went to his old linaro.org one, which
is dead, so he most likely never saw it.
Patches 2, 3 and 4 are unchanged from v2 and carry Lyude's Reviewed-by.
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.
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.
The two follow-up patches for nvkm_pstate_prog() that Lyude also reviewed
apply on top of this series and are not resent here:
https://lore.kernel.org/dri-devel/20260727152821.128432-1-postadelmaga@gmail.com/
v3: restore Dan Carpenter's authorship on patch 1 (Lyude), collect the
Reviewed-by tags, rebase on drm-misc-next.
Link to v2:
https://lore.kernel.org/dri-devel/20260712123616.1180830-1-postadelmaga@gmail.com/
Link to Lyude's review of patch 1:
https://lore.kernel.org/dri-devel/ad023b5df1495ced25aa681d10f57628df41ac13.camel@redhat.com/
Link to Dan's original patch:
https://lore.kernel.org/dri-devel/YvSkKAdk8Pe0g2K9@kili/
Dan Carpenter (1):
drm/nouveau/clk: fix list cursor use after loop in
nvkm_clk_ustate_update
Francesco Magazzu (3):
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 | 31 +++++++++++++------
2 files changed, 28 insertions(+), 11 deletions(-)
base-commit: bc47d5937f21c5fc94504f03e18f1adb56d97634
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update
2026-09-18 13:16 [PATCH v3 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
@ 2026-09-18 13:16 ` Francesco Magazzu
2026-09-18 18:05 ` lyude
2026-09-18 13:16 ` [PATCH v3 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop Francesco Magazzu
` (2 subsequent siblings)
3 siblings, 1 reply; 6+ messages in thread
From: Francesco Magazzu @ 2026-09-18 13:16 UTC (permalink / raw)
To: Lyude Paul, Danilo Krummrich
Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst
From: Dan Carpenter <dan.carpenter@oracle.com>
If list_for_each_entry() exits without hitting a break then "pstate" is
not a valid pstate pointer. Introduce a "found" variable instead.
The check is reachable from userspace: nvkm_clk_ustate_update() takes the
pstate id straight from the 'pstate' debugfs file, so requesting an id
that is not in clk->states - or any id at all when the perf tables are
broken and the list is empty - makes the pstate->pstate != req test
dereference the list head cast to a struct nvkm_pstate, which is an
out-of-bounds read.
Fixes: 7c8565220697 ("drm/nouveau/clk: implement power state and engine clock control in core")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
[Francesco: rebased on drm-misc-next, expanded the commit message]
Signed-off-by: Francesco Magazzu <postadelmaga@gmail.com>
---
This is Dan's 2022 patch, reposted with his authorship restored as asked
in the review of v2. The diff is byte for byte what he sent; the commit
message keeps his original two sentences and adds a paragraph on how the
check is reached from userspace.
Link: https://lore.kernel.org/dri-devel/YvSkKAdk8Pe0g2K9@kili/
drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 7 +++++--
1 file changed, 5 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..5da82db71 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
@@ -473,6 +473,7 @@ static int
nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
{
struct nvkm_pstate *pstate;
+ bool found = false;
int i = 0;
if (!clk->allow_reclock)
@@ -480,12 +481,14 @@ nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
if (req != -1 && req != -2) {
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 [flat|nested] 6+ messages in thread
* [PATCH v3 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop
2026-09-18 13:16 [PATCH v3 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
@ 2026-09-18 13:16 ` Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 3/4] drm/nouveau/device: " Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan Francesco Magazzu
3 siblings, 0 replies; 6+ messages in thread
From: Francesco Magazzu @ 2026-09-18 13:16 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>
Reviewed-by: Lyude Paul <lyude@redhat.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 5da82db71..a43246ae6 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 [flat|nested] 6+ messages in thread
* [PATCH v3 3/4] drm/nouveau/device: don't use the pstate cursor after the loop
2026-09-18 13:16 [PATCH v3 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop Francesco Magazzu
@ 2026-09-18 13:16 ` Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan Francesco Magazzu
3 siblings, 0 replies; 6+ messages in thread
From: Francesco Magazzu @ 2026-09-18 13:16 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>
Reviewed-by: Lyude Paul <lyude@redhat.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 [flat|nested] 6+ messages in thread
* [PATCH v3 4/4] drm/nouveau/clk: don't clobber reclock status when restoring volt/fan
2026-09-18 13:16 [PATCH v3 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
` (2 preceding siblings ...)
2026-09-18 13:16 ` [PATCH v3 3/4] drm/nouveau/device: " Francesco Magazzu
@ 2026-09-18 13:16 ` Francesco Magazzu
3 siblings, 0 replies; 6+ messages in thread
From: Francesco Magazzu @ 2026-09-18 13:16 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>
Reviewed-by: Lyude Paul <lyude@redhat.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 a43246ae6..1cb83edc7 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 [flat|nested] 6+ messages in thread
* Re: [PATCH v3 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update
2026-09-18 13:16 ` [PATCH v3 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
@ 2026-09-18 18:05 ` lyude
0 siblings, 0 replies; 6+ messages in thread
From: lyude @ 2026-09-18 18:05 UTC (permalink / raw)
To: Francesco Magazzu, Danilo Krummrich
Cc: dri-devel, nouveau, linux-kernel, Dan Carpenter, Karol Herbst
Reviewed-by: Lyude Paul <lyude@redhat.com>
Will push to drm-misc-fixes in just a moment, thank you for the fixes!
On Fri, 2026-09-18 at 15:16 +0200, Francesco Magazzu wrote:
> From: Dan Carpenter <dan.carpenter@oracle.com>
>
> If list_for_each_entry() exits without hitting a break then "pstate"
> is
> not a valid pstate pointer. Introduce a "found" variable instead.
>
> The check is reachable from userspace: nvkm_clk_ustate_update() takes
> the
> pstate id straight from the 'pstate' debugfs file, so requesting an
> id
> that is not in clk->states - or any id at all when the perf tables
> are
> broken and the list is empty - makes the pstate->pstate != req test
> dereference the list head cast to a struct nvkm_pstate, which is an
> out-of-bounds read.
>
> Fixes: 7c8565220697 ("drm/nouveau/clk: implement power state and
> engine clock control in core")
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> [Francesco: rebased on drm-misc-next, expanded the commit message]
> Signed-off-by: Francesco Magazzu <postadelmaga@gmail.com>
> ---
> This is Dan's 2022 patch, reposted with his authorship restored as
> asked
> in the review of v2. The diff is byte for byte what he sent; the
> commit
> message keeps his original two sentences and adds a paragraph on how
> the
> check is reached from userspace.
> Link: https://lore.kernel.org/dri-devel/YvSkKAdk8Pe0g2K9@kili/
>
> drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c | 7 +++++--
> 1 file changed, 5 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..5da82db71 100644
> --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
> +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/base.c
> @@ -473,6 +473,7 @@ static int
> nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
> {
> struct nvkm_pstate *pstate;
> + bool found = false;
> int i = 0;
>
> if (!clk->allow_reclock)
> @@ -480,12 +481,14 @@ nvkm_clk_ustate_update(struct nvkm_clk *clk,
> int req)
>
> if (req != -1 && req != -2) {
> 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;
> }
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-18 18:05 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-18 13:16 [PATCH v3 0/4] drm/nouveau: fix list cursor use after loop in the clk pstate paths Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 1/4] drm/nouveau/clk: fix list cursor use after loop in nvkm_clk_ustate_update Francesco Magazzu
2026-09-18 18:05 ` lyude
2026-09-18 13:16 ` [PATCH v3 2/4] drm/nouveau/clk: don't use the pstate cursor after the loop Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 3/4] drm/nouveau/device: " Francesco Magazzu
2026-09-18 13:16 ` [PATCH v3 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
all inboxes | Powered by JetHome®