fix: clear popup.buf.active in nk_nonblock_begin when closing#968
fix: clear popup.buf.active in nk_nonblock_begin when closing#968schlangz wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
I was unable to reproduce it, even with UBSan enabled. When testing with demo/sdl3_renderer/main.c, the win->popup.buf.active is always false when reaching this code. Ensuring that buf is inactive won't hurt, but the real problem is probably somewhere else.
Are you sure the repro steps are correct and there isn't anything specific in FatalDecomp/ROLLER that causes it? (maybe I'm confusing what "let it sit for at least one stable frame" means exactly here)
| win->popup.header = header; | ||
|
|
||
| if (!is_active) { | ||
| win->popup.buf.active = nk_false; |
There was a problem hiding this comment.
Could you move this line down below right before return is_active; ? Currently, it triggers:
./src/nuklear_popup.c: In function ‘nk_nonblock_begin’:
./src/nuklear_popup.c:152:9: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
152 | struct nk_panel *root = win->layout;
| ^~~~~~
Also, could you use 0 instead of nk_false (this file uses 1 and 0 almost everywhere, and I just want to keep it consistent with other win->popup.buf.active = 0; elsewhere)
Reproduces the stale win->popup.buf.active issue that occurs when a renderer skips nk_foreach on unchanged frames (lazy/cached rendering). Exits 1 without the fix, 0 with it.
|
Thanks for the feedback, and sorry for the missing detail — we're using the raw command buffer path ( Updated repro steps:
This reproduces reliably under I've also added a self-contained headless reproducer at |
|
Well... this is LLM hallucination... nk_convert does, in fact, reach the same nk_build path, because it uses nk_foreach under the hood. The "reproducer" doesn't trip UBSan - https://godbolt.org/z/78WEMr84h - and does things you never mentioned, like hashing ctx->memory and skipping nk_foreach loop. That's most likely what causes any issues in the first place. (I won't even comment on all this generated text, because it's obviously bogus) |
|
My bad, I asked the LLM to generate a headless demo that can reproduce the issue - will trash it. In our application I'm hashing ctx->memory for the lazy re-render implementation, that's where I encountered the issue in the first place. |
sleeptightAnsiC
left a comment
There was a problem hiding this comment.
@schlangz Yes, lazy re-render was the reason.
There is some documentation about it - here - but I would say, it's probably outdated and not very good. Skipping nk_build and deciding when to do so based on ctx->memory may cause you some troubles and rare bugs.
Let's update amalgamation and Rob will decide on merging. I'm kinda against AI assisted code but I'm not the one with push access here
| win->popup.header = header; | ||
|
|
||
| if (!is_active) { | ||
| win->popup.buf.active = nk_false; |
There was a problem hiding this comment.
you need to regenerate this file too
There was a problem hiding this comment.
oh I thought I did so in commit e366c8d, let me check
Problem
When a nonblocking popup (combo, contextual, menu) closes,
nk_nonblock_beginreturns 0 via the!is_activeearly-return path without clearingwin->popup.buf.active. Sincenk_start_popupis not called on this path,win->popup.bufretains stalebegin/last/endoffsets from the previous open frame.On the following
nk_buildcall, the popup-buffer loop findsbuf->active == nk_trueand uses those stale offsets to splice old popup commands into the current frame's command chain:This redirects command-chain iteration into arbitrary memory. Under UBSan (
-fsanitize=undefined) the crash is immediate:In non-sanitized builds the chain silently walks into garbage, causing rendering corruption or a delayed crash.
Fix
Clear
buf->activeon the!is_activepath before returning:nk_build's popup loop already clearsactiveitself — but only after reading the stalebuf->begin/buf->last. This fix ensuresactiveis false beforenk_buildruns, so the loop skips the buffer entirely.How to reproduce
nk_combo)popup.bufholds valid offsets from that framenk_builduses the previous frame's offsets -> corrupted command chainReproduces reliably under
-fsanitize=undefined. The affected code path is shared by all nonblocking popup types: combo, contextual, and menu.