Skip to content

fix: guard entry reads when a directory is empty - #27

Merged
lepht merged 1 commit into
mainfrom
claude/adopt-upstream-723-empty-list
Aug 12, 2026
Merged

fix: guard entry reads when a directory is empty#27
lepht merged 1 commit into
mainfrom
claude/adopt-upstream-723-empty-list

Conversation

@lepht

@lepht lepht commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Closes #6. Adopted from upstream LoveRetro/NextUI#723 by @ericreinsmidt.

What this fixes

top->entries->items[top->selected] was loaded unconditionally in both the input and the render path, so an empty directory read an uninitialised pointer out of the capacity-8 allocation Array_new() never clears. The render path then ran assert(entry) over it — an abort in a debug build, not merely a latent read.

This is the fix #6 asked for, in its own words: hoist the count == 0 check above the load in both paths, rather than relying on the downstream guards. Upstream arrived at the same shape independently:

Entry* entry = total>0 ? top->entries->items[top->selected] : NULL;

The BTN_L1/BTN_R1 alpha-jump handlers get the same total>0 treatment, and the assert goes away. The render body is wrapped in if (entry), with an else branch that forces list_show_entry_names so an empty folder renders a usable state instead of a black screen.

Scope check

After this, every read of entries->items[top->selected] in nextui.c sits behind a count or total>0 check — I audited all of them, not just the two upstream touched. The remaining loop-indexed reads are bounded by count and were never at risk.

One thing this does not do: Array_new() still leaves its capacity uninitialised. That is now unreachable rather than fixed, and I've said so in the commit rather than claiming more than the change earns.

Upstream wrote this for the empty-root case (no ROMs, Tools hidden); it covers the ordinary empty Roms/ folder #6 describes by the same route. #1 separately sidesteps the Favorites-emptied-from-inside path by backing out to root — these are complementary, not redundant.

Testing

Cherry-picks clean onto main. nextui.c passes -fsyntax-only against the desktop platform headers; the only warnings are the pre-existing ones catalogued in #7, including the -Wparentheses hit at line 2681 — none introduced here. Not driven under Xvfb and not tested on hardware.

Sequencing note

This lands in nextui.c where #1 and #2 are actively working, and its BTN_L1/BTN_R1 guards sit exactly where #8's letter indicator would go. Worth re-verifying as that stack merges.

Why now

Upstream is mid-transition from GPL-3.0 to PolyForm Noncommercial (LoveRetro/NextUI#765). Their LICENSE is still GPL-3.0 today, so this is adoptable now; code taken after the transition would not be. Original authorship is preserved in the commit trailer.

Closes #6. Adopted from upstream LoveRetro#723 by Eric Reinsmidt,
taken while upstream is still GPL-3.0 (see LoveRetro#765).

top->entries->items[top->selected] was loaded unconditionally in both the
input and the render path, so an empty directory read an uninitialised
pointer out of the capacity-8 allocation Array_new() never clears. The
render path then ran assert(entry) over it, which is an abort in a debug
build rather than a latent read.

This is the fix #6 asked for: hoist the count check above the load in both
paths instead of relying on the downstream total>0 guards. The L1/R1 alpha
jump handlers get the same treatment, and the assert goes away.

Every remaining read of entries->items[top->selected] in this file is now
behind a count or total>0 check. Array_new() still leaves its capacity
uninitialised - that is unchanged and now unreachable, not fixed.

Upstream wrote this for the empty-root case (no roms, Tools hidden); it
covers the ordinary empty Roms/ folder #6 describes by the same route.

Co-Authored-By: Eric Reinsmidt <eric@reinsmidt.com>
@lepht
lepht force-pushed the claude/adopt-upstream-723-empty-list branch from 986eeb5 to aa29957 Compare August 12, 2026 04:14
@lepht
lepht merged commit 4a52e6c into main Aug 12, 2026
63 checks passed
@lepht
lepht deleted the claude/adopt-upstream-723-empty-list branch August 12, 2026 04:31
lepht pushed a commit that referenced this pull request Aug 12, 2026
Brings in the three upstream adoptions (#26, #27, #28) now on main.

One conflict, in the scroll-text block of nextui.c. Both sides edited
adjacent lines for unrelated reasons:

  - this branch added SCREEN_CONTEXTMENU to the outer screen exclusion list
  - #27 added a total>0 guard to the inner condition

Resolved as the union of both, which is the only correct answer here.
Taking ours alone drops total>0 and reopens #6 two lines above the
top->entries->items[top->selected] read; taking theirs alone drops the
context menu exclusion and renders scroll text over it.

Git auto-merged the other five guard sites #27 added. Verified after the
merge: no assert(entry) remains, every read of entries->items[top->selected]
sits behind a count or total>0 check, nextui.c is syntax-clean against the
desktop platform headers, and make test passes.
lepht pushed a commit that referenced this pull request Aug 12, 2026
Carries the main merge (and with it #26, #27, #28) up the stack.

Same single conflict as one level down, in the same two lines of nextui.c:
this branch had added SCREEN_SEARCH to the outer screen exclusion list on
top of SCREEN_CONTEXTMENU, while #27 added a total>0 guard to the inner
condition. Resolved as the union again - all four screen exclusions kept,
total>0 kept.

Verified after the merge: no assert(entry) remains, every read of
entries->items[top->selected] is guarded, nextui.c is syntax-clean against
the desktop platform headers, and make test passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Out-of-bounds read on empty folders

2 participants