The bug is here: lo = pstate->base.domain[domain->name];
The list iterator 'pstate' will point to a bogus position containing HEAD if the list is empty or no element is found. This case should be checked before any use of the iterator, otherwise it will lead to a invalid memory access.
To fix this bug, add an check. Use a new value 'iter' as the list iterator, while use the old value 'pstate' as a dedicated variable to point to the found element.
Cc: stable@vger.kernel.org Fixes: 9838366c1597d ("drm/nouveau/device: initial control object class, with pstate control methods") Signed-off-by: Xiaomeng Tong xiam0nd.tong@gmail.com --- drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c index ce774579c89d..6b768635e8ba 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c @@ -72,7 +72,7 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) } *args = data; struct nvkm_clk *clk = ctrl->device->clk; const struct nvkm_domain *domain; - struct nvkm_pstate *pstate; + struct nvkm_pstate *pstate = NULL, *iter; struct nvkm_cstate *cstate; int i = 0, j = -1; u32 lo, hi; @@ -103,11 +103,16 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) return -EINVAL;
if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) { - list_for_each_entry(pstate, &clk->states, head) { - if (i++ == args->v0.state) + list_for_each_entry(iter, &clk->states, head) { + if (i++ == args->v0.state) { + pstate = iter; break; + } }
+ if (!pstate) + return -EINVAL; + lo = pstate->base.domain[domain->name]; hi = lo; list_for_each_entry(cstate, &pstate->list, head) {
On 3/26/22 22:31, Xiaomeng Tong wrote:
The bug is here: lo = pstate->base.domain[domain->name];
The list iterator 'pstate' will point to a bogus position containing HEAD if the list is empty or no element is found. This case should be checked before any use of the iterator, otherwise it will lead to a invalid memory access.
To fix this bug, add an check. Use a new value 'iter' as the list iterator, while use the old value 'pstate' as a dedicated variable to point to the found element.
Cc: stable@vger.kernel.org Fixes: 9838366c1597d ("drm/nouveau/device: initial control object class, with pstate control methods") Signed-off-by: Xiaomeng Tong xiam0nd.tong@gmail.com
drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c index ce774579c89d..6b768635e8ba 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/ctrl.c @@ -72,7 +72,7 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) } *args = data; struct nvkm_clk *clk = ctrl->device->clk; const struct nvkm_domain *domain;
- struct nvkm_pstate *pstate;
- struct nvkm_pstate *pstate = NULL, *iter; struct nvkm_cstate *cstate; int i = 0, j = -1; u32 lo, hi;
@@ -103,11 +103,16 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) return -EINVAL; if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
if (i++ == args->v0.state)
list_for_each_entry(iter, &clk->states, head) {
if (i++ == args->v0.state) {
pstate = iter;
Is iter and the assignment really necessary ? Unless I am missing something, list_for_each_entry() always assigns pos (pstate/iter), even if the list is empty. If nothing is found, pstate would be NULL at the end, so
break;
}}
if (!pstate)
return -EINVAL;
... just this check should do to cover both the "not found" and "list empty" cases.
Thanks, Guenter
lo = pstate->base.domain[domain->name]; hi = lo; list_for_each_entry(cstate, &pstate->list, head) {
On Sat, 26 Mar 2022 22:38:05 -0700, Guenter Roeck linux@roeck-us.net wrote:
@@ -103,11 +103,16 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) return -EINVAL; if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
if (i++ == args->v0.state)
list_for_each_entry(iter, &clk->states, head) {
if (i++ == args->v0.state) {
pstate = iter;
Is iter and the assignment really necessary ? Unless I am missing something, list_for_each_entry() always assigns pos (pstate/iter), even if the list is empty. If nothing is found, pstate would be NULL at the end, so
the pstate will not be NULL at the end! so the assignment is necessary! #define list_for_each_entry(pos, head, member) \ for (pos = __container_of((head)->next, pos, member); \ &pos->member != (head); \ pos = __container_of(pos->member.next, pos, member))
-- Xiaomeng Tong
On 3/26/22 23:59, Xiaomeng Tong wrote:
On Sat, 26 Mar 2022 22:38:05 -0700, Guenter Roeck linux@roeck-us.net wrote:
@@ -103,11 +103,16 @@ nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size) return -EINVAL; if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
if (i++ == args->v0.state)
list_for_each_entry(iter, &clk->states, head) {
if (i++ == args->v0.state) {
pstate = iter;
Is iter and the assignment really necessary ? Unless I am missing something, list_for_each_entry() always assigns pos (pstate/iter), even if the list is empty. If nothing is found, pstate would be NULL at the end, so
the pstate will not be NULL at the end! so the assignment is necessary! #define list_for_each_entry(pos, head, member) \ for (pos = __container_of((head)->next, pos, member); \ &pos->member != (head); \ pos = __container_of(pos->member.next, pos, member))
Uuh, yes, you are correct. Sorry for the noise.
Guenter
linux-stable-mirror@lists.linaro.org