From ca658c9dde9f4375ce115d0dcf378adf40367758 Mon Sep 17 00:00:00 2001 From: seedds Date: Sun, 25 Oct 2026 09:36:51 +0800 Subject: [PATCH] NVN BufferInitialize: a buffer past the end of its pool is a one-line note, not a red line (general, found on Breath of the Wild). Breath of the Wild creates one 256 KB buffer (offset 0) over a 144 KB pool when the pause menu opens and near a save commit (Mac and iPhone: the same line, once each time). The NVN driver does not check a buffer's range against its pool, and the host never reads a buffer's size to address memory: every GPU access takes an address and a length from its command and resolves it in the pool's memory (mtl_addr / pool_find), which reports a range that leaves a pool on its own (RED_ONCE). The buffer's only use is BufferGetAddress and one texture-to-buffer copy (nvnCommandBufferCopyTextureToBuffer, called from main+0xec2b80 on the unpatched module) of the screen picture's own extent, which resolved inside the pool (no "destination is in no memory pool" line), then it is finalized in the same function; it is never mapped. Now a non-red [nvn] line, once per run. Checked on the Mac (BotW, a copy of the save): the pause menu opens with the inventory drawn as before and the line is a note; the host tests and the gfx run list pass. From PR #145 (v0lumehi). Also checked on current main: the host unit tests and the gfx run list pass. Side finding of the PR, not changed here: in the world, Breath of the Wild's uniform buffer binds of 0x750 bytes at +0xa00 of one-page pools straddle the pool end (pool_find returns -1 for them) --- dm/gfx/nvn/hle_nvn.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dm/gfx/nvn/hle_nvn.c b/dm/gfx/nvn/hle_nvn.c index 0666ce1..cc85d49 100644 --- a/dm/gfx/nvn/hle_nvn.c +++ b/dm/gfx/nvn/hle_nvn.c @@ -1778,7 +1778,10 @@ NVN(nvnBufferBuilderSetStorage, void, NVNbufferBuilder *b, NVNmemoryPool *p, ptr NVN(nvnBufferInitialize, NVNboolean, NVNbuffer *buf, const NVNbufferBuilder *b) { const buf_builder_t *bb = (const buf_builder_t *)b; if (!bb->pool || !bb->pool->mem) { LOG("[RED] BufferInitialize: pool has no storage (pool %p flags %#x offset %#llx size %#llx)", (const void *)bb->pool, bb->pool ? bb->pool->flags : 0, (unsigned long long)bb->off, (unsigned long long)bb->size); return 0; } - if ((uint64_t)bb->off + bb->size > bb->pool->size) LOG("[RED] BufferInitialize: past the end of the pool (offset %#llx size %#llx pool %#llx)", (unsigned long long)bb->off, (unsigned long long)bb->size, (unsigned long long)bb->pool->size); + // The NVN driver does not check a buffer's range against its pool, and the host's own GPU access never goes by the buffer: it takes an address and a length from the command (mtl_addr resolves it + // in the pool's memory and reports a range that leaves the pool as its own red line). Breath of the Wild creates a 256 KB buffer over a 144 KB pool for a screen readback that feeds a + // compressing encoder (when the pause menu opens, and near a save commit): the buffer's one use is a texture-to-buffer copy of the picture's own extent, which lies inside the pool. Not an error by itself: one line. + if ((uint64_t)bb->off + bb->size > bb->pool->size) ONCE_LOG("BufferInitialize: a buffer reaches past the end of its pool (offset %#llx size %#llx pool %#llx); only an access that leaves the pool is an error, and it is reported where it happens", (unsigned long long)bb->off, (unsigned long long)bb->size, (unsigned long long)bb->pool->size); *(buf_t *)buf = (buf_t){MAGIC('B', 'U', 'F', 0), bb->pool, bb->off, bb->size}; OLOG("BufferInitialize %p pool=%p off=%#llx size=%#llx", (void *)buf, (const void *)bb->pool, (unsigned long long)bb->off, (unsigned long long)bb->size); return 1;