From a6c123ca4acc2f0bfec2415e129d3aa840e7c97e Mon Sep 17 00:00:00 2001 From: mattogepeto Date: Thu, 15 Oct 2026 23:20:58 +0200 Subject: [PATCH 1/2] fix(libkernel): return EPROTOTYPE for a socket type inet does not have socket() returned EPROTONOSUPPORT for every socket type it does not create. FreeBSD 11.4's socreate returns EPROTOTYPE when no protocol is found, the protocol is 0 and the type is not 0, and EPROTONOSUPPORT when a protocol was given. Its inet domain (in_proto.c) has SOCK_RAW entries in every kernel and a SOCK_SEQPACKET entry only with SCTP, and no SOCK_RDM or other type. Return EPROTOTYPE for a type the inet domain never has, such as SOCK_RDM, with protocol 0. SOCK_RAW and SOCK_SEQPACKET keep EPROTONOSUPPORT, since whether the console creates them is not known. Test the unknown types and mismatched protocols for AF_INET and AF_INET6, and list the socket types and families that are not created in the library's technical debt file. Written with AI assistance (Claude). Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libkernel/Socket/src/Udp.cpp | 4 +++- core/libs/tests/GuestSocketDescriptors.cpp | 17 +++++++++++++++++ docs/dev/debt/libkernel.md | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libkernel/Socket/src/Udp.cpp b/core/libs/prx/libkernel/Socket/src/Udp.cpp index 5c6fc0b521..4512bf1fc0 100644 --- a/core/libs/prx/libkernel/Socket/src/Udp.cpp +++ b/core/libs/prx/libkernel/Socket/src/Udp.cpp @@ -924,7 +924,9 @@ int APS5_VABI socket_nid_postfix(int family, int type, int protocol) { if (family != 2 && family != 28) return Fail(47); const int nativeType = plainType == 1 ? SOCK_STREAM : plainType == 2 ? SOCK_DGRAM : -1; const int expectedProtocol = plainType == 1 ? 6 : 17; - if (nativeType == -1 || (protocol != 0 && protocol != expectedProtocol)) return Fail(43); + const bool inetKnowsType = plainType == 3 || plainType == 5; + if (nativeType == -1) return Fail(protocol == 0 && plainType != 0 && !inetKnowsType ? 41 : 43); + if (protocol != 0 && protocol != expectedProtocol) return Fail(43); #ifdef _WIN32 static const int startup = [] { WSADATA data{}; return WSAStartup(MAKEWORD(2, 2), &data); }(); if (startup) return Fail(5); diff --git a/core/libs/tests/GuestSocketDescriptors.cpp b/core/libs/tests/GuestSocketDescriptors.cpp index e8340085ae..76618bf409 100644 --- a/core/libs/tests/GuestSocketDescriptors.cpp +++ b/core/libs/tests/GuestSocketDescriptors.cpp @@ -181,7 +181,24 @@ static void DuplicateKeepsSocket() { Require(close_nid_postfix(sender) == 0); } +static void SocketTypeErrors() { + constexpr int PrototypeError = 41; + constexpr int ProtocolError = 43; + constexpr int FamilyError = 47; + constexpr int Inet6 = 28; + for (const int family : {Inet, Inet6}) { + Require(socket_nid_postfix(family, 99, 0) == -1 && *__error_nid_postfix() == PrototypeError); + Require(socket_nid_postfix(family, 4, 0) == -1 && *__error_nid_postfix() == PrototypeError); + Require(socket_nid_postfix(family, 0, 0) == -1 && *__error_nid_postfix() == ProtocolError); + Require(socket_nid_postfix(family, 99, 6) == -1 && *__error_nid_postfix() == ProtocolError); + Require(socket_nid_postfix(family, 1, 17) == -1 && *__error_nid_postfix() == ProtocolError); + Require(socket_nid_postfix(family, 2, 6) == -1 && *__error_nid_postfix() == ProtocolError); + } + Require(socket_nid_postfix(99, 1, 0) == -1 && *__error_nid_postfix() == FamilyError); +} + int main() { + SocketTypeErrors(); SelectSocketAndFile(); CloseReleasesNumber(); DuplicateKeepsSocket(); diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 5791f7ab5e..7acb437253 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -175,6 +175,7 @@ - [Socket error numbers](../../../core/libs/prx/libkernel/Socket/include/SocketError.hpp) (libkernel) - a host socket error becomes the FreeBSD 11 `errno.h` number of the same name; the numbers are not checked on the console. Winsock `WSAESHUTDOWN` becomes `EPIPE`, as a send after `shutdown` does on FreeBSD, and a host error with no FreeBSD socket counterpart becomes `EIO`. A nonblocking `connect` in progress reports `EINPROGRESS` on Linux but `EAGAIN` on Windows (`WSAEWOULDBLOCK`), where FreeBSD reports `EINPROGRESS`. On Windows a refused nonblocking `connect` is never reported (`SO_ERROR` is unsupported), so a repeated one keeps reporting `EALREADY` where FreeBSD reports `ECONNREFUSED`. A receive after `shutdown(SHUT_RD)` reports `EPIPE` on Windows where FreeBSD returns 0 - [getsockopt, setsockopt](../../../core/libs/prx/libkernel/Socket/src/SocketOptions.cpp) (libkernel) on a guest socket handle the SOL_SOCKET level and `TCP_NODELAY`, after FreeBSD 11.4's [`sosetopt` and `sogetopt`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c): `SO_REUSEADDR`, `SO_KEEPALIVE`, `SO_DONTROUTE`, `SO_BROADCAST`, `SO_OOBINLINE` and `SO_LINGER` act on the host socket, and `SO_ERROR`, `SO_TYPE`, `SO_ACCEPTCONN`, `SO_SNDBUF` and `SO_RCVBUF` are read. A boolean option reads back as its own bit, as FreeBSD returns `so_options & option`. Winsock does not clear a socket's pending error when it is read, so on Windows the library reports a pending error once and shows it again only after a new `connect`; a second failure with the same code on the same socket without a `connect` in between would be hidden. `SO_SNDBUF` and `SO_RCVBUF` read the host's value, which is not the size FreeBSD reports (Linux doubles it). `SO_NOSIGPIPE` is kept per socket and reads back as its bit. On Linux a `send`, `sendto`, `sendmsg` or `write` on that socket passes `MSG_NOSIGNAL` to the host, as [`kern_sendit`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_syscalls.c#L890-L895) and [`soo_write`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_socket.c#L147-L151) leave out the signal. An accepted socket takes it from its listener, as [`sonewconn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L603) copies the options. `SO_NBIO` (`0x1200`) is the console's own option, not FreeBSD's. It switches the socket between blocking and non-blocking as `FIONBIO` does and reads back as 0 or 1, as `sceNetSetsockopt` and `sceNetGetsockopt` treat it here; what the console reads back for it is not measured. `TCP_NODELAY` (`IPPROTO_TCP`, 1) is set on the host socket and reads back as `TF_NODELAY` (4), as [`tcp_default_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet/tcp_usrreq.c#L1860-L1861) returns it. The `IPPROTO_TCP` level on a datagram or `AF_UNIX` socket fails with `EINVAL`, as [`ip_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet/ip_output.c#L943-L944) and [`uipc_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_usrreq.c#L1228-L1229) answer it, and an accepted socket takes `TCP_NODELAY` through the host. `SO_SNDTIMEO` and `SO_RCVTIMEO` follow [`sosetopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L2657-L2689) and [`sogetopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L2846-L2850). A 16-byte `timeval` with a negative field or 1000000 microseconds or more fails with `EDOM`, and more than `INT32_MAX` seconds becomes the longest wait. The value is kept and read back through FreeBSD's 32.32 fixed-point conversion ([`tvtosbt`, `sbttotv`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/sys/time.h#L178-L286)), so half a second reads back as 499999 microseconds and one microsecond as 0. A `recv`, `recvfrom`, `recvmsg`, `read` or `readv`, and a `send`, `sendto`, `sendmsg`, `write` or `writev`, on a blocking socket then waits that long for the host to report the socket readable or writable, and fails with `EWOULDBLOCK`; a call with `MSG_DONTWAIT` does not wait. An accepted socket takes both from its listener ([`sonewconn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L629-L630)). The wait covers only the first transfer: once the socket is writable the host send runs without a limit, where FreeBSD stops a send that cannot finish and returns the bytes sent so far, and a receive whose data another thread takes between the wait and the host call blocks again. `sendfile` ignores the send timeout. The host wait has millisecond steps, so a shorter timeout lasts up to a millisecond longer. A null value fails with `EINVAL`, where FreeBSD's [`kern_setsockopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_syscalls.c#L1414-L1415) returns `EFAULT`. Every other SOL_SOCKET option (`SO_DEBUG`, `SO_USELOOPBACK`, `SO_REUSEPORT` and `SO_TIMESTAMP`, which FreeBSD stores and no title has been seen to set, the low-water marks among them), every other `IPPROTO_TCP` option (`TCP_MAXSEG`, `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT` among them) and every other level (`IPPROTO_IP`, `IPPROTO_IPV6`) fail with `ENOPROTOOPT`. None of this was measured on a console. - [connect to AF_UNSPEC](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) dissolves a datagram association as FreeBSD 11.4's `udp_disconnect()` does, which resets the local address to the wildcard address and keeps the port. On Linux a socket bound to an explicit port keeps its local address as well (`127.0.0.1:` stays `127.0.0.1:`), because a bound socket cannot be bound again; one bound to port 0 is bound again to the wildcard address with the port it had +- [socket](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) creates only `SOCK_STREAM` and `SOCK_DGRAM` sockets in `AF_INET` and `AF_INET6`. A type that FreeBSD 11.4's inet domain has no protocol for in any configuration ([`in_proto.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/netinet/in_proto.c) has no `SOCK_RDM` entry and no unknown type) fails with `EPROTOTYPE` when the protocol is 0 and `EPROTONOSUPPORT` otherwise, as [`socreate`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c) does. `SOCK_RAW`, which `in_proto.c` always has (`socreate` creates it for a privileged process), and `SOCK_SEQPACKET`, which it has only in a kernel built with `SCTP`, fail with `EPROTONOSUPPORT`; whether the console allows either is not known. `SOCK_DGRAM` with `IPPROTO_UDPLITE` and `AF_UNIX` (`socketpair` supports it) fail with `EPROTONOSUPPORT` and `EAFNOSUPPORT` where FreeBSD creates the socket - [msync](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) follows FreeBSD 11's `kern_msync` and `vm_map_sync`, not measured on the console: the address is rounded down to the page, a range that wraps is `EINVAL`, as is `MS_ASYNC` with `MS_INVALIDATE`, and a range whose start or any page is not mapped (as `sceKernelVirtualQuery` reports it, reservations included) is `ENOMEM`; a page unmapped from inside a larger mapping still counts as mapped there, since `sceKernelVirtualQuery` keeps reporting the whole mapping. The guest has no file mappings, so a successful call writes nothing back. `MS_INVALIDATE` on a mapped range throws: FreeBSD returns `EBUSY` when the range holds `mlock`ed pages, and locked pages are not tracked - [madvise](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) accepts `MADV_PROTECT` (10) as a no-op hint without modifying process protection. FreeBSD 11's `kern_madvise` (`sys/vm/vm_mmap.c`) handles it before the address check, ignores the address and length, and calls `kern_procctl(..., PROC_SPROTECT, PPROT_SET)`, which requires `PRIV_VM_MADV_PROTECT` and fails with `EPERM` without it. Returning 0 unconditionally assumes the calling process is privileged. Console behaviour is unverified - [mincore](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) follows FreeBSD 11's `sys_mincore` (`sys/vm/vm_mmap.c`), not measured on the console: the address is rounded down to the page, length 0 returns 0, a null vector over a mapped range returns `EFAULT`, an overflowing range or any unmapped page in the range (as reported by `sceKernelVirtualQuery`) returns `ENOMEM`. For valid ranges, a page reports `MINCORE_INCORE` (0x1) when `sceKernelVirtualQuery` reports it committed and 0 when it is only reserved; actual residency, referenced and modified bits are not modelled. Length 0 returns 0 when the start page is mapped and `ENOMEM` otherwise, and the range and mapping checks come before the null vector `EFAULT`, as in `kern_mincore` Java 25 LAN / DNS (PPSA99869) imports it From 8f0caa17963d5f25e7c48ea471958946bedbcb6c Mon Sep 17 00:00:00 2001 From: mattogepeto Date: Sat, 17 Oct 2026 16:41:52 +0200 Subject: [PATCH 2/2] docs(libkernel): move the socket type debt entry away from other changes The entry sat right after "connect to AF_UNSPEC", where the open #6884 adds its own entry, so whichever merges second would conflict. Move it after the mlockall entry, which no open pull request changes. The text is unchanged. Written with AI assistance (Claude). Co-Authored-By: Claude Opus 5.5 --- docs/dev/debt/libkernel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dev/debt/libkernel.md b/docs/dev/debt/libkernel.md index 7acb437253..532741d184 100644 --- a/docs/dev/debt/libkernel.md +++ b/docs/dev/debt/libkernel.md @@ -175,11 +175,11 @@ - [Socket error numbers](../../../core/libs/prx/libkernel/Socket/include/SocketError.hpp) (libkernel) - a host socket error becomes the FreeBSD 11 `errno.h` number of the same name; the numbers are not checked on the console. Winsock `WSAESHUTDOWN` becomes `EPIPE`, as a send after `shutdown` does on FreeBSD, and a host error with no FreeBSD socket counterpart becomes `EIO`. A nonblocking `connect` in progress reports `EINPROGRESS` on Linux but `EAGAIN` on Windows (`WSAEWOULDBLOCK`), where FreeBSD reports `EINPROGRESS`. On Windows a refused nonblocking `connect` is never reported (`SO_ERROR` is unsupported), so a repeated one keeps reporting `EALREADY` where FreeBSD reports `ECONNREFUSED`. A receive after `shutdown(SHUT_RD)` reports `EPIPE` on Windows where FreeBSD returns 0 - [getsockopt, setsockopt](../../../core/libs/prx/libkernel/Socket/src/SocketOptions.cpp) (libkernel) on a guest socket handle the SOL_SOCKET level and `TCP_NODELAY`, after FreeBSD 11.4's [`sosetopt` and `sogetopt`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c): `SO_REUSEADDR`, `SO_KEEPALIVE`, `SO_DONTROUTE`, `SO_BROADCAST`, `SO_OOBINLINE` and `SO_LINGER` act on the host socket, and `SO_ERROR`, `SO_TYPE`, `SO_ACCEPTCONN`, `SO_SNDBUF` and `SO_RCVBUF` are read. A boolean option reads back as its own bit, as FreeBSD returns `so_options & option`. Winsock does not clear a socket's pending error when it is read, so on Windows the library reports a pending error once and shows it again only after a new `connect`; a second failure with the same code on the same socket without a `connect` in between would be hidden. `SO_SNDBUF` and `SO_RCVBUF` read the host's value, which is not the size FreeBSD reports (Linux doubles it). `SO_NOSIGPIPE` is kept per socket and reads back as its bit. On Linux a `send`, `sendto`, `sendmsg` or `write` on that socket passes `MSG_NOSIGNAL` to the host, as [`kern_sendit`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_syscalls.c#L890-L895) and [`soo_write`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/sys_socket.c#L147-L151) leave out the signal. An accepted socket takes it from its listener, as [`sonewconn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L603) copies the options. `SO_NBIO` (`0x1200`) is the console's own option, not FreeBSD's. It switches the socket between blocking and non-blocking as `FIONBIO` does and reads back as 0 or 1, as `sceNetSetsockopt` and `sceNetGetsockopt` treat it here; what the console reads back for it is not measured. `TCP_NODELAY` (`IPPROTO_TCP`, 1) is set on the host socket and reads back as `TF_NODELAY` (4), as [`tcp_default_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet/tcp_usrreq.c#L1860-L1861) returns it. The `IPPROTO_TCP` level on a datagram or `AF_UNIX` socket fails with `EINVAL`, as [`ip_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/netinet/ip_output.c#L943-L944) and [`uipc_ctloutput`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_usrreq.c#L1228-L1229) answer it, and an accepted socket takes `TCP_NODELAY` through the host. `SO_SNDTIMEO` and `SO_RCVTIMEO` follow [`sosetopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L2657-L2689) and [`sogetopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L2846-L2850). A 16-byte `timeval` with a negative field or 1000000 microseconds or more fails with `EDOM`, and more than `INT32_MAX` seconds becomes the longest wait. The value is kept and read back through FreeBSD's 32.32 fixed-point conversion ([`tvtosbt`, `sbttotv`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/sys/time.h#L178-L286)), so half a second reads back as 499999 microseconds and one microsecond as 0. A `recv`, `recvfrom`, `recvmsg`, `read` or `readv`, and a `send`, `sendto`, `sendmsg`, `write` or `writev`, on a blocking socket then waits that long for the host to report the socket readable or writable, and fails with `EWOULDBLOCK`; a call with `MSG_DONTWAIT` does not wait. An accepted socket takes both from its listener ([`sonewconn`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_socket.c#L629-L630)). The wait covers only the first transfer: once the socket is writable the host send runs without a limit, where FreeBSD stops a send that cannot finish and returns the bytes sent so far, and a receive whose data another thread takes between the wait and the host call blocks again. `sendfile` ignores the send timeout. The host wait has millisecond steps, so a shorter timeout lasts up to a millisecond longer. A null value fails with `EINVAL`, where FreeBSD's [`kern_setsockopt`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/sys/kern/uipc_syscalls.c#L1414-L1415) returns `EFAULT`. Every other SOL_SOCKET option (`SO_DEBUG`, `SO_USELOOPBACK`, `SO_REUSEPORT` and `SO_TIMESTAMP`, which FreeBSD stores and no title has been seen to set, the low-water marks among them), every other `IPPROTO_TCP` option (`TCP_MAXSEG`, `TCP_KEEPIDLE`, `TCP_KEEPINTVL` and `TCP_KEEPCNT` among them) and every other level (`IPPROTO_IP`, `IPPROTO_IPV6`) fail with `ENOPROTOOPT`. None of this was measured on a console. - [connect to AF_UNSPEC](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) dissolves a datagram association as FreeBSD 11.4's `udp_disconnect()` does, which resets the local address to the wildcard address and keeps the port. On Linux a socket bound to an explicit port keeps its local address as well (`127.0.0.1:` stays `127.0.0.1:`), because a bound socket cannot be bound again; one bound to port 0 is bound again to the wildcard address with the port it had -- [socket](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) creates only `SOCK_STREAM` and `SOCK_DGRAM` sockets in `AF_INET` and `AF_INET6`. A type that FreeBSD 11.4's inet domain has no protocol for in any configuration ([`in_proto.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/netinet/in_proto.c) has no `SOCK_RDM` entry and no unknown type) fails with `EPROTOTYPE` when the protocol is 0 and `EPROTONOSUPPORT` otherwise, as [`socreate`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c) does. `SOCK_RAW`, which `in_proto.c` always has (`socreate` creates it for a privileged process), and `SOCK_SEQPACKET`, which it has only in a kernel built with `SCTP`, fail with `EPROTONOSUPPORT`; whether the console allows either is not known. `SOCK_DGRAM` with `IPPROTO_UDPLITE` and `AF_UNIX` (`socketpair` supports it) fail with `EPROTONOSUPPORT` and `EAFNOSUPPORT` where FreeBSD creates the socket - [msync](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) follows FreeBSD 11's `kern_msync` and `vm_map_sync`, not measured on the console: the address is rounded down to the page, a range that wraps is `EINVAL`, as is `MS_ASYNC` with `MS_INVALIDATE`, and a range whose start or any page is not mapped (as `sceKernelVirtualQuery` reports it, reservations included) is `ENOMEM`; a page unmapped from inside a larger mapping still counts as mapped there, since `sceKernelVirtualQuery` keeps reporting the whole mapping. The guest has no file mappings, so a successful call writes nothing back. `MS_INVALIDATE` on a mapped range throws: FreeBSD returns `EBUSY` when the range holds `mlock`ed pages, and locked pages are not tracked - [madvise](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) accepts `MADV_PROTECT` (10) as a no-op hint without modifying process protection. FreeBSD 11's `kern_madvise` (`sys/vm/vm_mmap.c`) handles it before the address check, ignores the address and length, and calls `kern_procctl(..., PROC_SPROTECT, PPROT_SET)`, which requires `PRIV_VM_MADV_PROTECT` and fails with `EPERM` without it. Returning 0 unconditionally assumes the calling process is privileged. Console behaviour is unverified - [mincore](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) follows FreeBSD 11's `sys_mincore` (`sys/vm/vm_mmap.c`), not measured on the console: the address is rounded down to the page, length 0 returns 0, a null vector over a mapped range returns `EFAULT`, an overflowing range or any unmapped page in the range (as reported by `sceKernelVirtualQuery`) returns `ENOMEM`. For valid ranges, a page reports `MINCORE_INCORE` (0x1) when `sceKernelVirtualQuery` reports it committed and 0 when it is only reserved; actual residency, referenced and modified bits are not modelled. Length 0 returns 0 when the start page is mapped and `ENOMEM` otherwise, and the range and mapping checks come before the null vector `EFAULT`, as in `kern_mincore` Java 25 LAN / DNS (PPSA99869) imports it - [mlockall, munlockall](../../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) - `mlockall` returns `EINVAL` for zero or unknown flags. Valid `MCL_CURRENT` throws because it would lock host stacks, heaps, modules and driver mappings along with guest memory; `MCL_FUTURE` throws because later mappings are not tracked for locking. FreeBSD 11.4's `sys_munlockall` (`sys/vm/vm_mmap.c`:1133-1165) checks `priv_check(td, PRIV_VM_MUNLOCK)` first (returning `EPERM` without it) and unwires the user map (`vm_map_unwire` over `vm_map_min`...`vm_map_max` with `VM_MAP_WIRE_HOLESOK`). Returning 0 unconditionally assumes the calling process is privileged. `munlockall` unlocks memory locked by `mlock` (`VirtualUnlock` on Windows, tolerating `ERROR_NOT_LOCKED` for pages never locked, and `munlockall` on Linux). On main, XPSemu (PPSA97358) fails to load at these imports (mlockall #4386, munlockall); their calls in QEMU `os-posix.c` execute only when `-overcommit mem-lock=on` is enabled. The call has not been observed in a run with this implementation. +- [socket](../../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) creates only `SOCK_STREAM` and `SOCK_DGRAM` sockets in `AF_INET` and `AF_INET6`. A type that FreeBSD 11.4's inet domain has no protocol for in any configuration ([`in_proto.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/netinet/in_proto.c) has no `SOCK_RDM` entry and no unknown type) fails with `EPROTOTYPE` when the protocol is 0 and `EPROTONOSUPPORT` otherwise, as [`socreate`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/sys/kern/uipc_socket.c) does. `SOCK_RAW`, which `in_proto.c` always has (`socreate` creates it for a privileged process), and `SOCK_SEQPACKET`, which it has only in a kernel built with `SCTP`, fail with `EPROTONOSUPPORT`; whether the console allows either is not known. `SOCK_DGRAM` with `IPPROTO_UDPLITE` and `AF_UNIX` (`socketpair` supports it) fail with `EPROTONOSUPPORT` and `EAFNOSUPPORT` where FreeBSD creates the socket - [sigtimedwait](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) waits on a process-wide pending set instead of throwing: a signal raised while blocked stays pending and is taken by `sigwait`, `sigwaitinfo` or `sigtimedwait`, delivered to the handler when `_sigprocmask` unblocks it, or delivered by `pause`. The set covers all 128 signals and the lowest number that is pending and in the set is selected (FreeBSD specifies the lowest number only among real-time signals). Selection, `EAGAIN` on timeout, `EINVAL` for a `tv_nsec` outside 0 to 1000000000 while nothing is pending and `NULL` timeout blocking indefinitely follow FreeBSD's [sigtimedwait(2)](https://man.freebsd.org/cgi/man.cgi?query=sigtimedwait). `sigwait` returns 0 with the number in its output, as POSIX specifies, and `sigwaitinfo` and `sigtimedwait` return the number, as FreeBSD does. The guest `siginfo_t` follows FreeBSD 11's `sys/sys/siginfo.h`: 80 bytes, `si_signo` at 0, `si_code` at 8, `si_pid` at 12, `si_uid` at 16, `si_addr` at 24, `si_value` at 32 and `si_band` at 40; only `si_signo`, `si_code` (`SI_USER`) and `si_pid` (the guest pid) are written, `si_uid` stays 0 because no uid is modelled, and the console's layout is not verified. PPSA85300 imports `sigtimedwait` and fails to load without it; its only call site is VLC's `vlc_writev` ([filesystem.c](https://github.com/videolan/vlc/blob/6de05adcbaf2e8b85fe86aad4169393098628119/src/posix/filesystem.c#L243-L278)), which polls with a zero timeout to drain a `SIGPIPE` only after `writev` fails with `EPIPE`. The waits are not cancellation points and an unblocked caught signal does not interrupt them with `EINTR`, where FreeBSD's `sigwaitinfo` and `sigtimedwait` may return it. `SIGPIPE` has no host equivalent and is never raised by a host write here (see `read, write and vector I/O`), so VLC's drain finds nothing - [pause, sigsuspend, sigwait, sigwaitinfo](../../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) take the pending set of `sigtimedwait` instead of throwing. `pause` waits for a pending signal that is not blocked and whose disposition can be delivered, runs its handler on the calling thread and returns -1 with `EINTR`; an ignored signal is discarded and the wait continues, and it never returns for a signal whose handler cannot run. A signal whose registered handler has `SA_SIGINFO` still throws, and one whose disposition is the default action and that has no host equivalent (every signal except 2, 4, 6, 8, 11 and 15) is neither delivered nor discarded: it stays pending until a wait takes it, where FreeBSD runs the default action, which terminates the process for most of them. PPSA68091, PPSA99169, PPSA99420 and PPSA97358 import them and fail to load without them. PPSA68091 calls `pause` at one site (0x8F7266), in a loop that waits while a global holds another thread's `errno` address. PPSA99169 only stores the address of `pause` in a table of symbol addresses (0x30DB1). PPSA99420 calls `pause` at 0x3BD80F6 and 0x3C9E220, `sigwait` at 0x3C9E4C7 and `sigwaitinfo` at 0x3C9E588. PPSA97358 calls `sigwait` from QEMU's `sigwait_compat` thread ([compatfd.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/util/compatfd.c#L28-L53)), which `qemu_signal_init` ([main-loop.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/util/main-loop.c#L110-L147)) starts for `SIGIO`, `SIGALRM` and `SIGBUS` when the main loop is created, so it reaches the wait at start-up, and from the vCPU thread of the dummy accelerator ([dummy-cpus.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/accel/dummy-cpus.c#L48-L56)). `sigsuspend` swaps the process-wide mask of `_sigprocmask` for its argument, waits as `pause` does through the same helper, then restores the mask through `_sigprocmask`, and returns -1 with `EINTR`; a null set throws, as the console faults in libthr instead of returning `EFAULT`. All four words of the argument replace the mask and are restored afterwards, as in `_sigprocmask`. Java 25 LAN / DNS (PPSA99869, #5501) imports it and fails to load without it, after `sysctlnametomib`, `fegetenv` and `mincore`. None of the five waits is a cancellation point - [getsid](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) returns the calling process ID for pid 0 and the calling process, and `ESRCH` for other process IDs, assuming the single guest process is its own session leader. PPSA74126 imports it and fails to load without it