Skip to content

feat(libkernel): implement dup and dup2 - #2441

Closed
Recapi wants to merge 1 commit into
boykopovar:mainfrom
Recapi:feat/libkernel-dup
Closed

Recapi wants to merge 1 commit into
boykopovar:mainfrom
Recapi:feat/libkernel-dup

Conversation

@Recapi

@Recapi Recapi commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

What

dup and dup2 in libkernel.

  • Host descriptors (files, pipes, stdio) go to the host dup/dup2 (_dup/_dup2 on Windows), which share the open file, offset and flags as FreeBSD does. dup2(fd, fd) returns fd when it is open.
  • Guest sockets (numbers from GuestSockets::FirstDescriptor): dup gives a new socket number that shares the socket, which stays open until its last number is closed; dup2 onto another socket number replaces what was there and keeps later socket numbers above it.
  • Errors as in FreeBSD kern_dup: a negative or closed source, or a negative target, is -1 with EBADF; EMFILE and other host failures are passed through.
  • Throws: moving a guest socket onto a host number or a host descriptor into the socket range (the two numberings are separate here), and duplicating a Windows directory descriptor (its emulated read position would not be shared).

Prior work: #1855 (dup) and #1870 (dup2) were closed because no title stopped at them on main then.

Log

Linux (Ubuntu, GCC 15.2.0), main 8b393b6 with nothing else merged; catalog release ZIPs checked against their sha256, eboot.bin and bundled modules unwrapped from their SELF containers, relinked with relinker --registry, run in a bwrap sandbox (no network, read-only filesystem). These are the stops for both titles in the catalog table in #1578.

XPSemu (PPSA97358, Alpha-2), on main:

./app.elf: symbol lookup error: ./app.elf: undefined symbol: iiQjzvfWDq0

With this PR it stops at the next absent import, __inet_aton (#1848):

./app.elf: symbol lookup error: ./app.elf: undefined symbol: a7ToDPsIQrc

Prospero Win (PPSA99995, 0.1.1), on main:

./app.elf: symbol lookup error: ./app.elf: undefined symbol: wdUufa9g-D8

With this PR it stops at sigemptyset (#1641):

./app.elf: symbol lookup error: ./app.elf: undefined symbol: +F7C-hdk7+E

In their sources both titles use dup(1)/dup2(logfd, 1) to redirect stdout and stderr to a log (util/log.c in XPSemu, native/ps5log/ps5log.h in Prospero Win). Prospero Win also has dup2(socketpair end, 1) (native/wine64_main.c, capture_fd), which this PR throws on; it runs after the title loads Wine, past several other absent imports.

Tested

Ubuntu, Linux 7.0, GCC 15.2.0, full build with BUILD_TESTING=ON. New test guest_dup: a dup of a pipe end outlives the original, dup2 onto an open pipe end closes it (its reader sees EOF), dup2(fd, fd), EBADF for negative and closed descriptors, socket dup and dup2 (including onto an unused socket number, with later socketpair numbers above it, and onto an open socket), and the two cross-numbering throws. Patched libkernel.prx exports iiQjzvfWDq0 and wdUufa9g-D8. Both titles run before and after as above.
The project's Build workflow on the fork, on this commit (a fork-only branch adds the previous build.yml, since the current one only runs for pull requests and main): https://github.com/Recapi/AnyPS5/actions/runs/37983997708: linux 521/521 and windows (MinGW) 512/512 with guest_dup passing, relinker on ubuntu, windows and macos passing.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Thank you for your interest in the project! We are sorry, but we cannot accept more PRs while we work through the existing backlog. This PR has been automatically closed and labeled "intake paused". This is not a judgment of the quality of your changes.

@github-actions github-actions Bot closed this Oct 9, 2026
@popeee99

popeee99 commented Oct 9, 2026

Copy link
Copy Markdown

Thanks @Recapi for the socket duplication implementation. I independently tested its allocation-failure paths.

Independent fault-injection finding at 1864f37: the new socket dup/dup2 paths let std::bad_alloc escape from sockets.emplace / insert_or_assign. Adjacent socket exports already translate this failure to -1 / guest ENOMEM (12).

There is also a state-recovery consequence: dup2(source, INT_MAX-2) leaves one synthetic descriptor available. A failed allocation in dup(source) advances nextDescriptor before insertion; retry incorrectly returns EMFILE (24). This needs only a few sockets, not real memory exhaustion.

A proposed minimal correction catches bad_alloc in both methods and advances the counter only after successful insertion. A harness linked against the actual production translation units confirms:

  • Unchanged head: all 15 allocation-failure runs fail; 15 allocation-free control runs pass.
  • Corrected source: 30/30 fresh-process runs, plus 30/30 ASan+UBSan runs with no diagnostics.
  • Actual libc/libkernel builds and existing guest_dup, guest_socket_pair, guest_file_vector tests pass 3/3 on both versions.

Reproduction, proposed patch and complete logs: patch, reproduction instructions and test results.

Tested on Linux/WSL with GCC 15.2. The harness catches the escaping exception; no game-process crash or native PS5 OOM contract is claimed. Original socket usability, successful-alias lifetime and bounded native-FD cleanup were checked; Windows/macOS, concurrency, full-emulator behavior and CI were not validated.

I see intake has paused and this PR was automatically closed. Sharing a focused finding for future use, without opening a PR or requesting an intake exception. AI-assisted with OpenAI Codex.

@Recapi

Recapi commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @popeee99, confirmed: the other socket exports in Udp.cpp (socket, socketpair, accept) already return ENOMEM on std::bad_alloc, and Duplicate advanced nextDescriptor before the insert. I applied your patch unchanged to the branch: Recapi:feat/libkernel-dup is now 430fcdc2. guest_dup, guest_socket_pair and guest_file_vector pass locally (Linux, 3 runs), and the Build workflow on the fork passes on it: https://github.com/Recapi/AnyPS5/actions/runs/37993776038 (linux 521/521, windows 512/512).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

intake paused Closed while PR intake is limited to authors with at least 10 PRs merged into main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants