Limitations
paludarium is a young project. These things are not possible yet, or not verified.
Not supported
- Dynamically linked programs: only statically linked programs run.
- 32-bit x86, AVX, AVX-512, x87, MMX: out of scope.
- Network: without an allow list (
--allow-net,Config::with_network, or the JavaScriptnetworkoption, which a browser needs a relay for)socket()returnsEAFNOSUPPORTfor every family. With one, only TCP clients, TCP and UDP servers for the guest's own sockets and name resolution work (Network):- A guest serves only its own sockets, on its loopback (Loopback servers): neither the host nor another machine can connect to it, and
bindtakes only the loopback's addresses and the unspecified one (EADDRNOTAVAILfor any other, where Linux takes each of the machine's own). - On the loopback, a closed connection leaves no
TIME_WAIT, so its port is free at once, where Linux keeps it 60 seconds after the side that closed first (EADDRINUSEwithoutSO_REUSEADDR). A connection that finds the listener's queue full waits untilacceptmakes room, where Linux's client tries again after 1, 3, 7… seconds and gives up withETIMEDOUT. Listeners sharing a port withSO_REUSEPORTdo not share its connections: all go to the first. A nonzeroSO_LINGERtime never holdsclose, as the bytes are delivered at once. - A UDP socket reaches the DNS stub and the guest's own sockets on the loopback; a datagram to any other address is
ENETUNREACH, and one connected elsewhere takes no port (Linux binds one). A datagram counts its bytes and 768 more againstSO_RCVBUF, where Linux counts itssk_buff, so a full buffer drops at another point; multicast and broadcast datagrams reach no one. Raw sockets areEPERM, ICMP socketsEACCES, and other kinds (SCTP'sSOCK_SEQPACKET, which Linux loads a module for)ESOCKTNOSUPPORT. socket(AF_UNIX)isEAFNOSUPPORTas before, andconnectandbindon a socket pair areENOSYS.- The DNS stub answers only
AandAAAA, and ignores EDNS, so a datagram answer holds at most 512 bytes; a name the host cannot resolve isSERVFAIL. A TCP connection to port 53 always reaches the stub, never a real DNS server. - Before a connection through the host is open,
getsocknamegives the address the socket is bound to, or the unspecified one with port 0, where Linux gives the port it took; a port the socket bound does not reach the host, whose connection leaves from a port of its own.SIOCOUTQcounts what the host has not yet taken to send, not what the peer has not acknowledged.SO_SNDBUFandSO_RCVBUFare kept forgetsockoptbut do not size the buffers, which hold 256 KiB each way; the other options butTCP_NODELAYdo not reach the host's socket. MSG_OOBfinds no urgent data (EINVAL), so noSIGURGcomes either.
- A guest serves only its own sockets, on its loopback (Loopback servers): neither the host nor another machine can connect to it, and
- File mappings: a file mapping shows the file's page cache as on Linux (a
MAP_PRIVATEone in the pages it has not written), but the stores of aMAP_SHAREDmapping reach the file on the host only when the guest reads the file,stats it or callsmsyncorfsync, or the file's last mapping goes away (Linux writes them back in its own time), and a change another host program makes to the file does not reach the mappings. On a Windows host, the stores of a shared mapping made through a descriptor open withO_APPENDdo not reach the file. The end of a mapped file, past which a page raisesSIGBUS, is the size the guest's own file operations give it, so a host program changing the size does not move it. - Some system calls: unimplemented system calls return
ENOSYS, so some programs can carry on without the feature. - File owners: a file in a mounted host directory stays root's, so
chownto any other owner or group returnsEPERMthere. The guest stays root when it runs a set-user-ID or set-group-ID program, where Linux would take the file's owner or group. - Socket messages:
sendmsgandrecvmsgpass descriptors withSCM_RIGHTSand credentials withSCM_CREDENTIALSas Linux does, with these differences:- The guest is root, so
SCM_CREDENTIALSmay name any process, user and group, where Linux lets an unprivileged process send only its own. - A socket whose descriptor waits unread in a socket that only that descriptor keeps open, such as its own, stays open: Linux's garbage collector would close both.
- The guest is root, so
- Socket options:
setsockoptrefuses, withENOPROTOOPT, the options whose effect is not modelled, where Linux takes them:SO_PASSSECandSO_TIMESTAMPING, as well as filters and the other options Linux has.SO_RCVTIMEOandSO_SNDTIMEOround to a 1000 Hz tick, where Linux rounds to its ownCONFIG_HZ.SO_SNDBUFandSO_RCVBUFare reported but leave the pair's buffer as it is. The buffer counts only the bytes in it, where Linux also counts each message's or part's overhead, so more small messages fit before a send finds it full, andSIOCOUTQcounts only the bytes the peer has not read. The guest is root, so the options that needCAP_NET_ADMIN(SO_DEBUG,SO_SNDBUFFORCE, …) are set, andSO_PEERCREDnames uid and gid 0. - Signal-driven I/O:
O_ASYNCworks on pipes and sockets only, so standard input and terminals never sendSIGIO, andFIOASYNCsetting it on them isENOTTY, where Linux's do. - Namespaces:
cloneandclone3with a namespace flag (CLONE_NEWNS,CLONE_NEWUTS,CLONE_NEWIPC,CLONE_NEWUSER,CLONE_NEWPID,CLONE_NEWNET,CLONE_NEWCGROUP, orCLONE_NEWTIMEforclone3) fail withEINVALand create no child, where Linux lets the guest, which runs as root, create the namespace.unshareandsetnsreturnENOSYS. - Files of a mounted host directory on hosts other than Linux and macOS:
lseekwithSEEK_DATAorSEEK_HOLEtreats the whole file as data, where Linux finds the holes. On Windowsst_blocksis also the size rounded up to 512-byte blocks, where Linux counts what the host's filesystem allocated; other Unix hosts give their own. On Linux and macOS hosts both come from the host.FIOQSIZEgivesst_blocksin bytes on every host, where Linux gives what the filesystem has allocated so far, which some (ext4 with delayed allocation, overlayfs) count later thanst_blocks. - Block size:
st_blksize,stx_blksizeandFIGETBSZare 4096 for every file, where Linux gives the block size of the file's filesystem: 1024 for/procand terminals (devpts), and the host filesystem's for a mounted directory (4096 for most), whosestatfshere does report the host's.FIGETBSZon a filesystem without a block size, such as overlayfs in most containers, isEINVALon Linux. Pipes, sockets,memfdand other in-memory files are 4096 on Linux too. - Standard input on Windows:
poll,selectandepollsee the host's standard input as always readable, as the Windows host cannot tell whether input is waiting; on Linux and macOS hosts and in WebAssembly it is readable only with input or at its end. FIONREADandSIOCOUTQon the host's standard streams: both returnENOTTY, where Linux answers for what is behind them:FIONREADfor a pipe, terminal, socket or regular file,SIOCOUTQfor a terminal or socket. A guest's own pipes, sockets and regular files are counted as on Linux.- Core files: a core is written as with the kernel's default
core_pattern(corein the working directory); a host that pipes cores to a crash handler (systemd-coredump, apport) is not modelled. The core holds the registers, the fatal signal, the auxiliary vector and the memory, but not every note Linux writes (NT_FILE,NT_X86_XSTATE), and it holds every anonymous mapping, where Linux leaves out one never written. - Memory limit: a write beyond the limit ends the process that made it, where Linux's OOM killer picks the process using the most memory.
- A native JIT: only the WebAssembly build has a JIT; native builds use the interpreter.
- Stable Rust: a nightly Rust is required.
Not verified
- What is verified are the programs listed under Supported features and the per-instruction tests. Other programs are not yet guaranteed to behave exactly the same.
- Fuzzing shows that no problem was found in a fixed time. It is not a proof that every input is safe.
- Differential references were taken on some recorded AMD and Intel CPUs. On a CPU profile not observed yet, the tests stop on purpose.
- Memory, tasks (threads and processes) and open files are unlimited unless the embedder sets limits, so without them an untrusted program could use up the host's memory. The memory limit counts the guest's pages, not file contents, pipe and socket buffers or the emulator's own memory (command line, Rust, JavaScript).
- Automated Safari tests sometimes timed out while waiting for the browser to start. A fix passed ten times in a row, which does not guarantee it never happens again.
Found a problem?
When a CPU fault kills the guest, the command prints the signal, the address and the instruction bytes. Run it again with --report-enosys to list the system calls that returned ENOSYS (Command line). Please include both in an issue.