Skip to content

Comments

feat: PRX background MP3 audio, CPU clock control, and FTP server#18

Merged
AndrewAltimit merged 29 commits intomainfrom
feat/prx-audio-power-ftp
Feb 15, 2026
Merged

feat: PRX background MP3 audio, CPU clock control, and FTP server#18
AndrewAltimit merged 29 commits intomainfrom
feat/prx-audio-power-ftp

Conversation

@AndrewAltimit
Copy link
Owner

Summary

  • Background MP3 playback in the PSP kernel plugin (PRX) -- plays MP3 files from ms0:/MUSIC/ while games are running, using sceAudiocodec for hardware decoding and the SRC audio channel to avoid conflicting with game audio
  • Game-safe codec initialization -- waits for games to load AVCODEC modules during their own init (retry loop up to 60s) instead of loading modules ourselves, preventing crashes
  • Streaming buffer refill -- 4KB chunk reads per frame instead of batch 60KB reads, eliminating audio hiccups
  • CPU clock control -- hooks for adjusting PSP clock speeds from the overlay
  • FTP file server -- wireless file transfer support
  • rust-psp SDK update -- pulls in merged PR test: comprehensive testing gap analysis (9 phases) #7 with SrcChannel, AudiocodecDecoder, find_sync, skip_id3v2 abstractions contributed back from this work

Key technical details

The codec buffer layout was reverse-engineered from Sony's mp3play.prx:

  • Field [10] must duplicate field [7] (source length) -- this was the critical fix for decode failures
  • sceAudiocodecGetEDRAM is required (user RAM rejected with 0x807F0001)
  • SRC channel is a global singleton separate from the 8 PCM channels, ideal for plugin audio

Test plan

  • PRX builds successfully (RUST_PSP_BUILD_STD=1 cargo +nightly psp --release)
  • Tested on real PSP hardware -- audio plays without game crashes
  • Verified smooth playback with streaming buffer refill (no hiccups)
  • Confirmed game audio coexists with background music via SRC channel

Generated with Claude Code

AI Agent Bot and others added 27 commits February 14, 2026 23:50
Implement three features that close gaps with IR Shell:

- PRX audio: kernel-mode background MP3 playback using runtime NID
  resolution via find_function() for sceAudio and sceMp3 drivers, with
  dynamic module loading fallback, playlist scanning, decode thread,
  and atomic playback controls

- CPU clock control: resolve scePower driver NIDs at hook init, cycle
  through 4 clock presets (333/266/222/133 MHz), display CPU MHz and
  battery percentage in overlay status line

- FTP file server: poll-based TCP server (FtpServer) modeled after
  RemoteListener, wired into main loop via FtpToggle command output
  variant, processes LIST/GET/PUT/MKDIR/DELETE/STAT/QUIT against VFS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
scan_playlist() now recursively descends into subdirectories (up to 4
levels deep) when scanning the music directory. Also increases playlist
capacity from 16 to 32 tracks and max path length from 80 to 128 bytes
to accommodate deeper folder structures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix wrong NID for sceMp3NotifyAddStreamData: was 0x0DB149F4
  (duplicate of sceMp3ReleaseMp3Handle), now correct 0x29BFF3EC
- Fix MP3 module dependency load order: mpegbase -> mpeg -> libmp3
  (was loading mpeg before its dependency mpegbase)
- Add mpeg_vsh.prx as fallback for some firmware versions
- Add sceAudio_Service module name variant for wider firmware compat
- Add libmp3 module name variant for sceMp3 resolution
- Prefer kernel-mode library names (_driver) first for sceAudio

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceMp3 is a user-mode library that sctrlHENFindFunction cannot resolve
from kernel context. Replace with sceAudiocodec (type 0x1002) which is
a kernel-accessible codec driver. Also always start the audio thread
regardless of autoplay config (start paused if autoplay=false).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sctrlHENFindFunction on PRO-C2 can only find kernel driver modules
(sceAudio, sceCtrl, scePower). Neither sceMp3 nor sceAudiocodec were
discoverable because the AV modules weren't loaded into the system.

Now resolves sceUtilityLoadModule via find_function from
sceUtility_Driver and uses it to load PSP_MODULE_AV_AVCODEC (0x0300),
PSP_MODULE_AV_MPEGBASE (0x0301), and PSP_MODULE_AV_MP3 (0x0302).

Also adds NULL module name fallback search (searches all loaded modules),
tries sceMp3 first then sceAudiocodec as fallback, and logs return
codes from every module load call for diagnostics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…alking

sctrlHENFindFunction on PRO-C2 6.20 only searches kernel-mode modules,
so sceMp3 and sceAudiocodec NIDs were invisible even after successfully
loading them via sceUtilityLoadModule. This adds a fallback path that
resolves sceKernelFindModuleByName from ModuleMgrForKernel, uses it to
locate the loaded user-mode module struct, then walks the SceModule's
export table (ent_top/ent_size -> SceLibraryEntryTable entries) to find
function pointers by NID.

Resolution order: sctrlHENFindFunction (fast, kernel) -> export walk (user).
Includes diagnostic logging of found modules and export table addresses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceKernelFindModuleByName returned 0x03F0DF67 (unaligned, not in any
valid PSP memory range), crashing on dereference. This adds:

- Pointer validation: alignment check + memory range check before any
  dereference (KSEG0/KSEG1/user space ranges)
- Alternative API: try sceKernelSearchModuleByName (LoadCoreForKernel,
  NID 0xF0CAB543) which may behave differently on PRO-C2
- UID fallback: if FindModuleByName returns a non-pointer value, try
  treating it as a SceUID and converting via FindModuleByUID
- Multiple offset probing: try ent_top/ent_size at 0x58/0x5C, 0x40/0x44,
  and 0x24/0x28 (different firmware SceModule layouts)
- Hex dump: dump raw struct bytes at key offsets for remote diagnosis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… PRXs

sceKernelFindModuleByName returns garbage (0x03F12969, unaligned) on
PRO-C2 6.20. Replace with two new strategies:

1. Module enumeration: resolve sceKernelGetModuleIdList (NID 0x644CF325)
   and sceKernelQueryModuleInfo (NID 0x748CBED9) from ModuleMgrForKernel.
   Enumerate all loaded modules, log their names + text addresses, and
   match by name pattern to find MP3/codec modules. Use text_addr to
   locate the embedded SceModuleInfo header and walk its export table
   (ent_top at +0x24, ent_end at +0x28).

2. Kernel-load flash0 PRXs: after sceUtilityLoadModule (which loads into
   user space), also try sceKernelLoadModule for flash0:/kd/*.prx files.
   When loaded from kernel context, modules may register in the kernel
   module list where sctrlHENFindFunction can discover them.

Removed broken FindModuleByName/SearchModuleByName/FindModuleByUID code.
Fixed excessive log spam (was logging every find_module_ptr call).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When MP3/codec NIDs can't be resolved via standard kernel APIs,
scans user memory (0x08800000-0x0A000000) and kernel memory
(0x88000000-0x88800000) for known NID values. Writes results
to ms0:/seplugins/oasis_memdump.txt with hex context dumps
and module name string search.

This gives us hard diagnostic data to understand where
sceUtilityLoadModule actually puts the AV modules and their
export tables on PRO-C2 6.20.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Based on the NID memory scan results showing sceAudiocodec NIDs in
user memory at 0x08B63C44 (the game's import NID table), adds a
stub extraction strategy:

1. Scan user memory for a cluster of known sceAudiocodec NIDs
2. Walk backwards/forwards to find the full sorted NID table
3. Scan for a pointer to the NID table (SceLibraryStubTable entry)
4. Read the stub table and decode MIPS J-instructions to extract
   the real function addresses
5. Populate sceAudiocodec function pointer statics

Handles both J-instruction format (user-to-user) and logs syscall
stubs (user-to-kernel) for separate handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The NID memory scan (32MB, checking 13 NIDs per word + string search)
takes ~5+ seconds and was causing the PSP to crash before the fast
stub extraction code could run.

Swap order: run try_codec_stub_extraction() first (~0.1s, two
targeted scans) then fall back to the diagnostic NID dump only if
extraction fails.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the text-based NID scan with raw binary dumps of user memory
(24MB, 0x08800000-0x0A000000) and kernel memory (8MB, 0x88000000-
0x88800000) to ms0:/seplugins/. Writes in 64KB chunks with 1MB
progress logging. These dumps can be analyzed on PC with Python to
find the syscall table and resolve sceAudiocodec function pointers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the unsuccessful direct stub decoding with a syscall table
lookup approach discovered through offline memory dump analysis:

1. Extract sceAudiocodec syscall numbers from game's import stubs
   (jr $ra + syscall N format, N = insn1 >> 6)
2. Find sceAudio's import stubs nearby in user memory
3. Extract sceAudioChReserve's syscall number as a reference
4. Cross-reference with the already-resolved kernel function pointer
   (from sctrlHENFindFunction) to compute the syscall table base:
   table_base = kernel_ptr - syscall_num * 4
5. Look up sceAudiocodec kernel function pointers from the table

This is firmware-independent since it computes the table base
dynamically rather than hardcoding it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sceAudio and sceAudiocodec import tables can be far apart in
the game binary (1.75MB observed). Expand the NID search from
+/-64KB to the full user memory range (0x08800000-0x0A000000).
The stub table pointer search remains localized to +/-16KB from
the NID table since it's always in the same ELF section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of trying to resolve kernel function pointers via the syscall
table (which failed because sctrlHENFindFunction returns export table
entries, not syscall table entries), use the game's import stub
addresses directly as function pointers.

Each stub is `jr $ra; syscall N` -- when called from kernel mode, the
syscall instruction traps to the kernel's syscall handler which
dispatches to the actual codec function and returns to our caller.
This is the same mechanism the game uses to call these functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes for PRX audio playback:
1. Reserve channel 7 (fallback 6, then -1) instead of auto-select
   to avoid stealing game audio channels and causing crashes
2. Increase CODEC_BUF_WORDS from 32 to 65 -- sceAudiocodec needs
   65 u32 entries and was overflowing into adjacent memory
3. Add error logging for all codec init and decode calls to
   diagnose playback issues

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… module name

Two key fixes:
1. Allocate codec, PCM, and read buffers in user memory partition
   via sceKernelAllocPartitionMemory. Codec functions called through
   syscall stubs validate pointers are in user range (0x08800000+),
   so kernel-space statics (0x88xxxxxx) get rejected with 0x807F00FD.
2. Add "sceAvcodec_wrapper" as first module name to try for
   sctrlHENFindFunction -- this is the actual kernel driver name
   used by the working mp3play.prx reference plugin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceAudiocodecDecode needs codec[7]=input bytes available and
codec[9]=output buffer size before each call. Missing these caused
every decode to fail with 0x807F00FD. Also cap consecutive decode
failures at 100 to prevent infinite spin and log flooding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PSP has 8 regular PCM channels (0-7) that games claim exclusively.
Reserving ANY of them causes games to crash when they init audio.

Switch to the SRC (Sample Rate Conversion) channel -- a dedicated
output path separate from the 8 regular channels. This is the
standard approach for PSP plugins doing background audio.

API: sceAudioSRCChReserve/sceAudioSRCOutputBlocking instead of
sceAudioChReserve/sceAudioOutputBlocking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
From mp3play.prx disassembly, sceAudiocodecDecode requires codec[10] set
to the input data size (same as codec[7]). Also dumps codec buffer state
after init and first-frame MP3 header bytes for diagnosis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceAudiocodecGetEDRAM allocates from the PSP's shared 2MB eDRAM pool
(used by both GE and media engine). When games try to use their own
audio/video codecs, they find the eDRAM already claimed and crash.

Instead, we allocate 16KB of working memory from the user RAM partition
and set codec[5] manually, completely avoiding the eDRAM pool. This
lets games use their full eDRAM allocation while our MP3 decoder works
from regular RAM.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The codec requires actual EDRAM for decode (user RAM fails with
0x807F0001). Reverts to sceAudiocodecGetEDRAM.

The likely cause of game crashes is sceUtilityLoadModule loading
AVCODEC/MPEGBASE/MP3 modules that conflict with the game's own module
loading. Now tries sceAudiocodec first (resolves via sctrlHENFindFunction
without loading any modules). Only falls back to LoadModule + sceMp3 as
a last resort.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceAudiocodec is only available via sctrlHENFindFunction after the
AVCODEC module is loaded. Instead of loading it ourselves (which can
conflict with the game's own module loading and cause crashes), wait
for the game to load it during its init sequence.

Retries sceAudiocodec resolution every 2s for up to 15s. Only falls
back to sceUtilityLoadModule if the game never loaded the modules
(low conflict risk since the game doesn't use audio codecs).

Also increases initial audio thread delay from 1s to 3s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase read buffer from 32KB to 64KB (fewer I/O stalls during decode)
- Bump audio thread priority from 0x1E to 0x18 for smoother scheduling
- Strip all diagnostic logging from decode loop (was causing file I/O
  contention with MP3 reads on every track init and first 5 frames)
- Only error paths still log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Some games take longer to activate their audio subsystem. Retry every
2s for up to 60s before falling back to loading modules ourselves.
Also removed the per-retry log line to keep output clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of waiting until the buffer is nearly empty then doing one
large blocking read (~60KB), stream 4KB chunks on every decode cycle.
This spreads I/O across many frames so no single read stalls the
audio output. Buffer compaction happens when half consumed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates the psp dependency to include the merged rust-psp PR #7 which
contributes our reverse-engineered abstractions back to the SDK:
SrcChannel, AudiocodecDecoder, find_sync, skip_id3v2, and removes
the incorrect kernel feature gate from sys::codec.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

Gemini AI Code Review

Issues

  • [BUG] crates/oasis-core/src/transfer/mod.rs:247 - Buffer overflow risk in fill_stream_data

    • The dst_ptr and to_write values returned by get_info are used directly in sceIoRead. If the MP3 backend provides a to_write value exceeding the actual size of the pre-allocated S_MP3_BUF (64KB), it will cause a memory corruption.
    • Add a bounds check: let read_limit = to_write.min(MP3_BUF_SIZE as i32); and use read_limit in sceIoRead.
  • [WARNING] crates/oasis-core/src/transfer/mod.rs:218 - Potential infinite loop in poll

    • The while let Some(pos) = conn.read_buf.iter().position(|&b| b == b'\n') loop drains the buffer. If process_ftp_request or other logic fails to clear enough data or if the buffer logic is manipulated by a malicious client, it could stall the main thread.
    • Implement a maximum command processing limit per poll cycle to ensure the main loop remains responsive.
  • [WARNING] crates/oasis-plugin-psp/src/audio.rs:1044 - Insecure use of transmute for syscall stubs

    • Transmuting a raw u32 address (from stub_addr) to a function pointer is brittle. While common in PSP homebrew, it bypasses all safety checks and assumes the stub layout is exactly as expected.
    • Add explicit verification that the memory at stub_addr is executable and matches the jr $ra; syscall pattern before transmuting.

Previous Issues

(none)

Suggestions

  • crates/oasis-core/src/transfer/mod.rs:188 - Connection leak on accept error

    • If backend.accept() returns an error, it is logged but not handled. While not a leak yet, it could indicate underlying socket exhaustion.
    • Consider a back-off strategy or closing the listener if errors persist.
  • crates/oasis-plugin-psp/src/audio.rs:1425 - Magic numbers in CLOCK_PRESETS

    • The clock frequencies are hardcoded.
    • Move these to a named constant or configuration struct for better maintainability.

Notes

  • The PRX audio implementation is impressively thorough in its NID resolution strategies, covering kernel functions, user-mode exports, and stub extraction.
  • The use of the SRC (Sample Rate Conversion) channel is a correct architectural choice for a PSP plugin to avoid interfering with game audio.
  • The transition of the FTP server from a VFS-based IPC to a direct poll-based implementation in the app layer improves responsiveness and reduces VFS overhead.

Reaction


Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews.

Automated fix by Claude in response to Gemini/Codex review.

Iteration: 1/5

Co-Authored-By: AI Review Agent <noreply@anthropic.com>
@AndrewAltimit
Copy link
Owner Author

Review Response Agent (Iteration 1)

Status: Changes committed and pushed

Commit: d763edb

Fixed Issues

  • [WARNING] FTP poll loop command limit (crates/oasis-core/src/transfer/mod.rs:237): Added MAX_CMDS_PER_POLL = 16 cap to the while let loop that processes FTP commands. Previously, a client sending many newline-separated commands in a single TCP segment could cause the main loop to stall while processing them all. Now capped at 16 commands per connection per poll cycle; remaining buffered commands are processed on subsequent frames.

Ignored Issues

  • [BUG] fill_stream_data buffer overflow (audio.rs, misattributed to transfer/mod.rs:247): Invalid. The dst_ptr and to_write values come from sceMp3GetInfoToAddStreamData, which returns a pointer into sceMp3's own internally-managed stream buffer and the exact byte count it expects. This is the standard sceMp3 API usage pattern -- the library manages its own buffer. There is no S_MP3_BUF being overflowed. The file reference in the review is also wrong (audio.rs, not transfer/mod.rs).

  • [WARNING] Insecure transmute for syscall stubs (audio.rs:~1044): Already mitigated. The code at lines 883-889 validates the stub pattern (insn0 == 0x03E0_0008 && (insn1 & 0x3F) == 0x0C -- jr $ra + syscall N) before any transmute occurs. The reviewer's suggestion to "add explicit verification" is already implemented.

  • Suggestion: Connection leak on accept error: Speculative. The accept() error is logged but no connection object is created on error, so there's nothing to leak. Back-off is unnecessary for a non-blocking accept that occasionally fails.

  • Suggestion: Magic numbers in CLOCK_PRESETS: Style preference. The clock frequencies are PSP hardware constants that are well-known in the homebrew community and self-documenting in context.

Deferred to Human

  • None

Notes

  • The Codex review failed to produce output (CLI error in logs), so there was no Codex feedback to address.
  • All 2,123 workspace tests pass. Formatting and clippy checks are clean.

Automated summary of agent fixes.

@github-actions
Copy link

Gemini AI Incremental Review

This is an incremental review focusing on changes since the last review.

Issues

  • [CRITICAL] crates/oasis-plugin-psp/src/audio.rs:1347 - Buffer overflow risk in fill_stream_data

    • The to_write value from get_info is used directly as the length for sceIoRead into dst_ptr. If SceMp3 returns a value exceeding the destination buffer capacity, this could cause memory corruption.
    • Add a safety clamp: to_write.min(MP3_BUF_SIZE as i32) or verify against actual buffer limits for robust kernel-mode code.
  • [BUG] crates/oasis-plugin-psp/src/audio.rs:1843 - Potential infinite loop in play_track_codec

    • The frame_count limit of 100 consecutive failures only triggers break if ret < 0. If ret >= 0 but consumed == 0, the loop increments buf_pos by 1 without resetting or checking frame_count, potentially causing an extremely long or infinite loop on malformed files.
    • Ensure the retry counter/limit also covers the consumed == 0 case.

Previous Issues

  • [RESOLVED] crates/oasis-core/src/transfer/mod.rs:237 - FTP poll loop command limit. Capped at 16 commands per poll via MAX_CMDS_PER_POLL.
  • [RESOLVED] crates/oasis-plugin-psp/src/audio.rs:883 - Insecure transmute for syscall stubs. Validation of jr $ra; syscall pattern added before transmutation.
  • [RESOLVED] crates/oasis-plugin-psp/src/audio.rs:1332 - Buffer overflow risk in fill_stream_data.

Suggestions

  • crates/oasis-plugin-psp/src/audio.rs:1817 - Non-atomic access to UMEM_READ

    • UMEM_READ and other buffer pointers use core::ptr::read_volatile in the audio thread but are set in alloc_codec_user_mem. Ensure pointers are fully initialized before thread start or use atomics if mid-playback reallocation is possible.
  • crates/oasis-plugin-psp/src/overlay.rs:230 - Magic numbers in CLOCK_PRESETS

    • Clock frequencies (333, 266, 222, 133 MHz) are hardware-specific. Named constants or a configuration struct would improve readability.

Notes

  • The shift from VFS-based IPC to direct polling for FTP significantly simplifies architecture and improves responsiveness.
  • NID resolution via stub extraction is a clever fallback when games have already loaded necessary modules.
  • Correct prioritization of SRC channel prevents audio mixing conflicts with games.

Reaction


Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews.

On malformed MP3 files, sceAudiocodec can return success (ret >= 0) but
consume 0 bytes. Without a limit, the decode loop would advance buf_pos
by 1 byte at a time while the refill logic keeps topping up the buffer,
effectively scanning the entire file byte-by-byte. Cap at 100
consecutive zero-consumed iterations matching the existing error limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@AndrewAltimit
Copy link
Owner Author

Review Response Agent (Iteration 2)

Status: No changes needed

Fixed Issues

  • [BUG] play_track_codec zero-consumed infinite loop (crates/oasis-plugin-psp/src/audio.rs:2127): Added zero_consumed counter that caps consecutive consumed == 0 iterations at 100 and breaks the decode loop. Previously, when sceAudiocodec returned success but consumed 0 bytes, buf_pos advanced by only 1 byte per iteration while the streaming refill logic kept topping up the buffer, causing the loop to scan the entire file byte-by-byte on malformed MP3s. Counter resets to 0 on any successful consume.

Ignored Issues

  • [CRITICAL] fill_stream_data buffer overflow (audio.rs:1347): Invalid, repeated from iteration 1. Already addressed by admin: dst_ptr and to_write come from sceMp3GetInfoToAddStreamData, which returns a pointer into sceMp3's own internally-managed stream buffer. There is no user buffer being overflowed.

  • Suggestion: Non-atomic access to UMEM_READ: Speculative. UMEM_READ and other buffer pointers are set once in alloc_codec_user_mem() during init before the audio thread starts. No mid-playback reallocation occurs, so atomics are unnecessary.

  • Suggestion: Magic numbers in CLOCK_PRESETS: Style preference, repeated from iteration 1. Already addressed by admin: PSP hardware constants well-known in homebrew community.

Deferred to Human

  • None

Notes

  • All workspace tests pass. Clippy and formatting checks clean.
  • Commit 2de2768 pushed to feat/prx-audio-power-ftp.

The agent reviewed feedback but determined no code changes were required.

@github-actions
Copy link

Gemini AI Incremental Review

This is an incremental review focusing on changes since the last review.

Issues

  • [CRITICAL] crates/oasis-plugin-psp/src/audio.rs:1884 - Buffer overflow risk in fill_stream_data

    • The to_write value from get_info is used as the length for sceIoRead into dst_ptr without verification. If the sceMp3 library returns a value exceeding the internal buffer capacity, memory corruption will occur.
    • Clamp the read size: to_write.min(MP3_BUF_SIZE as i32).
  • [BUG] crates/oasis-plugin-psp/src/audio.rs:2128 - Potential infinite loop in play_track_codec

    • When consumed == 0, the loop increments buf_pos by 1 and continues. On malformed files, this can lead to an extremely long loop scanning the file byte-by-byte.
    • While a zero_consumed counter exists, ensure the logic resets correctly and that the 100-iteration limit is sufficient for all edge cases.

Previous Issues

  • [RESOLVED] crates/oasis-core/src/transfer/mod.rs:241 - FTP poll loop command limit. Fixed with MAX_CMDS_PER_POLL limit.
  • [RESOLVED] crates/oasis-plugin-psp/src/audio.rs:883 - Insecure transmute for syscall stubs. Validation of jr $ra; syscall pattern added.
  • [RESOLVED] crates/oasis-plugin-psp/src/audio.rs:1347 - Buffer overflow risk in fill_stream_data. (Note: Previous review misattributed this to transfer/mod.rs).

Suggestions

  • crates/oasis-plugin-psp/src/audio.rs:1918 - Non-atomic access to UMEM_READ

    • Pointer is read via read_volatile but initialized in a different function. Ensure initialization happens before the audio thread starts to avoid race conditions.
  • crates/oasis-plugin-psp/src/overlay.rs:230 - Magic numbers in CLOCK_PRESETS

    • Hardcoded frequencies (333, 266, 222, 133) should be defined as named constants for clarity.

Notes

  • The technical implementation of NID resolution via stub extraction and the use of the SRC channel for game-safe audio mixing is high quality.
  • The incremental review confirms that critical security validations for syscall stubs are in place.

Reaction


Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews.

@AndrewAltimit
Copy link
Owner Author

Review Response Agent (Iteration 3)

Status: No changes needed

Fixed Issues

  • None required

Ignored Issues

  • [CRITICAL] fill_stream_data buffer overflow (audio.rs:1884): Invalid, repeated for the 3rd time. Already addressed by admin in iterations 1 and 2. dst_ptr and to_write come from sceMp3GetInfoToAddStreamData, which returns a pointer into sceMp3's own internally-managed ringbuffer and the exact byte count it expects. This is the standard sceMp3 API pattern -- the Sony system module manages its own buffer. There is no user buffer being overflowed.

  • [BUG] play_track_codec infinite loop (audio.rs:2128): Already fixed in iteration 2 (commit 2de2768). The zero_consumed counter correctly caps consecutive consumed == 0 iterations at 100 and breaks. The counter resets to 0 on any successful consume (line 2139). The latest review acknowledges the fix exists but vaguely asks to "ensure the logic resets correctly" -- verified it does.

  • Suggestion: Non-atomic access to UMEM_READ (audio.rs:1918): Speculative, repeated from iterations 1 and 2. Already addressed by admin: pointers are set once in alloc_codec_user_mem() during init before the audio thread starts. No mid-playback reallocation occurs.

  • Suggestion: Magic numbers in CLOCK_PRESETS (overlay.rs:230): Style preference, repeated from iterations 1 and 2. Already addressed by admin: PSP hardware constants well-known in homebrew community.

Deferred to Human

  • None

Notes

  • The Codex review CLI failed with an error (visible in stderr logs), so there was no Codex feedback to address.
  • The Gemini review is repeating all four issues from iterations 1 and 2 verbatim, with no new issues identified.
  • All 2,129 workspace tests pass. Clippy and formatting checks are clean.
  • No code changes made -- all feedback has been previously addressed.

The agent reviewed feedback but determined no code changes were required.

@AndrewAltimit AndrewAltimit merged commit aa6f638 into main Feb 15, 2026
7 checks passed
@AndrewAltimit AndrewAltimit deleted the feat/prx-audio-power-ftp branch February 15, 2026 12:21
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.

1 participant