One, uring is not extremely problematic to integrate, as it can be chained into a conventional event loop if you want to, or can even be fit into a conventionally blocking design to get localized syscall benefits. That is, you do not need to convert to a fully uring event loop design, even if that would be superior - and usually kept entirely within a (slightly modified) event loop abstraction. The reason it has not yet been implemented is just priority - most stuff aren't bottlenecked on IOPS.
Two, yes you could have e middle-ground. I assume the syscall overhead you call out is the need to send UDP packets one at a time through sendmsg/sendto, rather than doing one big write for several packets worth of data on TCP. An API that allowed you to provide a chain of messages, like sendmsg takes an iovec for data, is possible. But it's also possible to do this already as a tiny blocking wrapper around io_uring, saving you new syscalls.
That should really be in the `SEE ALSO` of `man 3 sendmsg`...
Hmm? sendmsg takes the destination address in the `struct msghdr` structure, and sendmmsg takes an array of those structures.
At the same time, the discussion of efficiency is about UDP vs. TCP. TCP writes are per socket, to the connected peer, and so UDP has the upper hand here. The concerns were about how TCP allows giving a large buffer to the kernel in a single write that then gets sliced into smaller packets automatically, vs. having to slice it in userspace and call send more, which sendmmsg solves.
(You can of course do single-syscall or even zero-syscall "send to many" with io_uring for any socket type, but that's a different discussion.)
> Hmm? sendmsg takes the destination address in the `struct msghdr` structure, and sendmmsg takes an array of those structures.
But that's still pointless on a connected socket. And if you're not using connected sockets, you're performing destination lookups for each and every datagram you're trying to send. It also means you're running with small buffers by default (the 212kB default buffers per socket are shared with all your destinations, no longer per destination). Thus normally you want to use connected socket when dealing with UDP in environments having performance requirements.
Let's take downloading a 1MB jpeg image over QUIC and rendering it on the screen.
I would hope that can be done in about 100k CPU cycles and 20 syscalls, considering that all the jpeg decoding and rendering is going to be hardware accelerated. The decryption is also hardware accelerated.
Unfortunately, no network API allows that right now. The CPU needs to do a substantial amount of processing for every individual packet, in both userspace and kernel space, for receiving the packet and sending the ACK, and there is no 'bulk decrypt' non-blocking API.
Even the data path is troublesome - there should be a way for the data to go straight from the network card to the GPU, with the CPU not even touching it, but we're far from that.
1. A 1 MB file is at the very least 64 individually encrypted TLS records (16k max size) sent in sequence, possibly more. So decryption 64 times is the maximum amount of bulk work you can do - this is done to allow streaming verification and decryption in parallel with the download, whereas one big block would have you wait for the very last byte before any processing could start.
2. TLS is still userspace and decryption does not involve the kernel, and thus no syscalls. The benefits of kernel TLS largely focus on servers sending files straight from disk, bypassing userspace for the entire data processing path. This is not really relevant receive-side for something you are actively decoding.
3. JPEG is, to my knowledge, rarely hardware offloaded on desktop, so no syscalls there.
Now, the number of actual syscalls end up being dictated by the speed of the sender, and the tunable receive buffer size. The slower the sender, the more kernel roundtrips you end upo with, which allows you to amortize the processing over a longer period so everything is ready when the last packet is. For a fast enough sender with big enough receive buffers, this could be a single kernel roundtrip.
This has not been true for a long time. There was an early design mistake that made it quite prone to these, but that mistake has been fixed. Unfortunately, the reputational damage will stick around for a while.
For context, to the best of my knowledge the current approach of the Linux CNA is, in keeping with long-standing Linux security policy of "every single fix might be a security fix", to assign CVEs regardless of whether something has any security impact or not.
CVE numbers are just a way to ensure everyone is talking about the same bug. Not every security issue has a CVE, not every CVE is a security issue.
Often, a regular bug turns out years later to have been a security issue, or a security issue turns out to have no security impact at all.
If you want a central authority to tell you what to think, just use CVSS instead of the binary "does it have a CVE" metric.
How do you figure that? The original definition of CVE is exactly the same as how Linux approaches it.
Sure, in recent years security consultants have been overloading CVE to mean something else, but that's something to fix, not to keep.
> The CVE list aspires to describe and name all publicly known facts about computer systems that could allow somebody to violate a reasonable security policy for that system
As well as:
> Discussions on the Editorial Board mailing list and during the CVE Review meetings indicate that there is no definition for a "vulnerability" that is acceptable to the entire community. At least two different definitions of vulnerability have arisen and been discussed. There appears to be a universally accepted, historically grounded, "core" definition which deals primarily with specific flaws that directly allow some compromise of the system (a "universal" definition). A broader definition includes problems that don't directly allow compromise, but could be an important component of a successful attack, and are a violation of some security policies (a "contingent" definition).
> In accordance with the original stated requirements for the CVE, the CVE should remain independent of multiple perspectives. Since the definition of "vulnerability" varies so widely depending on context and policy, the CVE should avoid imposing an overly restrictive perspective on the vulnerability definition itself.
Under this definition, any kernel bug that could lead to user-space software acting differently is a CVE. Similarly, all memory management bugs in the kernel justify a CVE, as they could be used as part of an exploit.
> with specific flaws that directly allow some compromise of the system
> important component of a successful attack, and are a violation of some security policies
All of these are talking about security issues, not "acting differently".
If the kernel returned random values from gettime, that'd lead to tls certificate validation not being reliable anymore. As result, any bug in gettime is certainly worthy of a CVE.
If the kernel shuffled filenames so they'd be returned backwards, apparmor and selinux profiles would break. As result, that'd be worthy of a CVE.
If the kernel has a memory corruption, use after free, use of uninitialized memory or refcounting issue, that's obviously a violation of security best practices and can be used as component in an exploit chain.
Can you now see how almost every kernel bug can and most certainly will be turned into a security issue at some point?
Because no system has been ever taken down by code that behaved different from what it was expected to do? Right? Like http desync attacks, sql escape bypasses, ... . Absolutely no security issue going to be caused by a very minor and by itself very secure difference in behavior.
That's "security bug". Please stop saying it's not.
Additionally, any security bug in the kernel itself, so any use after free, any refcounting bug, any use of uninitialized memory.
Can you now see why pretty much every kernel bug fulfills that definition?
> The mission of the CVE® Program is to identify, define, and catalog publicly disclosed cybersecurity vulnerabilities [emphasis mine].
In fact, CVE stands for "Common Vulnerabilities and Exposures", again showing that CVE == security issue.
It's of course true that just because your code has an unpatched CVE doesn't automatically mean that your system is vulnerable - other mitigations can be in place to protect it.
> The CVE list aspires to describe and name all publicly known facts about computer systems that could allow somebody to violate a reasonable security policy for that system
There's also a decision from the editorial board on this, which said:
> Discussions on the Editorial Board mailing list and during the CVE Review meetings indicate that there is no definition for a "vulnerability" that is acceptable to the entire community. At least two different definitions of vulnerability have arisen and been discussed. There appears to be a universally accepted, historically grounded, "core" definition which deals primarily with specific flaws that directly allow some compromise of the system (a "universal" definition). A broader definition includes problems that don't directly allow compromise, but could be an important component of a successful attack, and are a violation of some security policies (a "contingent" definition).
> In accordance with the original stated requirements for the CVE, the CVE should remain independent of multiple perspectives. Since the definition of "vulnerability" varies so widely depending on context and policy, the CVE should avoid imposing an overly restrictive perspective on the vulnerability definition itself.
For more details, see https://web.archive.org/web/20000526190637fw_/http://www.cve... and https://web.archive.org/web/20020617142755/http://cve.mitre....
Under this definition, any kernel bug that could lead to user-space software acting differently is a CVE. Similarly, all memory management bugs in the kernel justify a CVE, as they could be used as part of an exploit.
So yes, any CVE is supposed to be a security problem, and it has always been so. Maybe not for your specific system or for your specific security posture, but for someone's.
Extending this to any bugfix is a serious misunderstanding of what an "exposure" means, and it is a serious difference from other CNAs. Linux CNA-assigned CVEs just can't be taken as seriously as normal CNAs.
You've doubtless heard Tony Hoare's "There are two ways to write code: write code so simple there are obviously no bugs in it, or write code so complex that there are no obvious bugs in it.". Linux is definitely in the latter category, it's now such a sprawling system that determining whether a bug "really" has security implications is no long a reasonable task compared to just fixing the bug.
The other reason is that Linux is so widely used that almost no assumption made to simplify that above task is definitely correct.
They are right, by the way. When CVEs were used for things like Heartbleed they made sense - you could point to Heartbleed's CVE number and query various information systems about vulnerable systems. When every single possible security fix gets one, AND automated systems are checking the you've patched every single one or else you fail the audit (even ones completely irrelevant to the system, like RCE on an embedded device with no internet access) the system is not doing anything useful - it's deleting value from the world and must be repaired or destroyed.
1. This is pretty clean and straightforward. 2. This is obviously what we need to decouple a bunch of things without the previous downsides.
What has made it so hard to integrate it into common language runtimes? Do you have examples of where there's been an irreconcilable "impedance mismatch"?
much more approachable, boats has written about challenges integrating in rust: https://without.boats/tags/io-uring/
in the most general form: you need a fairly "loose" memory model to integrate the "best" (performance wise) parts, and the "best" (ease of use/forward looking safety) way to integrate requires C library linkage. This is troublesome in most GC languages, and many managed runtimes. There's also the issue that uring being non-portable means that the things it suggests you must do (such as say pinning a buffer pool and making APIs like read not immediate caller allocates) requires a substantially separate API for this platform than for others, or at least substantial reworks over all the existing POSIX modeled APIs - thus back to what I said originally, we need a replacement for POSIX & BSD here, broadly applied.
Zero-copy APIs will necessarily be tricky to implement and use, especially on memory safe languages.
Seems like the author points out two things:
1. The lack of rust futures supporting manual cancellation. That doesn't seem like an inevitable choice by rust.
2. Sharing buffers with kernel mode. This is probably a bigger topic.
uring is extremely problematic to integrate into many common application / language runtimes and it has been demonstrably difficult to integrate into linux safely and correctly as well, with a continual stream of bugs, security and policy control issues.
in principle a shared memory queue is a reasonable basis for improving the IO cost between applications and IO stacks such as the network or filesystem stacks, but this isn't easy to do well, cf. uring bugs and binder bugs.