• 0 Posts
  • 42 Comments
Joined 2 months ago
cake
Cake day: August 12th, 2026

help-circle
  • I found KDE beta 4 hidden off in the /opt directory on the Red Hat 5.something install CD. It’s hard to overstate how impressive it was. I was running Windows 2000 at the time and dabbling with Linux.

    Linux, at the time, required you to run startx to run XFree86. Once there, you could run a window manager from the terminal, or you could put one in your .xinitrc file. You could also load other programs. A few window managers did this for you, giving you a dock or start-button interface for launching things. Then the programs you launched had a mishmash of visual styles and UI models arising from using completely different widget sets. Copying and pasting text between them might work, drag and drop or anything other than rich text did not.

    KDE was different. You still had to start X manually (graphical login things existed but I didn’t know how to configure them) but then it launched into a complete desktop. It wasn’t just a window manager, it was a complete suite of applications with a consistent UI throughout. It felt at least as polished as a fresh install of Windows at the time.


    score 1 · canonical


  • You are missing the point in your rush to be patronising.

    The common problem among people who are not computer security is not having a proper threat model and therefore not evaluating your security theatre against any plausible attack.

    Any attack that can compromise the integrity of a running app on a platform like Android or iOS can also compromise the integrity of the attestation. It gives you a nice warm fuzzy feeling and absolutely nothing else. Any system that is sufficiently compromised that the attestation would fail for a reason that will affect your app is also sufficiently compromised that the attacker can compromise the attestation.

    It does not make attacks noticeably harder but, since you’re going to be patronising about security people here’s a bit more explanation:

    We think of mitigations in two categories: durable and non-durable.

    Durable mitigations completely eliminate the root cause of a vulnerability class. If they can be made to work within an acceptable cost, they are always worth it. They don’t prevent attacks but they mean that you don’t need to consider a particular attack vector.

    Non-durable mitigations add work factor or block a particular exploit technique. They don’t prevent a vulnerability class, they just make it a bit harder. In the early 2000s, people rolled out a load of these because they did increase work factor. The problem is that newer exploit techniques work around them and are automated. Most exploits today are mostly, if not completely, automated. A mitigation that can be bypassed by an automated step in an attack chain does not make it less likely that an attacker will succeed.

    At the same time they’re politically impossible to remove. If you remove them, and an attack succeeds that they would have blocked, that’s terrible PR even if there is a trivially automatic step that would have allowed the attacker to succeed with a slightly different attack. And this means that you’re stuck paying the performance and complexity costs basically forever.

    And that’s when we security folks that you love to patronise do a little thing called a ‘cost-benefit analysis’. If you work for a bank, you might have heard of these. I’m told banks do them as well.

    We look at these non-durable mitigations and decide whether the attacks that they will prevent (relative to a threat model, where we look at the different possible abilities of an attacker and how they could do the thing that we’re trying to stop them doing) and weigh that against the cost of supporting the mitigation for an unbounded amount of time. And, when the mitigation requires a lot of complexity in the most privileged parts of a system, we weigh those very carefully.

    And, in the case of app-level remote attestation, we conclude that it’s a large amount of code in the TCB, it’s increased attack surface, and it can be bypassed by any attacker who has the ability to do the thing that it’s trying to prevent.

    And then we tell you not to waste time on it.

    Not because, as you imply, we’re complete muppets, but because we’ve actually thought through the cost of the complexity involved and concluded that the hypothetical (and small) increase in difficulty for attackers is not worth it.

    Note that companies like Apple and Google will see the cost-benefit calculations differently because ‘users of this banking app are tied into either an Apple or Google ecosystem’ is a very large benefit. But that’s a monopolistic market positioning benefit, not a security benefit.


    score 1 · canonical


  • There are a lot of arguments against remote attestation but I’d like to focus on one with respect to your use case: it does not give useful guarantees for security in this model.

    Remote attestation is great for simple embedded devices wanting to connect to a server, to ensure that the server is talking to only trusted endpoints. It’s great in this use case because the code on the client devices that you need to verify is largely stateless, so compromises struggle to persist, and the total amount of code is small. It’s not perfect, but it adds some defence in depth, especially when coupled with a bootloader with anti-rollback protection.

    Remote attestation is absolutely essential for confidential cloud computing to attest to the first stage loader in a pristine confidential VM so that you know that you have an isolated VM to boot your OS into and that the bootloader will boot only the thing that you have authorised it to boot.

    Remote attestation of applications on general-purpose devices is absolutely snake oil for security. It tells you that the application is running on top of a few tens of millions of lines of privileged C code and if that code is 100% bug free then you have some properties about the application.

    Remote attestation builds on the same ideas as secure boot. Each layer starts in a known-safe state and then loads the next layer. Each layer is then able to attest to the initial state of the next layer and depends on an indicative security proof to assume that, if the initial state was secure, the next stage is also secure.

    When you’re talking about something like the first EFI loader, you get some reasonable claims. It typically has a tiny attack surface (though some support network booting protocols) and is a small amount of code. It has to do some early initialisation and then read another loader into memory, read its cryptographic signature, verify the signature against an allowed set of public keys, and jump to it. If it’s a trusted version then it’s probably still trustworthy. It may load a larger second-stage loader that has a richer UI and can do things like network boot a recovery image.

    You probably can still make meaningful claims about this second-stage loader. It may even be a full OS, but it’s running a smaller set of programs, a limited set of drivers, and has a smaller attack surface. It does the same kind of signature verification as the first stage, but this time for an OS image (possibly a kernel, possibly a kernel plus loadable modules, possibly an initial immutable root filesystem).

    Now the OS boots. It’s running millions of lines of C in privileged mode. It spawns a bunch of services that have sufficient privilege that they could do a lot of damage if compromised. It exposes attack surface to userspace, to every network interface, to any bus. It runs for days at a time (possibly weeks or months) accumulating state that may allow an attacker to seed the ground for an attack. Knowing the initial state of this tells you absolutely nothing about the current security posture of a system.

    It then runs a program and provides an attestation quote related to the state of the system when the program was started. Everything in that quote that comes after the kernel boot is true if and only if the kernel has not been compromised[1].

    The kernel is responsible for enforcing process isolation. Any attacker that has the ability to tamper with the state of the attested process from another process also has the ability to tamper with the attestation state. One requires a kernel privilege-elevation vulnerability, the other requires a kernel privilege-elevation vulnerability.

    So the thing that an application-level attestation quote tells you is that either the application is running unmolested, or an attacker has compromised the kernel and has the ability to compromise the application. This is not a useful security property (it’s also roughly the property that you can assume withoutdoing remote attestation).

    It’s actually somewhat worse than this because all of the machinery to be able to do remote attestation adds a lot of code that runs in one of the most privileged environments of the system. So the mere fact that a system can do remote attestation at this level in the stack means that it has a larger TCB and a larger attack surface than one that can’t. So you’re asking OS vendors to intentionally weaken the security of their systems to get a meaningless security claim.

    You would get far better security by having some additional path that required them to confirm any large transactions via a separate device.

    Fortunately, the security of most banks doesn’t depend on any of this nonsense. It depends on having legally mandated undo functionality, so transactions that were initiated by a compromised device can be rolled back. As long as users are notified of transactions rapidly (within the rollback window), you have the security that you need without building a house of cards stuck together with snake oil.

    [1] Some of these things use hypervisor extensions and have a hypervisor make claims about the state of the kernel[2]. That doesn’t really add much work factor for the attacker because kernels are so big that code-reuse attacks are trivial.

    [2] Note that things like Windows’ virtualisation-based enclaves are quite different here. Hyper-V has a good security track record and a secure boot chain to Hyper-V gives you some useful properties. Hyper-V can usefully attest that a micro VM is secure and can then do things like emulate a smart card there (rather than a full OS). The problem is that all of the I/O goes via the untrusted layer, so you can’t really depend on it for any user-initiated action.


    score 1 · canonical


  • Yup, there are good, but also silly reasons for this. Most ISAs that have an FPU have some float-to-int conversion instruction but the rules are not the same (I think they are for anything that implements IEEE754, but C predated that and still doesn’t mandate it, though C23 lets you check at compile time if the storage representation is IEEE754 [which doesn’t necessarily imply the operations are: i386, for example, operates on 80-bit floats but can store them as 32- or 64-bit IEEE754 values]).

    In theory, this is a good fit for ‘implementation defined’ but that requires the compiler to write down exactly what will happen. On most ISAs, this is possible but some older ones didn’t specify the behaviour and left it up to implementations, so the resulting compiled code would behave differently on different CPU versions.

    C++ recently introduce a notion of ‘erroneous behaviour’ that fits this. Ideally, compilers diagnose on erroneous behaviour. If they don’t, it may give non-deterministic values but they are stable during a program execution and may not directly cause UB. I’m not sure if this was made EB in C++. It’s similar to uninitialised values: they are now EB in C++ and reading them will give an arbitrary value but branching on that value is guaranteed to enter either the if or else branch, whereas when they were UB it could branch anywhere (or crash or do anything else).

    I am not sure if C introduced EB.


    score 1 · canonical






  • The RAM savings look very high, but I guess a language with that header structure and a lot of small objects would have these properties. In some of the analysis that we did for CHERI, we typically found that under 20% of pages contained one or more pointers. A lot of memory ends up being big buffers, images, and other data that doesn’t contain pointers.

    This is also a big part of the reason that x32 largely went away. One process using it may be smaller, but once you factor in the extra disk space and RAM from 32-bit and 64-bit shared libraries, you end up with a much less clear win. If everything used x32 then it would be a win probably but it turns out that JavaScript and some similar scripting languages like NaN boxing, so 32-bit isn’t a win. Similarly, lightweight SFI things (e.g. WebAssembly) work nicely if you can carve out a 4 GiB chunk of the address space and just truncate pointers to that size (optionally with a base offset). So you find that things like Node.js and web browsers want a 64-bit system.

    The same thing happens with JVMs, where it’s nice to be able to map the heap in at least two places for generational GC. You also want to be able to have a large address range to put your Java objects in that doesn’t overlap anything else, so a 2 GiB userspace address space may limit you to something on the order of a 300 MiB heap. And the pointer size increase doesn’t matter for JVMs because 64-bit JVMs typically use a 32-bit integer to represent objects, which is an offset from the base of the heap right shifted by 3 or 4, so you can have a 64 GiB heap. And you can often put NIO buffers and large arrays in a separate heap region with only a small object header in the 64 GiB heap, which lets you actually scale to heaps that are a large fraction of a TiB, without any real pointer-size overhead.


    score 1 · canonical



  • What do people use for backup MX? Synchronising spam filter state seems annoying and I don’t want the backup MX to be a way past the spam filter. A lot of spammers are now apparently starting with the lower-priority MXs on the assumption that they don’t do as much spam filtering.

    I self host and had an issue a while ago where the incoming server died and I needed to update my DNS, but my DNS provider required sending me an email to log in. I’d like to have a backup MX to support that without so much suffering (even if I need to cat the mail spool). Ideally, I’d like the backup MX to monitor the health of the primary and not accept mail when it is working but comes online and catches incoming email when it fails.


    score 1 · canonical






  • Thanks @loige, this is a great article (well written and informative)!

    One thing to mention from the terminology:

    Whiteout is a really common term for any kind of merged dictionary (filesystem or otherwise) where a newer layer needs a mechanism for deleting an entry from an older one. And it’s another one of these culture-specific terms leaking into generic discourse. Wite-Out is a brand of correctional fluid sold in the USA. For folks in Europe, Tipp-Ex is the equivalent.

    For those people too young to have had to suffer with typewriters or pens and who have therefore never had to deal with either of these products:

    When you wrote with a pen or typed with a typewriter, there was no delete functionality. To do the equivalent of a deletion, you painted some white fluid over the top (the typewriter version involved little white ink pads that fitted in front of the ribbon so you could type a white letter over the black one and ‘delete’ it). This was picked up as a metaphor in computing. The US-trademarked term was changed to its more conventional spelling.

    Naming is hard.

    Locally, the storage driver (for example, overlay2) keeps each layer as an extracted directory and uses a union filesystem to stack them

    It’s worth noting that this, as an abstraction, was one of the big changes with containerd and OCI standardisation for this model. Docker’s filesystem abstraction explicitly did work like (I think it can now do both?) this but later implementations converged on a snapshotter abstraction rather than an overlay abstraction.

    In the overlay model, you create a bunch of directories and then assemble them into layers. In the snapshotter model, you create a directory tree then apply a bunch of changes and assign that a name, then you apply other changes and assign it a different name. The overlay model is more flexible: It can express everything that you can express in the snapshotter model and also some other things such as different composition orders (so, in theory, could provide mixins rather than strict sequential deltas). The problem is that a bunch of the desirable implementation substrates are snapshots, not arbitrary compositions. Implementing the simpler mode with the more complex is easy. It’s easy to use an overlay filesystem to implement a snapshot model, it’s hard to use a CoW filesystem to implement the overlay model.

    This is how a modification is represented: the new layer simply ships a new, complete version of the file

    This is one of the places where the article’s earlier comment about the difference between the snapshotter and the distribution format being different is important. If you use an overlay FS driver, you’ll have two directories with different versions of the file. If you use the ZFS snapshotter, you’ll extract the second one on top of the first and, if the changes are small, you may or may not get a separate copy depending on whether you’ve enabled dedup on the datasets that it uses.

    BTW, am I the weird one, or did your brain come up with the same question? 🧠

    Both.

    In other words, PAX is tar’s official escape hatch for extra metadata

    Pax isn’t an acronym, it literally means peace (in Latin) because it’s the result of a compromise between the divergence in the UNIX world between tar and cpio (and their various forks). Ironically, POSIX 2001 defined the modern pax format, which is supported by tar on GNU and FreeBSD systems but not by pax. Because consensus is even harder than naming.

    As files prefixed with .wh. are special whiteout markers, it is not possible to create a filesystem which has a file or directory with a name beginning with .wh…

    This seems weird, but there are a bunch of other filesystem-specific limitations that OCI container authors need to care about, so it’s probably not the weirdest. Try creating a Windows OCI container image containing a file called COM1 some time.


    score 1 · canonical




  • Packet count attacks are extra spicy because routers often have a lower packets-per-second capacity than their bits-per-second capacity

    Presumably this is even more true for things like the Smurf attack, where the origins of the reflected packets are different and so limits on packets-per-host don’t apply

    Modern protocols (last 10 or 15 years) are designed to avoid amplification. QUIC is the best example: the client has to pad its initial packet to be larger than the server’s response, and the client is responsible for connection retries.

    That’s nice. How variable is the response size? Can IoT devices try a small request and a larger one if needed, or do they need a large initial packet? I’m starting to look a bit (a very small bit so far) at QUIC for CHERIoT because it has a couple of potential memory reductions:

    • The TCP bit of the network stack is pretty big and includes a pile of buffering, whereas UDP can trivially do zero-copy and is smaller.
    • The TLS layer needs to buffer inbound data for, potentially, multiple packets because TLS over TCP doesn’t know about packets and the receiving stack can’t pass on the data until it’s got to a boundary where it has an HMAC to check. In contrast, QUIC is encrypted per-packet and so can have less buffering.

    Needing larger transmit buffers may be less optimal.

    More fundamentally, I think it’s a mistake to base a global network protocol on addresses

    As I recall, this was one of the big disagreements with the design of IPv6 (are addresses routes?), and the consensus ended up in the opposite direction, though I’m not sure I understood the rationale.


    score 2 · canonical