[PATCH] drm/nouveau/bios: cap init script nesting depth

From: Noah Weinberger

Date: Sun Nov 08 2026 - 18:57:03 EST


Hello,

INIT_SUB_DIRECT executes a sub-table by recursing into
nvbios_exec() after redirecting init->offset to a u16 address taken
from the VBIOS:

save = init->offset;
init->offset = addr;
if (nvbios_exec(init)) { ... }
init->offset = save;

A sub-table that directs to itself (directly or via a cycle)
therefore recurses without any limit - the init->nested counter is
incremented and decremented but never checked anywhere. The result
is unbounded recursion and kernel stack exhaustion when the script
runs at devinit/display-init time. The patch caps the depth at 16,
which is far above anything a legitimate image needs.

Affected version/commit: mainline 8a65162514fbad21a2c58ffed65f42b27e92b163 (v7.4-rc2 window, 2026-11-08), drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c:1343-1363 (init_sub_direct sets init->offset = addr and calls nvbios_exec() recursively, restoring afterwards), :2302-2319 (nvbios_exec: init->nested++ / nested-- with no limit; the counter is bookkeeping only)
Longstanding interpreter.

This was found during an assisted (AI-supported) static source review of
mainline 8a65162514fbad21a2c58ffed65f42b27e92b163 (v7.4-rc2 window, 2026-11-08). The patch below has NOT been compiled and has
NOT been runtime tested, and no reproducer was produced - the triggering
conditions are described above instead. Please treat this as a report for
verification rather than a battle-tested fix.

---
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
index 3f888fb979..93d090d529 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
@@ -2304,6 +2304,10 @@ nvbios_exec(struct nvbios_init *init)
struct nvkm_bios *bios = init->subdev->device->bios;

init->nested++;
+ if (init->nested > 16) {
+ error("init table nesting too deep\n");
+ return -EINVAL;
+ }
while (init->offset) {
u8 opcode = nvbios_rd08(bios, init->offset);
if (opcode >= ARRAY_SIZE(init_opcode) ||