|
|
Log in / Subscribe / Register

Splicing out vmsplice()

By Jonathan Corbet
June 4, 2026
The splice() and vmsplice() system calls are meant to improve performance for certain data-movement tasks by minimizing (or avoiding altogether) system calls and the copying of data. They also have a long history of security problems. The recent flood of LLM-discovered vulnerabilities has drawn attention, once again, to splice() and vmsplice(); as a result, they may end up being removed altogether.

Some history

Larry McVoy is credited for first raising the idea of a splice() system call that would connect a file directly to a pipe. With the classic POSIX API, an application would copy file data into a pipe with a loop that read chunks of data from the file (thus copying that data into user space), then wrote those chunks to the pipe (copying the data back into the kernel). With a single splice() call, the application could request that the kernel implement that loop, getting the work done with far fewer system calls and less data copying. After years of discussion, a splice() implementation was added in 2006 to the 2.6.17 kernel by Jens Axboe; it looks like:

    ssize_t splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out,
    		   size_t size, unsigned int flags);

It will attempt to copy up to size bytes from fd_in to fd_out; one of the two file descriptors must be a pipe. The return value is the number of bytes actually copied.

vmsplice() was added (by Axboe) shortly thereafter (also in time for 2.6.17):

    ssize_t vmsplice(int fd, const struct iovec *iov, size_t nr_segs, unsigned int flags);

Here, iov is an array of nr_segs iovec structures indicating regions of memory. If fd is a readable pipe file descriptor, data will be read into those memory regions from the pipe. If, instead, fd is writable, the data will move from the memory regions into the pipe. The fact that there is no explicit argument indicating the direction of data movement is one of vmsplice()'s special quirks. Another is that there is no way to know when the data transfer completes and, thus, when it is safe to access the memory given to vmsplice(). The SPLICE_F_GIFT flag "gifts" the indicated memory pages to the kernel; the caller pledges to never touch them again. This option is meant to make zero-copy operations available in some situations.

The implementation of the splice system calls involves a fair amount of complexity within the kernel; it also depends on all kernel subsystems that might receive a spliced buffer to handle it properly. So, arguably, it is not surprising that they have been the focus of a lot of vulnerabilities, including a high-profile exploit (see also this followup article) in 2008. Many of the recently disclosed kernel vulnerabilities involve a combination of these system calls and subsystems that do not handle them correctly.

Protecting read-only files

In mid-May, Pedro Falcato sent a brief patch aimed at making the splice system calls harder to exploit. Specifically, the patch adds a new sysctl knob, fs.splice_needs_write; if that knob is set to a value of one (the default is zero), then it will not be possible to splice() to a file that the calling process lacks the permissions to write to, even if the requested operation is a read from that file that would otherwise be permitted. Similarly, vmsplice() cannot be invoked with memory backed by an unwritable file.

In essence, this patch is an admission of defeat; it is an acknowledgment that the splice system calls simply cannot be implemented in a way that prevents security vulnerabilities. Rather than (continue to) try, the kernel developers would simply be giving administrators the ability to forbid splice operations that might be exploited to give write access to a read-only file. If more such vulnerabilities exist, this change would be a quick way to render them all harmless.

The reactions to the proposal were mixed. Matthew Wilcox said: "I don't have a problem with the idea, other than it's really sad we have to do this". Christian Brauner, though, called it "a knee-jerk reaction to an exploit class originating in buggy modules that we have little control over" and an extension of an already problematic API. Jann Horn suggested that, rather than blocking operations on read-only files, it would be better to degrade the call to an ordinary copy operation. Mateusz Guzik called it "a half-measure which will at best buy few weeks until splice bugs dry out and there will be a new attack vector du jour which people point their LLMs at".

After the discussion had gone on for a few days, Falcato said that the consensus seemed to favor degrading to simple copy operations rather than blocking the system call entirely. There would be a second version of the series forthcoming that took that approach.

Removing vmsplice()

Before that second version could appear, though, Askar Safin showed up with a patch series that takes away the special functionality of vmsplice() entirely. The system call still exists, but the implementation simply copies the data within the kernel rather than attempting to provide complex, zero-copy semantics. In short, a vmsplice() call would be turned into the equivalent preadv2() or pwritev2() call.

Falcato was unimpressed with this development, and suggested that Safin's patches should not even be considered. Brauner had some gentle criticism for the way in which this work was done:

So I think this is a case where no explicit rules have been broken. But if you know that someone has been posting patches and is working on a problem just racing them to get your own stuff merged is very likely to unnecessarily ruffle feathers. So sync with the person next time.

The patches themselves, though, have been reasonably well received. Andy Lutomirski said:

I have no comment on the code or the history. But I'm 100% in favor of the solution. vmsplice is a crappy API, and would be incredibly complex to get the implementation right, and it should be removed. But it has users, and the approach of just mapping them straight to pread/pwrite makes perfect sense.

Linus Torvalds was cautiously in favor of the change; he also suggested making a similar change to splice() if the vmsplice() change does not cause too much anguish. Brauner, for his part, has applied the series with an eye toward merging during the 7.2 development cycle.

That merging should not be seen as a certainty at this point; it is noteworthy that this conversation has happened mostly without the participation of developers who actually use the splice calls. Some of those users are beginning to appear now. Christian Brauner passed on a report of a test regression pointing out a subtle behavior change that can probably be addressed. Willy Tarreau said that he is a heavy user of the splice system calls: "It simply doubles the network bandwidth compared to not using that. (62 Gbps per core vs 31). I would seriously miss it if I couldn't use this anymore." He suggested perhaps further restricting the types of memory that could be passed to vmsplice() (such as only allowing anonymous memory) instead.

So users of the splice system calls do exist, but there seem to be a lot of voices united in their desire to remove the zero-copy logic behind those calls. Torvalds has also indicated a desire to make a similar change to the more widely used sendfile() system call which, he said, was "a mistake". The reimplementation of these system calls should not break any code, since the resulting behavior should look the same from user space, but it does have the possibility of causing performance regressions. That may be enough to prevent these changes from happening in the end. But, as Torvalds said: "I just suspect we'll never get real answers without going the 'let's just see what happens' route". The time has apparently come to see what happens.

Index entries for this article
Kernelsplice()
KernelSystem calls/vmsplice()


to post comments

GNU coreutils

Posted Jun 4, 2026 16:52 UTC (Thu) by collinfunk (subscriber, #169873) [Link] (3 responses)

GNU coreutils

Posted Jun 4, 2026 16:56 UTC (Thu) by corbet (editor, #1) [Link] (2 responses)

I encourage you to join the conversation, test out the changes to see what performance impacts they have, and let people know what you find — that's how decisions like this are made. Of course, not many people see yes as a performance-critical application :) But cat might be another story.

GNU coreutils

Posted Jun 4, 2026 17:19 UTC (Thu) by collinfunk (subscriber, #169873) [Link] (1 responses)

I'll see if I can once I am back home with access to my main system.

The 'yes' change was actually more meaningful, in my opinion. Although we probably use it a bit more than most, e.g., for generating a lot of bytes for testing. The 'cat' change is a bit more niche, since we still try using copy_file_range first and only attempt splice if that fails.

Since 'cat' doesn't use vmsplice, it seems a bit safer for now.

GNU coreutils

Posted Jun 4, 2026 18:34 UTC (Thu) by koverstreet (subscriber, #4296) [Link]

The numbers you posted are pretty dramatic - profiles would be really interesting to see

On safer zero-copy IO

Posted Jun 4, 2026 18:23 UTC (Thu) by koverstreet (subscriber, #4296) [Link] (1 responses)

So the thing that made the splice() family a giant footgun was working directly at the level of struct page; you can splice a page into a pipe - a page you don't own - and the pipe now owns a ref to that page, and everything that consumes spliced pages has to get COW semantics correct. "Ownership" in this model was always unclear.

But obviously, we do want zero copy IO, and we do it all the time in other areas of the kernel without the same footguns.

Half of the solution is doing it via a real iterator - struct bio in the block layer, and iov_iter in vfs/filesystem code. These are just scatter/gather lists, and they're much simpler to use. Notably, iov_iter generalizes what it can point to - it can be a biovec (used to make loopback devices in O_DIRECT mode zero copy), internal kernel memory, user buffers - and the code that consumes it doesn't know or care.

Iterators are also higher performance than sending pages around because the struct page operations are not cheap; the atomic refcount operations can be some of the most expensive parts of the O_DIRECT io path. They're necessary, you have to pin memory for the IO, but you want that done once at the top of the IO stack and avoid touching struct page at all after that - even if you're not getting/putting, any struct page access after that point is a pointer deref and likely cache miss you don't want.

Networking does have skbs - iterators aren't a foreign concept there. The other part of the problem is networking code that does legitimately need to do complex operations with those data buffers; iterators won't save you if buggy code is just writing into a buffer it doesn't own.

For that the solution is just going to be getting more driver code rewritten in Rust.

Rewrite the network stack in Rust?

Posted Jun 7, 2026 13:57 UTC (Sun) by DemiMarie (subscriber, #164188) [Link]

I think the network stack deserves a rewrite. Is there any kernel code that doesn't?

Ugh

Posted Jun 4, 2026 19:11 UTC (Thu) by Sesse (subscriber, #53779) [Link] (13 responses)

cubemap, my video reflector, relies _heavily_ on sendfile() being fast. I can't imagine I'm the only one :-/ (I started being worried as I read the article, given that sendfile() was reimplemented in terms of splice at some point.)

Granted, I don't push multi-ten-gigs of video anymore myself, but, yes.

Ugh

Posted Jun 4, 2026 19:13 UTC (Thu) by Sesse (subscriber, #53779) [Link] (2 responses)

Thinking of it… perhaps the idea is that anyone who really cares about networking performance runs DPDK or XDP or something?

Ugh

Posted Jun 4, 2026 19:42 UTC (Thu) by wahern (subscriber, #37304) [Link] (1 responses)

Netflix uses sendfile on FreeBSD for streaming. It can zero-copy straight from disk to the network card (with TLS offload), though only when using UFS as the design of ZFS isn't amenable to that sort of optimization. DPDK, etc is popular for routing, filtering, and other low-level network-related tasks. I don't think it's very common for application servers (except for doing the former stuff at the edges?); you're basically writing your own unikernel at that point.

Ugh

Posted Jun 4, 2026 23:32 UTC (Thu) by dankamongmen (subscriber, #35141) [Link]

it would be more writing your own L2--L5 stack in userspace, but that kind of functionality is already present if desired in DPDK. XDP is definitely not how you would want to implement anything that relies on reassembling buffers, in my experience.

Ugh

Posted Jun 5, 2026 3:31 UTC (Fri) by wtarreau (subscriber, #51152) [Link] (9 responses)

Then please participate to that thread! There seems to be an untold belief that vmsplice() and splice() are just not used and can be sacrificed. We NEED implementers to participate so that a reasonable tradeoff can be found, that is sufficient to block the attacks, has no performance impact for applications, while possibly requiring slight restrictions (e.g. a sysctl or checking the original page is writable etc). But without real applications participating, you'll only see a competition of ideas from people who just want to plug the splice() holes without perceiving the real-world impacts!

Ugh

Posted Jun 5, 2026 6:22 UTC (Fri) by Sesse (subscriber, #53779) [Link] (2 responses)

Just to be clear, since this is quite surprising: Is the problem that nobody in the kernel actually knows that e.g. Apache is using sendfile() for performance reasons? Or is it more that nobody will care unless the actual implementers step in and argue their case? (vmsplice() is pretty obscure and I've honestly never gotten it to work well in practice; splice() with two sockets also feels quite niche, but splice() between a file and a socket is indeed sendfile() and I'm having a hard time understanding how anyone would go back to pre-2.2 levels of performance for sendfile().)

I can imagine less stressful things in my life to do than to jump into a heated lkml debate, though :-)

Ugh

Posted Jun 5, 2026 18:09 UTC (Fri) by wtarreau (subscriber, #51152) [Link]

It's a bit of a mix. I think that people using splice() in general are not very vocal about it because it's complex and looks dirty, so as a result the whole ecosystem is not well known. In addition Linus already stated his dislike for sendfile(). He stated he clearly wants to stop sending file-backed pages to the network.

I think it's a mistake, possibly driven by a lack of knowledge of the real use cases and their benefits for certain users.

In my opinion, a sysctl to enable/disable vmsplice() from file-backed pages would just do the work pretty well. Indeed there are actually two totally distinct populations using splice()/vmsplice()/sendfile() and friends:
- high-performance servers which are always tuned and which never load any of the outdated network protocols that nobody even remembers what they used to be used for and that are used in LPE exploits
- smartphones, tables and end-user PCs who want to have everything available but don't care about extreme network speeds.

The sysctl perfectly does the job here. Enable it to have your sendfile() or vmsplice() bypass the copy, disable it to have a copy. In both cases the application works, but at different speeds.

Ugh

Posted Jun 5, 2026 21:53 UTC (Fri) by ballombe (subscriber, #9523) [Link]

According to a recent example [1], users of syscalls should just not implement fallbacks for ENOSYS so that removing the syscall would break userspace.

[1] https://lwn.net/Articles/1070072/

Ugh

Posted Jun 5, 2026 6:47 UTC (Fri) by koverstreet (subscriber, #4296) [Link] (5 responses)

I don't think there's any silver bullet here - only allowing it if the original file is writeable isn't terrible but that still disallows it in a lot of setups (and introduces another variable to track down when looking at performance), and I think lots of people are aware of sendfile()'s use in networking. It's just a sticky situation.

But, if you look at what the load is on webservers these days, most of them aren't trying to dump tons and tons of static content onto the wire as fast as possible - it's basically all dynamic content where the cost of generating the content dominates, by far. We're not trying to saturate gigabit ethernet links on Pentium IIs anymore, and memcpy() has gotten a lot faster relative to the branchy logic required for everything else in satisfying a request.

I don't think anyone's webservers are going to be suddenly falling over under load if sendfile is flipped off. The spectre mitigations (especially when they were first released) were probably more impactful.

Ugh

Posted Jun 5, 2026 18:16 UTC (Fri) by wtarreau (subscriber, #51152) [Link] (4 responses)

> if you look at what the load is on webservers these days, most of them aren't trying to dump tons and tons of static content onto the wire as fast as possible - it's basically all dynamic content where the cost of generating the content dominates, by far. We're not trying to saturate gigabit ethernet links on Pentium IIs anymore, and memcpy() has gotten a lot faster relative to the branchy logic required for everything else in satisfying a request.

It really depends. I'm dealing with users running multi-100G links and where zero-copy is crucial. memcpy() is fast but only when run in the cache and on small blocks. When you're duplicating data, you're pumping data from the RAM into the cache, then evicting other pages to make your copy, and do that in loops. At the end your cache is totally cold and your data passes twice on the DRAM bus. With zero-copy you don't even need to touch the CPU nor the caches. You just send the NIC the pointer to the data, and it reads them, encrypts them, segments them and emits them over the wire without the CPU ever having to see them. Not only the data only passes once, but in addition your cache remains filled with application data, not data that you're never going to see anymore and that evicted something else. As soon as your network bandwidth becomes a sizeable fraction of the memory bandwidth, splice wins hands on. And with todays networks it's quite common.

BTW the reason for TLS processing to have moved to the NIC is not at all because it's faster there: it's super cheap to do AES on the CPU nowadays. No the only reason for moving it there precisely is to avoid the NIC<->CPU<->RAM transfers.

Ugh

Posted Jun 5, 2026 21:08 UTC (Fri) by koverstreet (subscriber, #4296) [Link] (2 responses)

Yeah, at those data rates it absolutely does matter - and the second order effects of blowing everything else out of your CPU caches is huge. I saw similar outside performance improvements when I was working on the buffered IO paths - vectorizing generic_file_buffered_read() so that we get all the pages/folios up front and _then_ do the data copies, so that we're not blowing L1 and then walking the radix tree for the next page.

Networking just has some really difficult requirements to deal with - TCP retransmits. That forces you into "we actually own the data buffer even after the syscall has returned", which fs/block has always managed to avoid.

Idle speculative thought: a lot of these design decisions were made back when async anything was really painful - didn't exist for networking, and actually doing async programming in C was always a pita, so we opted for nonblocking IO. Nowadays, we do have io_uring, and async programming in Rust is a dream - so a version of sendmsg() that doesn't return until after the ack, and which you can trivially pipeline with io_uring, could eliminate all the contortions around ownership of pages and be perfectly fine for modern environments (maybe even preferable, it'd get you better error reporting semantics).

If you have that as a primitive, sendfile() is just a version of that that operates from the pagecache directly without faulting it into userspace, similarly for pipes and splice(). And it would actually be faster than splice, because on a pipe there'd be no need for the page refcount operations. For everything else you still need the gup(), like with anything else that DMAs, but the page refcount handling would still probably be simpler overall.

Of course, still doesn't help with drivers that diddle all over buffers that they're not supposed to :) but if userspace bought into the approach it'd be a nice simplification.

Ugh

Posted Jun 7, 2026 10:55 UTC (Sun) by wtarreau (subscriber, #51152) [Link] (1 responses)

In fact even without doing architectural code changes like iouring, we could imagine that sendmsg() could return EINPROGRESS just like connect(), as a way to mean "data are in the process of being sent, we'll wakeup poll once no longer needed". But MSG_ZEROCOPY does almost that, via a receive channel indicating what can be freed.

Ugh

Posted Jun 7, 2026 21:00 UTC (Sun) by koverstreet (subscriber, #4296) [Link]

*twitches involuntarily at -EINPROGRESS*

The problem with -EINPROGRESS is that you're taking something that should be a nice clean RPC/async fn and muddying it - userspace needs to know which sendmsg() completed asynchronously and when so it can free its own buffer, and you want to pipeline multiple sendmsg()s, so you really do need something like io_uring to do this sanely.

And MSG_ZEROCOPY is the same basic mechanic as splice, but if you have an async op with the lifetime of the full send, and can tie the page get/puts to that instead of having them released by the networking code itself, you could potentially get rid of those entirely.

"Is this op to an mlock'd region?" - take a percpu ref that holds the mlock instead, cut the atomic page ref ops out entirely.

Always wanted to do something like this for O_DIRECT.

kTLS offload is missing TLS1.3 and thus post-quantum

Posted Jun 8, 2026 12:09 UTC (Mon) by DemiMarie (subscriber, #164188) [Link]

kTLS offload only supports TLS1.2, which will never get post-quantum cryptography support. That means it dies at the end of 2028 or so.

Why 2028? Because Google’s timeline for migration is 2029: https://blog.google/innovation-and-ai/technology/safety-s.... And, if I understand IBM’s timelines correctly, they believe they can build a quantum computer by then that will be able to break elliptic curve cryptography.

is a hybrid compromise possible

Posted Jun 5, 2026 2:31 UTC (Fri) by robert.cohen@anu.edu.au (subscriber, #6281) [Link] (7 responses)

Is it possible to do a hybrid system where a small number of important subsystems are whitelisted.
If the source or destination isn't whitelisted it falls back to the preadv/pwritev implementation.

is a hybrid compromise possible

Posted Jun 5, 2026 6:04 UTC (Fri) by kleptog (subscriber, #1183) [Link] (6 responses)

This was my thought too. Afaict the use cases people care about are memory to/from pipes and tcp sockets. Make those fast and everything else gets the slow route. You still get to toss out a lot of dodgy code, and the parts that are there are in the most heavily used parts of the system.

Although, since [vm]splice() requires special support in a socket type, I would imagine that there must be a slow fallback path anyway. In which case just deleting the code would have the desired effect. OTOH, if the code was written, someone must have cared.

is a hybrid compromise possible

Posted Jun 5, 2026 6:28 UTC (Fri) by mirabilos (subscriber, #84359) [Link]

Why not let it return ENOSYS instead? And then drop the NR from the headers so future builds will not even show it as present.

Don’t forget QUIC!

Posted Jun 7, 2026 14:02 UTC (Sun) by DemiMarie (subscriber, #164188) [Link] (4 responses)

QUIC is going to be as important as TLS1.3 for high-performance work.

Don’t forget QUIC!

Posted Jun 8, 2026 8:54 UTC (Mon) by paulj (subscriber, #341) [Link] (3 responses)

QUIC is slow, and has design decisions that make it very hard to make quick.

IMO, high-performance work will use another protocol (TCP or else a custom protocol).

Don’t forget QUIC!

Posted Jun 8, 2026 10:04 UTC (Mon) by Cyberax (✭ supporter ✭, #52523) [Link] (2 responses)

There is nothing that would make QUIC be fundamentally slower than TCP+TLS 1.3

It's purely an issue of implementation, right now QUIC is implemented entirely in userspace, without any hardware offloading.

Don’t forget QUIC!

Posted Jun 8, 2026 10:41 UTC (Mon) by paulj (subscriber, #341) [Link] (1 responses)

There are multiple reasons why QUIC can not be faster than TCP:

1. Its mechanism for acknowledging received data has a level of indirection. Tracking ACKs intrinsically, fundamentally, has more overhead than a transport with a direct mechanism.

2. Packets always have to be re-encrypted to be re-transmitted.

It has other annoying, needless, complications - not total blockers to performance, but just adds friction to writing a really fast QUIC.

Don’t forget QUIC!

Posted Jun 8, 2026 11:00 UTC (Mon) by paulj (subscriber, #341) [Link]

Oh, I'd accept these overheads can probably be minimised to a point where QUIC performance wouldn't be far off from TCP, with a good and careful implementation. However, there will always be a cost to them.

Oh, the unbounded number of ACK ranges is another complication. To fully support the standard AND always be performant you must use reasonably sophisticated data-structures, which of themselves come with some constant-time overheads, versus what you could do if # of ACK ranges had some reasonable bound on them. You may argue allowing high number of ACK ranges allows for better performance even under non-trivial loss/re-ordering on v high BDP links, and perhaps you'd be right - but that's not proven, which makes you wonder if the software costs (complexity of code, processing costs) are worth it for the general case.

And, in general, perhaps for each such design decision that adds complexity in QUIC you could argue it is of some benefit in some case, but... certainly the evidence either is missing or is subjective for most (all?) of them. And collectively it leads to a somewhat complex, finicky protocol.

SO_SPLICE?

Posted Jun 5, 2026 11:58 UTC (Fri) by joed (subscriber, #139219) [Link] (2 responses)

For socket-to-socket operations, why not implement OpenBSD[1]/FreeBSD[2]'s SO_SPLICE? All the copying gets handed off to a thread-pool within the kernel. It's a quite a convenient API, and even allows for bidirectional data flow.

[1]: https://man.openbsd.org/setsockopt#SO_SPLICE
[2]: https://man.freebsd.org/cgi/man.cgi?query=setsockopt&...

SO_SPLICE?

Posted Jun 5, 2026 18:45 UTC (Fri) by wahern (subscriber, #37304) [Link] (1 responses)

Socket to socket splicing seems even more niche. I could only find two uses of SO_SPLICE in the OpenBSD tree, both in relayd as variants. Likewise, FreeBSD base only has a single use, AFAICT, and it's in the so_splice command-line test tool, which apparently was written when adding SO_SPLICE to FreeBSD to enable support in their relayd port: https://klarasystems.com/articles/network-offload-and-soc... If SO_SPLICE gains support for splicing kTLS sockets (TBD at the time of that article), or can be used with TLS offload NICs, I could definitely see the value, but still more niche than sendfile. I think OpenBSD invented it just because it was simple enough and an easy performance win for relayd relative to the general lack of performance tuning in OpenBSD.

SO_SPLICE?

Posted Jun 7, 2026 17:34 UTC (Sun) by erincandescent (guest, #141058) [Link]

Socket-to-socket splicing is not too common inside edge proxies/load balancers/etc

Go will do this via splice(2) if you do an io.Copy() between a pair of sockets, and so it shows up in e.g. Traefik

Privacy

Posted Jun 5, 2026 20:17 UTC (Fri) by alip (subscriber, #170176) [Link] (2 responses)

One should mention, the only benefit of splice is not just the performance of zero-copy. Using splice and pipes (not necessarily vmsplice, that's a whole different beast) a process may perform i/o and another process with ptrace or similar rights may NOT observe the contents of that i/o unlike the equivalent preadv/pwritev. This matters for the Syd sandbox and I am sure many more programs in the wild. In this perspective making splice not zero-copy does not change anything but removing it does...

Splice and syd

Posted Jun 7, 2026 14:04 UTC (Sun) by DemiMarie (subscriber, #164188) [Link] (1 responses)

How does this matter for Syd’s sandbox?

Splice and syd

Posted Jun 7, 2026 19:11 UTC (Sun) by alip (subscriber, #170176) [Link]

Syd uses the splice/pipe mechanism mainly for two things:

1. Secure forwarding: PTY sandboxing[1] isolates the terminal,
whereas Proxy sandboxing[2] isolates the network. syd-pty,
and syd-tor are helper programs that perform the forwarding.
They're confined to use splice/pipe only. This prevents another
process from eavesdropping using ptrace/seccomp or other means
of process interaction. Full implications of this is hard to describe
here so I'll refer you to the security sections of syd-pty[3] and syd-tor[4].

2. Secure encryption: Crypt sandboxing[5] uses Linux kernel algorithm
sockets and splice/pipe to perform encryption with minimum information:
ALG_SET_KEY_BY_KEY_SERIAL socket option allows Syd to directly seed
the encryption socket using a key id from a Linux keyring. This means
Syd never keeps key material in memory. Transfer using splice/pipe ensures
security and privacy: Even Syd itself does not have access to the plain text,
so even if an attacker compromises Syd having access to the plain text will
be difficult given also the presence of Syscall Argument Cookies[6].

It's therefore sad to see these useful APIs get deprecated one by one due to
security issues. At some point in time syd-pty broke because splice support
for character devices was dropped from Linux kernel, and added back shortly
after. Now people are discussing to deprecate splice and kernel algorithm sockets
altogether. I understand the reasoning but the usefulness of these APIs are understated,
in my humble opinion.

[1]: https://man.exherbo.org/syd.7.html#PTY_Sandboxing
[2]: https://man.exherbo.org/syd.7.html#Proxy_Sandboxing
[3]: https://man.exherbo.org/syd-pty.1.html#SECURITY
[4]: https://man.exherbo.org/syd-tor.1.html#SECURITY
[5]: https://man.exherbo.org/syd.7.html#Crypt_Sandboxing
[6]: https://man.exherbo.org/syd.7.html#Syscall_Argument_Cookies


Copyright © 2026, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds