Commit graph

17 commits

Author SHA1 Message Date
kalenikaliaksandr
bdd9c98d44 LibIPC: Move Windows handle serialization into TransportSocketWindows
Move handle serialization and deserialization entirely into
TransportSocketWindows so that Windows can share the common Message and
File implementations with other platforms.
2026-04-08 20:19:05 +02:00
Aliaksandr Kalenik
4ea4d63008 Everywhere: Replace Unix socket IPC transport with Mach ports on macOS
On macOS, use Mach port messaging instead of Unix domain sockets for
all IPC transport. This makes the transport capable of carrying Mach
port rights as message attachments, which is a prerequisite for sending
IOSurface handles over the main IPC channel (currently sent via a
separate out-of-band path). It also avoids the need for the FD
acknowledgement protocol that TransportSocket requires, since Mach port
right transfers are atomic in the kernel.

Three connection establishment patterns:

- Spawned helper processes (WebContent, RequestServer, etc.) use the
  existing MachPortServer: the child sends its task port with a reply
  port, and the parent responds with a pre-created port pair.

- Socket-bootstrapped connections (WebDriver, BrowserProcess) exchange
  Mach port names over the socket, then drop the socket.

- Pre-created pairs for IPC tests and in-message transport transfer.

Attachment on macOS now wraps a MachPort instead of a file descriptor,
converting between the two via fileport_makeport()/fileport_makefd().

The LibIPC socket transport tests are disabled on macOS since they are
socket-specific.
2026-03-23 18:50:48 +01:00
Aliaksandr Kalenik
19627bba54 LibIPC: Return TransportHandle directly from create_paired()
Previously, `create_paired()` returned two full Transport objects, and
callers would immediately call `from_transport()` on the remote side to
extract its underlying fd. This wasted resources: the remote
Transport's IO thread, wakeup pipes, and send queue were initialized
only to be torn down without ever sending or receiving a message.

Now `create_paired()` returns `{Transport, TransportHandle}` — the
remote side is born as a lightweight handle containing just the raw fd,
skipping all unnecessary initialization.

Also replace `release_underlying_transport_for_transfer()` (which
returned a raw int fd) with `release_for_transfer()` (which returns a
TransportHandle directly), hiding the socket implementation detail
from callers including MessagePort.
2026-03-14 18:25:18 +01:00
Aliaksandr Kalenik
db9652643a LibIPC+LibWeb+LibWebView: Remove clone_from_transport() API
Replace clone_from_transport() (which dup()s the FD) with
from_transport() (which releases the FD) in the WebWorkerClient
call site. The UI process never uses the WebWorkerClient connection
after spawning — it only passes the transport to WebContent — so
releasing instead of cloning is safe and simpler.

This removes clone_from_transport() from TransportHandle, and
clone_for_transfer() from TransportSocket/TransportSocketWindows,
as they no longer have any callers.
2026-03-13 15:34:15 +01:00
Aliaksandr Kalenik
2e881978af LibIPC+LibWeb+LibWebView+Services: Add Transport::create_paired()
Consolidate the repeated socketpair + adopt + configure pattern from
4 call sites into a single Transport::create_paired() factory method.
This fixes inconsistent error handling and socket configuration across
call sites, and prepares for future mach port support on macOS.
2026-03-11 14:42:24 +01:00
R-Goc
3a86e779bd LibCore/LibIPC/Meta: Stop using deprecated Winsock functions
This commit stops using deprecated WSA functions. While the ANSI
versions are most likely not going anywhere, Windows is natively UTF-16
so it has to convert to ANSI internally. All the ANSI functions in
Winsock are marked as deprecated. The macro suppressing the warnings is
no longer defined.
2026-02-02 10:35:11 +01:00
Andreas Kling
a0c389846e Revert "LibIPC: Move message decoding from main thread to I/O thread"
This reverts commit 757795ada4.

Appears to have regressed WPT.
2026-01-25 12:19:53 +01:00
Andreas Kling
757795ada4 LibIPC: Move message decoding from main thread to I/O thread
Previously, IPC messages were decoded on the main thread:

1. I/O thread received raw bytes and file descriptors
2. I/O thread stored them in a queue and notified main thread
3. Main thread decoded bytes into Message objects
4. Main thread processed the messages

Now, decoding happens on the I/O thread:

1. I/O thread receives raw bytes and file descriptors
2. I/O thread decodes them using a configurable MessageDecoder
3. I/O thread calls MessageHandler which stores decoded messages
4. I/O thread signals condition variable (for sync waiters)
5. I/O thread wakes main event loop via deferred_invoke()
6. Main thread processes already-decoded messages

This is achieved by:

- Adding MessageDecoder and MessageHandler callbacks to TransportSocket
- Connection template sets up the decoder (tries both endpoints)
- ConnectionBase::initialize_messaging() sets up the handler
- Storing a WeakEventLoopReference to wake the main thread
- Using mutex + condition variable for thread-safe queue access
- Sync message waiting now uses the CV directly instead of polling

The raw message API (read_as_many_messages_as_possible_without_blocking)
is preserved for MessagePort which uses its own decoding logic.

This architecture prepares for future multi-thread dispatch where
different message types could be routed to different handler threads
(e.g., scrolling messages to a dedicated scroll thread).
2026-01-25 09:32:51 +01:00
Andreas Kling
cb3bc3c493 LibIPC: Move IPC message limits to a shared Limits.h header
This consolidates the message size and FD count limits into a single
header file that can be used by both the encoding and decoding sides
of the IPC layer.
2026-01-22 17:38:15 +01:00
Andreas Kling
bef09fc277 LibIPC: Add limits on accumulated unprocessed data in TransportSocket
A malicious or misbehaving peer could send data faster than we process
it, causing unbounded memory growth in the unprocessed bytes buffer
and file descriptor queue.

Add MAX_UNPROCESSED_BUFFER_SIZE (128 MiB) and MAX_UNPROCESSED_FDS (512)
limits. When exceeded, the peer is disconnected gracefully rather than
allowing memory exhaustion.
2026-01-22 17:38:15 +01:00
Andreas Kling
bdca85f985 LibIPC: Add maximum limits for IPC message size and FD count
Reject messages from peers that exceed reasonable limits:
- Maximum payload size: 64 MiB
- Maximum file descriptor count: 128

Also use checked arithmetic for message size calculations to prevent
integer overflow attacks.

These limits prevent malicious peers from causing excessive memory
allocation or resource exhaustion.
2026-01-22 17:38:15 +01:00
Andreas Kling
d15acc46d6 LibIPC: Use checked arithmetic for message size calculations
The message parsing loop performs arithmetic on payload_size (u32) and
index (size_t). While overflow is unlikely on 64-bit systems, use
Checked<size_t> to explicitly validate:

1. message_size = payload_size + sizeof(MessageHeader)
2. new_index = index + payload_size + sizeof(MessageHeader)

This prevents potential integer overflow attacks from malicious peers.
2026-01-22 17:38:15 +01:00
Andreas Kling
9e34434f3b LibIPC: Handle allocation failures gracefully in TransportSocket
Replace crash-on-OOM patterns with graceful error handling:

- Use try_append() instead of append() for buffer operations
- Handle ByteBuffer::copy() failure instead of using MUST()

A malicious peer could send messages with large payload sizes to
trigger OOM conditions. Instead of crashing, we now disconnect
the misbehaving peer.
2026-01-22 17:38:15 +01:00
Andreas Kling
01c07a9f78 LibIPC: Replace crash-on-error with graceful peer disconnect
A misbehaving or compromised IPC peer (e.g. a WebContent process running
malicious JavaScript) could previously crash the UI process by sending
malformed messages. This was a problem: untrusted peers should only be
able to kill their own connection, not crash the process handling them.

Replace VERIFY_NOT_REACHED() and VERIFY() calls in error paths with
graceful disconnection using the existing shutdown infrastructure
(m_peer_eof, IOThreadState::Stopped, ShouldShutdown::Yes, etc.)

Affected error paths:
- Malformed IPC messages that fail to parse
- poll() errors and POLLERR/POLLNVAL conditions
- Unexpected send/receive errors on the socket
- Invalid message header types
- Protocol violations (e.g. bad FileDescriptorAcknowledgement)

All error paths now log via dbgln() for debugging before disconnecting.
2026-01-22 17:38:15 +01:00
ayeteadoe
dbba6c0df9 LibWeb: Enable in Windows CI 2025-06-30 10:50:36 -06:00
stasoid
8af2a49b5c LibIPC: Make TransportSocketWindows responsible for reading entire
messages. Port of a371f84 to Windows.
2025-06-17 15:36:47 -06:00
stasoid
652af318db LibIPC: Port to Windows
The Linux IPC uses SCM_RIGHTS to transfer fds to another process
(see TransportSocket::transfer, which calls LocalSocket::send_message).
File descriptors are handled separately from regular data.

On Windows handles are embedded in regular data. They are duplicated
in the sender process.

Socket handles need special code both on sender side (because they
require using WSADuplicateSocket instead of DuplicateHandle, see
TransportSocketWindows::duplicate_handles) and on receiver side
(because they require WSASocket, see FileWindows.cpp).

TransportSocketWindows::ReadResult::fds vector is always empty, it is
kept the same as Linux version to avoid OS #ifdefs in Connection.h/.cpp
and Web::HTML::MessagePort::read_from_transport. Separate handling of
fds permeates all IPC code, it doesn't make sense to #ifdef out all this
code on Windows. In other words, the Linux code is more generic -
it handles both regular data and fds. On Windows, we need only the
regular data portion of it, and we just use that.

Duplicating handles on Windows requires pid of target (receiver)
process (see TransportSocketWindows::m_peer_pid). This pid is received
during special TransportSocketWindows initialization, which is performed
only on Windows. It is handled in a separate PR #3179.
Note: ChatGPT and [stackoverflow](https://stackoverflow.com/questions/25429887/getting-pid-of-peer-socket-on-windows) suggest using GetExtendedTcpTable/GetTcpTable2
to get peer pid, but this doesn't work because [MIB_TCPROW2::dwOwningPid](https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ns-tcpmib-mib_tcprow2)
is "The PID of the process that issued a context bind for this TCP
connection.", so for both ends it will return the pid of the process
that called socketpair.

Co-Authored-By: Andrew Kaster <andrew@ladybird.org>
2025-02-12 22:31:43 -07:00