Skip to content

fix(libkernel): protect app metadata state across threads and validate icon stream read - #5704

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
fogydes:fix/libkernel-app-metadata-concurrency
Oct 15, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
fogydes:fix/libkernel-app-metadata-concurrency

Conversation

@fogydes

@fogydes fogydes commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

  • Synchronizes ensureTitleLoaded and ensureIconLoaded in AppMetadata.cpp with std::mutex g_metadataMutex to prevent concurrent guest threads from racing on title/titleId/param initialization and icon buffer allocation.
  • Protects against negative or zero file.tellg() results on error or unseekable streams (file.fail() || fileSize <= 0), preventing integer wrap into std::bad_alloc.
  • Validates file.gcount() and stream state after reading icon data to ensure the buffer is fully read before PNG header validation.
  • Adds guest_app_icon_tests (core/libs/tests/GuestAppIcon.cpp) registered in ctest, verifying:
    • Missing icon returns false from HasAppIcon and throws on GetAppIconData
    • Valid PNG fixture is read, validated, and returned
    • Empty or unreadable file (tellg() <= 0) throws the expected error
    • Corrupted non-PNG signature throws the expected error
    • Concurrent reader threads safely query icon metadata without race conditions
  • Dropped unreachable destination buffer guard in copyToFixedBuffer and dropped unsourced 64 MiB size cap per review feedback.

Tested

  • Built and ran guest_app_icon_tests passing all sub-cases (--no-icon, --valid-icon, --empty-icon, --corrupt-png, --concurrent).
  • Conventions check passed with python tools/check_conventions.py --base origin/main.
  • git diff --check passed cleanly with 0 errors.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@adriadam10 adriadam10 self-assigned this Oct 15, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the locking in ensureTitleLoaded/ensureIconLoaded is right: every reader goes through them, so the mutex covers the globals. Points before this can merge:

  • No test for any of the changes. The icon checks (negative/tellg failure, short read, oversize file) are reachable through HasAppIcon/GetAppIconData with a fixture icon0.png in a guest test registered in ctest; each check must fail without the change. "Tested" lists only param_json_utf8_tests, which doesn't touch this file.
  • AppMetadata.cpp:31: the !destination || destinationSize == 0 guard can't trigger; both callers pass g_title/g_titleId with sizeof. Drop it or show the call that reaches it.
  • AppMetadata.cpp:85: the 64 MiB cap has no source. Say where the limit comes from (real icon0.png sizes, FreeBSD/PS5 reference), or drop it: the tellg() <= 0 check already removes the wrap into bad_alloc, which is the case described in the body.

Welcome to push these and I'll look again.

@fogydes
fogydes force-pushed the fix/libkernel-app-metadata-concurrency branch from 8b5ba55 to c836b3b Compare October 15, 2026 12:16
@fogydes

fogydes commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

@adriadam10 Thanks for the review! Addressed all points in the latest push:

  1. Guest Test: Added guest_app_icon_tests (core/libs/tests/GuestAppIcon.cpp) registered in ctest, covering:
    • Missing icon0.png (--no-icon): verifies HasAppIcon returns false and GetAppIconData throws.
    • Valid PNG (--valid-icon): verifies successful read, 8-byte PNG signature check, and data return.
    • Empty/unreadable icon (--empty-icon): exercises file.fail() || fileSize <= 0 throwing icon0.png is empty or unreadable.
    • Corrupt header (--corrupt-png): exercises PNG signature validation throwing icon0.png is not a valid PNG file.
    • Thread concurrency (--concurrent): exercises multi-threaded access across 8 concurrent threads verifying mutex synchronization.
  2. Unreachable Guard: Dropped the !destination || destinationSize == 0 check in copyToFixedBuffer.
  3. 64 MiB Cap: Dropped the unsourced size cap in ensureIconLoaded(), retaining the file.fail() || fileSize <= 0 check to prevent the negative wrap into std::bad_alloc.

Rebased on latest main (b3f9f89e6) and verified check_conventions.py passes cleanly.

This was referenced Oct 15, 2026
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3891 core/libs/prx/libkernel/CMakeLists.txt
PR 4156 core/libs/prx/libkernel/CMakeLists.txt
PR 4396 core/libs/prx/libkernel/CMakeLists.txt
PR 4397 core/libs/prx/libkernel/CMakeLists.txt
PR 4422 core/libs/prx/libkernel/CMakeLists.txt
PR 4423 core/libs/prx/libkernel/CMakeLists.txt
PR 4424 core/libs/prx/libkernel/CMakeLists.txt
PR 4520 core/libs/prx/libkernel/CMakeLists.txt
PR 4521 core/libs/prx/libkernel/CMakeLists.txt
PR 4522 core/libs/prx/libkernel/CMakeLists.txt
PR 4523 core/libs/prx/libkernel/CMakeLists.txt
PR 4566 core/libs/prx/libkernel/CMakeLists.txt
PR 4599 core/libs/prx/libkernel/CMakeLists.txt
PR 4718 core/libs/prx/libkernel/CMakeLists.txt
PR 4740 core/libs/prx/libkernel/CMakeLists.txt
PR 4749 core/libs/prx/libkernel/CMakeLists.txt
PR 4760 core/libs/prx/libkernel/CMakeLists.txt
PR 4813 core/libs/prx/libkernel/CMakeLists.txt
PR 4972 core/libs/prx/libkernel/CMakeLists.txt
PR 5021 core/libs/prx/libkernel/CMakeLists.txt
PR 5835 core/libs/prx/libkernel/CMakeLists.txt
PR 5993 core/libs/prx/libkernel/AppMetadata/src/AppMetadata.cpp
core/libs/prx/libkernel/CMakeLists.txt
PR 6044 core/libs/prx/libkernel/CMakeLists.txt
PR 6107 core/libs/prx/libkernel/CMakeLists.txt
PR 6222 core/libs/prx/libkernel/CMakeLists.txt
PR 6226 core/libs/prx/libkernel/CMakeLists.txt

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked the re-push: the mutex covers every reader of the title/icon globals, the unreachable guard and the unsourced cap are gone, and guest_app_icon is registered in ctest. It passes here (build + ctest, 174 tests) and fails when AppMetadata.cpp is reverted to main. Merging once CI is green.

@adriadam10
adriadam10 merged commit 0b5d774 into boykopovar:main Oct 15, 2026
11 checks passed
@fogydes
fogydes deleted the fix/libkernel-app-metadata-concurrency branch October 19, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants