Skip to content

libkernel: guest socket descriptors start at 0x10000000 and overflow fd_set (Palworld, The Mound, Dying Light 2); fix branch ready #4366

Closed

Description

@screamm

Guest socket descriptors start at GuestSockets::FirstDescriptor = 0x10000000, so any title that puts a socket in an fd_set writes past it. Observed: Palworld (PPSA23126), The Mound (PPSA26223) and Dying Light 2 (PPSA02262) crash in the EOS SDK on FD_SET(0x10000005) (verbatim lines in their issues; see also the earlier #2170, closed for lack of a title and log, which now exist). FreeBSD 11.4 select.h:58-59 (FD_SETSIZE 1024) and kern_descrip.c:1643-1660 (descriptors are the lowest free small integers, shared between files and sockets).

Fix branch ready: screamm:fix/socket-descriptors-below-fd-setsize (20378375, one commit on main 436e310d). I cannot open it as a PR under the intake rule while #4081 is open; anyone eligible is welcome to carry it, keeping the commit as is. Unlike #2170 it leaves file numbering alone and only moves sockets into the shared table. Open PRs that still use FirstDescriptor (#4053, #4111, #3649, #4115) will need a rebase; #4053's sceKernelClose on sockets is included here.

  • socket, accept, socketpair and dup take the lowest free number from the table files use: on Windows that number holds a zero-access NUL handle (the trick directory descriptors already use), on Linux a duplicate of the host socket, so anything reaching the host directly still hits the real socket. The socket table maps number to socket, so "is this a socket" is a table lookup at all 31 former range checks; FirstDescriptor and the old counter are gone. close releases both the socket entry and the number; dup/dup2 share one socket object through the host table; dup2 of a file onto a socket number drops the socket entry; sceKernelClose closes sockets. sceNetSocket/sceNetAccept take their ids from the same table instead of counting from 32, so small libkernel numbers cannot collide with libSceNet ids.
  • select now polls libkernel sockets alongside libSceNet sockets, the random device, stdio and (Linux) host descriptors in 10 ms slices (it never handled libkernel sockets before); poll throws only for an open non-socket descriptor and reports POLLNVAL for a closed one. File calls that used to hit EBADF by accident on a socket now answer as FreeBSD does: lseek ESPIPE, ftruncate/getdirentries EINVAL, flock EOPNOTSUPP, fstat throws (not implemented); pread on a closed descriptor returns EBADF instead of throwing. dup2 of a socket onto 0 to 2 still throws, because libc writes those streams directly.
  • TechnicalDebt records that the numbers will not match the console's (the table also holds the runtime's own files; the first number socket()/sceNetSocket return on a console and its getdtablesize are unmeasured, which hugodearaujo could settle), and three paths that skip the socket table (libc fdopen on a socket, libkernel fstat on a socket, libkernel close on a libSceNet socket); ENOTSOCK versus the existing EBADF for socket calls on files is unchanged.
  • New guest_socket_descriptors (number below 1024, select on a socket and a file together, close freeing the number for a file, dup of a socket, sceKernelClose); guest_select gains a socket-and-pipe case (Linux); guest_poll, guest_dup, guest_sce_net, guest_socket_pair, guest_fstatfs, guest_posix_filesystem updated.

Tested on Windows 11 Pro 26220, WinLibs MinGW-w64 GCC 15.2.0 r7, Ninja, Release, BUILD_TESTING=ON, full build including libs: the 16 socket/file/poll/dup/net/http/resolver/aio/fcntl tests 3/3; ctest -j8 -E "^agc_driver_|^video_out_flip" 597/597; tools/check_conventions.py --base main and git diff --check pass. Linux: built and run in a gcc:15 container by the implementing agent, 24 related tests including guest_select 3/3. The three titles have not been run with it; their next stops still need a run.

Written with Claude Code assistance (implementation by a subagent, reviewed and validated in this session).

Activity

  1. MarcZX10 commented on Oct 12, 2026

    @MarcZX10
    Contributor

    Carried the socket descriptor fix as PR #4391: #4391. It reserves ordinary descriptor numbers for libkernel and libSceNet sockets and adds select/poll/dup regression coverage. The upstream implementation author is preserved. Socket fstat remains unsupported in this PR; a regression now verifies that fstat on a closed low-numbered socket descriptor returns EBADF. Linux Release focused CTests pass 6/6, and conventions/diff checks pass. No title run was performed here.

  2. oneandonlydean commented on Oct 13, 2026

    @oneandonlydean
    Collaborator

    Closing: fixed by #4391; guest sockets get ordinary descriptor numbers below FD_SETSIZE now. Reopen if a title still overflows its fd_set on current main.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions