Hacker News new | ask | show | jobs
by usrbinenv 36 days ago
I understand why in 1979 and perhaps until mid 1990s capability OS architecture might have been irrelevant and excessive. But after that, it sounds like the only architecture suitable for the internet age, where you can download and run anything from anywhere. Instead, we're stuck with legacy systems, which now contain layers of layers of abstractions and security measures. User rights, anti-virus software, vetting (signatures, hashes, app-store verification) - all become obsolete or near-obsolete in a capability-based system where a program simply doesn't have access to anything by default. Part of the appeal of virtualization is also due to the fact that it isolates programs (for instance, I only run npm inside Docker container these days, because chances are some package will contain malware at some point).

Part of it is inertia, but part of it is ignorance. Enthusiasts spend tons of money and effort building another GPU enabled terminal or safe programming languages - and maybe that's fine, but I wonder what we could've accomplished if people were simply aware what a well-designed capability OS could be like, because this is literally the only OS paradigm in existence (that I know of) that's even worth any serious effort.

14 comments

If you go through old CS OS texts on the matter, they really didn't have the same understanding of capabilities then as the later object-capabilities (ocap) model would introduce. Typically they would show an access control matrix, note that acls were rows and capabilities columns and note that they are duals of one another. They're the same, acls are easier to manage, done.

OP is arguably the first paper that introduces ocaps. Some of the issues are discussed in "Capability Myths Demolished" https://papers.agoric.com/assets/pdf/papers/capability-myths...

I’m not going to argue against much of the content of this paper, but it should be pointed out that their argument in the middle section against the “confinement myth” seems pretty bogus. They say that you can isolate the capability read/write resource from the data read/write resource, but… this makes absolutely no sense. Bits are bits. If you assume some out-of-band isolation of capability distribution then you’ve changed the game, but even that isn’t enough for me to believe that isolation is possible.
Early thinking was in terms of capability handles. As with file descriptors, the handle is only meaningful when passed across a protection boundary to something which can check if the handle is valid.

Later, there were encrypted capabilities, which are signed data, like TLS certs. These get kind of bulky. And hardware support, in a few machines.

alternate futures where the 33bit versions of the i960 became the processor family of choice.
Consider two processes on a *nix system, and for the sake of argument let's say they're sufficiently isolated from each other as to have only one communications channel between them. If that communications channel is a unix domain socket, one process can send a file descriptor (effectively a capability) to the other over the socket. Each process has a file descriptor table in the kernel whose integer keys are only meaningful to that process in particular, and the kernel provides a mechanism to transmit file descriptors across a socket. The kernel mediates in this case.

If the communications channel is not a unix domain socket and is instead something like a TCP connection, you don't have this option available to you.

You aren't always just sending bits from one process to another!

No, you’re using the same sleight of hand as the paper.

Boebert’s objection is about whether Alice can transmit unauthorized authority to Bob across a security boundary that’s supposed to prevent that flow. Your SCM_RIGHTS example is a case where the kernel is deliberately providing a sanctioned channel for authority transfer, with the kernel’s blessing, between two processes that the kernel does not consider to be on opposite sides of a mandatory access control boundary. Unix has no (*)-property. There is no “high” and “low” in the Bell-LaPadula sense on a standard Unix system. So of course the kernel mediates the transfer cleanly; it’s not enforcing any policy that would be violated by the transfer.

The moment you try to extend this to the actual case under dispute—Alice is “high,” Bob is “low,” and the security policy says high-to-low information flow is forbidden—then if the kernel refuses to deliver the fd across the boundary, the security property was enforced by the separate MAC layer, not the capability mechanism.

The conflation which is endemic in this whole debate is between “capabilities as a kernel-mediated authority mechanism” and “capabilities as a property that holds across all observable behavior of the system.” Unix file descriptors are the former. Boebert’s objection is about the latter.

Your communication channel between Alice and Bob is, itself, a capability (or a collection of capabilitys) that grants Bob memory write, Alice memory read, but does not grant the ability to transmit a capability from Bob to Alice.

Absent a misunderstanding on your part, the only way I can coherently interpret your argument is that you are arguing that the presence of kernel data structures mediating the handles somehow makes it not a capability system. That there is some background element mediating the validity of your capability representation and thus that is just a MAC layer; unless you can write the byte representation of your handle into memory and somebody else can read it out and then have access to that resource it is not a capability.

One, that allows forging capabilitys unless they are cryptographically secure against collisions.

Two, the actual essence of capabilitys is not being bearer tokens, it is non-construction. Capabilitys are derived from existing capabilitys, not manifested into existence. They have provenance. It is the OS equivalent of not allowing programs to cast arbitrary integers to pointers and thus manifesting pointers into existence which breaks basically every high level memory safety guarantee. You do not allow programs to cast arbitrary data into handles to resources which is what ambient authority systems effectively require.

I'm going to first apologize for engaging in rhetorical sleight of hand myself, since I indulged in a bit of the hand-wavy argumentation that happens so often in these nit-picky debates. I'm going to respond cleanly here mostly to sharpen my own argumentative saw.

The original PSOS paper makes a few claims that are in tension with one another, and then buries the lede about how that tension can be addressed. Here's a few passages, directly quoted from the paper:

> [...] there are several important pragmatic reasons why PSOS capabilities are useful as a naming and protection mechanism for supporting abstract objects.

> 1. The capability mechanism has a very simple implementation. This allows capabilities to be built into the system at the lowest level of abstraction, thus making capabilities available for the most primitive objects.

> 2. Capabilities are uniform in size, making them easy to manage.

> 3. The inclusion of access rights in capabilities permits efficient fine-grained control of access to objects.

> 4. Capabilities can be written into storage (including secondary storage) and retrieved from storage in the same manner as other data, and therefore have many of the properties of other data.

Item 4 above is the one that should draw the most attention. I don't think anyone would contest the claim that PSOS has wonderful ergonomics for managing access to resources, but the moment you want to impose a system-wide access control policy then you must add another security mechanism, completely outside the capability abstraction, that adds some friction. This is fully acknowledged by the PSOS authors, although frankly they buried the lede since this is the only thing that the secure systems folks really cared about at the time. From the section on Store permissions:

> Because simplicity of the basic capability mechanism is extremely important to achieve the goals of PSOS, any means for restricting the propagation of capabilities should not add complexity to the capability mechanism. [...] A few access rights (only one is currently used by PSOS itself) are reserved as store permissions. This is the only burden placed on the capability mechanism.

> By properly choosing the segments that are capability store limited, some very useful restrictions on the propagation of capabilities can be achieved. The restriction used in PSOS is not allowing a process to pass certain capabilities to other processes or to place these capabilities in storage locations (e.g., a directory or interprocess communication channel) accessible to other processes. [...] The store permission mechanism has been selected as primitive in the system because it achieves the desired result with negligible additional complexity or cost.

This appears as claim 8 in the summary section of the paper near the end:

> Propagation of capabilities can be restricted by use of capability store permissions. The passage of a capability to other users can be prevented by not including process store permission in that capability's access rights.

Ok, so that's the PSOS paper and it's claims. Boebert's paper--really a note, since it is a mere 3 pages--states its argument in fairly direct terms:

> The attack is made possible by an inherent attribute of pure capability machines: the right to exercise access carries with it the right to propagate that access. Thus even if an omniscient oracle correctly creates capabilities, it cannot control their further propagation. If extra mechanisms are imposed to impose this control, the machine is no longer an unmodified capability machine.

The only issue here is, perhaps, semantic: Boebert (correctly) states that an unmodified capability machine cannot provide what is considered a very basic mandatory security policy, but the PSOS folks already acknowledged this by stating that the system needs a capability store permission manager for mandatory security policy enforcement. The phrasing that they used--"the store permission mechanism has been selected as primitive in the system"--is the bait-and-switch where they treat it like part of the capability model rather than making it clear that it is an entirely distinct mechanism that must be composed with pure capabilities to achieve the (genuinely difficult) security properties that systems designers were seeking.

I suspect the horse is already dead it's worth double-tapping to make sure, so let's continue. The Myths paper muddies the waters further by making this claim after supposedly debunking Boebert:

> Boebert’s result is valid in any capability system that cannot distinguish between data transfer and capability transfer. But partitioned and type-enforced capability systems do not have this problem, and password capability systems have been engineered to avoid this problem [1, 11].

> Moreover, it has been formally verified that any capability system enforcing independent controls on data transfer and capability transfer can enforce both confinement and the *-Property [22].

We'll focus on reference [22] since that is the stronger claim here. That paper is Shapiro & Weber (2000) "Verifying the EROS Confinement Mechanism": https://flint.cs.yale.edu/cs428/doc/eros-verify.pdf

This is the motivation for their paper, which is stated unambiguously:

> Boebert [1] and Karger [9] have argued that unmodified capability systems cannot enforce even basic mandatory access controls such as the *-property. Both have proposed solutions in the form of hybrid protection architectures. Karger has also argued that unmodified capability systems cannot enforce confinement [8]. Given that EROS is a pure capability system, and that its security design rests on its ability to enforce confinement, a rigorous verification of the EROS confinement mechanism is necessary.

For some reason, they decide to respond to these claims in the Related work section, just before their conclusion, although they do address them head-on:

> Boebert [1] and Karger [9] show that pure capability systems cannot enforce the *-property. While their conclusion is correct, capability systems do provide sufficient strength to construct mandatory policies at a higher level of abstraction with reasonable performance, as has been done in KeySafe [14].

> Karger has also shown that unmodified capability systems cannot enforce the confinement policy [8]. The apparent discrepancy results from differences in term definition. Karger’s confinement policy is a mandatory access control policy: "this piece of information must not be disclosed to that set of unauthorized parties." That is, it is a policy concerning the flow of information to subjects. Lampson’s confinement problem [10] imposes a weaker constraint: information can flow out of the subsystem only through authorized channels. That is, in the Lampson definition the channels define an encapsulation boundary to be enforced.

> We believe that the KeySafe architecture can enforce both the *-property and Karger’s confinement policy, but this does not directly contradict their claims. KeySafe is a reference monitor built on top of a more primitive capability mechanism; such a reference monitor constitutes a modified capability system in the sense of Karger’s discussion.

It's worth questioning whether the Myths authors were justified in citing this paper the way they did. But either way, I think it's pretty clear that once you pin down a precise definition of the terms used in the discussion, there is little disagreement among any of these authors. However, in casual arguments this precision is lost and you end up with a situation where two things are true at the same time but people choose to talk about only one at a time and think they're winning arguments:

1. An unmodified capability machine cannot enforce the *-property or mandatory access control confinement policies.

2. Modifying a capability machine to enforce such policies (and provide proof of enforcement) is straightforward because there is a single clearly-defined interface through which the systems may be composed.

My stance is that the PSOS folks screwed up their marketing. They really do have a superior product, so to speak, but they tried to downplay the fact that it did not provide a solution to the genuinely difficult problem of enforcing MAC policies (which was really about reference monitor discipline, not capabilities or ACLs). The right pitch for ocap design is "we offer a cleaner, more compositional, more auditable substrate for authority management--which is itself a substantial contribution and worth caring about--and on top of that substrate you can build the same MAC policies you'd build on any other substrate, but with better starting axioms and clearer proof structures." That's a contribution that doesn't need to be defended against Boebert because it doesn't claim (or appear to claim) what Boebert showed couldn't be claimed.

That argument assumes that the delegation of a capability to another process must happen through a path of interprocess communication that can be established only by the operating system, if the processes that want to communicate have the capabilites for this.

I have not studied to see how the existing capability-based operating systems solve this problem, because it seems that this is not a simple solution. If the capabilities are very fine-grained, to make certain that IPC really cannot happen, that might be cumbersome to use, while coarse-grained capabilities could be circumvented. To really prevent IPC without appropriate capabilities, a lot of the convenient features of a UNIX-like system must be forbidden, like the existence of files that can be read by any user, or directories like /tmp , where anyone can write a file.

> If the capabilities are very fine-grained, to make certain that IPC really cannot happen, that might be cumbersome to use, while coarse-grained capabilities could be circumvented.

In SeL4 it’s kinda like this: A capability is an opaque handle you can invoke to RPC into some other process or into the kernel. There’s no worry about how fine grained capabilities are because there’s no global table of permission bits or anything like that. Processes can invent capabilities whenever they want. Because caps just let other processes call your code, you can programmatically make them do anything.

Suppose I want to give a process read only access to a file I have RW access to. The OS doesn’t need a special “read only capability” type. It doesn’t need to. Instead, my process just makes capabilites for whatever I actually want on the fly. In this case, I just make a new capability. When it’s invoked I see the associated request, if the caller is making a read request, I proxy that request to the file handle I have. (Also another cap). And if it’s a write request, I can reject it. Voila!

This is how you can write the filesystem and drivers in userland. One process can be in charge of the block devices. That process creates some caps for reading and writing raw bytes to disk. It passes the “client side” of that cap to a filesystem process, which can produce its own file handle caps for interacting with directories and files, which can be passed to userland processes in turn. Its capabilities all the way down.

This kind of proxy capabilities has other benefits as well, e.g. you can implement a disk quota, or transparent compression, or logging, or ask the user (if you have a capability which can do that), or provide access to a part of the file as though it is the entire file, etc.

Or, if a program requests access to a camera, you can provide a capability with a still picture, a video file, a filter (e.g. that resizes the picture or modifies the colour) from some source (including, but not limited to, a camera), etc; this can be helpful in case e.g. you do not have a camera on your computer, or for testing.

(Other people have similar ideas, sometimes independently than I do.)

There is also a way to transmit capabilities across a network; I had thought of how a protocol would be made to do such a thing.

That works perfectly fine for an embedded computer, which is where systems like SeL4 are used.

On the other hand, I cannot see how this approach can be scaled to something like a personal computer.

For some programs that I run, e.g. for an Internet browser, I may want to not authorize them to access anything on SSDs/HDDs, except for a handful of configuration files and for any files they may create in some cache directories.

For other programs that I run, I may want to let them access most or all files in certain file systems. Any file system that I use contains typically many millions of files.

Therefore it is obvious that using one capability per file is not acceptable. Moreover, such programs may need to access immediately many thousands of files that have been just created by some other program that has run before them, for instance a linker running after a compiler.

Assuming that a pure capability-based system is used, not some hybrid between ACLs and capabilities, there must be some more general kinds of capabilities, that would grant access simultaneously to a great number of resources, based on some rules, e.g. all files in some directory can be read, all files with a certain extension or some other kind of name pattern from some directory may be written or deleted or renamed, and so on.

> On the other hand, I cannot see how this approach can be scaled to something like a personal computer.

Personally I think the biggest challenge is UX. The systems engineering is good, and it works just fine.

> For other programs that I run, I may want to let them access most or all files in certain file systems. Any file system that I use contains typically many millions of files. Therefore it is obvious that using one capability per file is not acceptable.

Yeah, of course! Just make a capability representing the containing directory or filesystem. Then the program is free to open and browse files within that directory, but nothing outside of it.

I agree with others in this thread. Think of the capability like a bearer token. You wouldn't make a token per file. Just make one for the directory.

Then make a userspace server to do that. If you want to see how this works in practice, macOS and iOS are great “pragmatic” implementations of this pattern. They use a Mach/BSD hybrid
you're absolutely right. this is just a terminology confusion I think. we can talk about capabilities as 'a replacement for ACLs', in which case, yes we need to think about policy rules and not a gigantic list of possible atoms.

from a mechanism point of view a 'capability' is really more a bearer token, the result of a policy decision, a credential that we can give to the OS to show that we have been given access without going through the rules-based machine for every operation.

>Its capabilities all the way down.

IIUC one problem with such layering of capability processing is that each passed layer results in a context switch (i.e. switch of memory mappings, thrashing of caches, etc.) and its on top of the cost of passing through the kernel. In other words, you may need to pay cost of N syscalls for one multi-layered capability-based operation.

True, but capability calls in SeL4 are supposedly faster than linux syscalls. Because caps are such an important primitive, they're extremely heavily optimised.

As an example, when you invoke a capability, your process hands the callee your scheduler time-slice. So its not like linux where your process yields to the scheduler. The same CPU core will handle the entire call -> process -> return computation pipeline between multiple processes.

I'm not sure how fast it ends up in practice compared to a similar system built on top of linux. I suspect a lot of the difference would come down to implementation choices. And if its still not fast enough, you can always just set up a ring buffer or something between processes to share data directly.

Doesn't that mean that your process is then responsible for ensuring that an app with a read-only capability cannot do a write ?

You're moving the burden of enforcement from the kernel to the user level ?

Yes, microkernels like SeL4 do almost all real work out of the kernel, and in userland processes. It’s much more secure that way.
Covert channels are a thing. Shared access to resources always opens the possibility of covert information passing through e.g. modulation of resource usage. This isn’t even out-of-band, it’s just a hard fact that a shared resource always creates a potential covert channel (source: Lampson 1972, A Note on the Confinement Problem).
How I had idea of a computer and operating system design, measuring time also requires a capability. Creating files (including temporary files) also requires capabilities. Shared memory is read-only by everyone; to be able to write to memory you must have exclusive access. All of these capabilities are not necessarily what the program using them intended them to be; they may also be proxies, or capabilities of the wrong type (the kernel does not know anything about the types of capabilities except those it created itself), etc. A proxy may limit communication from one program to a service. Using these as well as other things (including, but not limited to, the CPU design), there are things that can be done to mitigate these problems (including things necessary to mitigate other kind of timing attacks based on other capabilities, e.g. slowing down network access for purpose of testing its working on slow networks).
They mention a compiler having access to a file called BILL for storing billing information and if you specify that it is the file for debugging then it is overwritten by the debugging information. While an appropriate kind of capability system (such as proxy capabilities, or object-capabilities described in that article which is very similar) can help, locking the file might also help (if it is locked for billing first before any files specified by the user are locked); then the compiler will complain that the file specified as the debugging output file cannot be written because it is locked (even though the compiler is the one that locked that file). A capability system is better, although it would be possible to do both, since locks (and transactions as well) are also helpful for other purposes.
We kind of have the taste of what capability-based OS would look like in form of a web browser: you can open a web page with a potentially-malicious code and it doesn't have access to any of your files or sensitive data unless you explicitly allow it to.

We also have it on mobile operating systems, although some things are a rather coarse-grained.

On desktop there's just a lot of inertia. Everyone switching to a new thing is kind of impossible, and some simple add-on to existing systems would look like containers/docker.

I think capability-oriented programming languages might actually be an easier way to switch to that model, as it's much easier to adopt a new application than a new OS. E.g. with language-level capabilities (ocaps) you can implement a safe plugin system. That's pretty much non-existent now and is quite relevant - e.g. getting pwned via an IDE plugin is the reality.

So maybe a "new Emacs" can be a way to get people to adopt capabilities beyond what we already have in the browser/cloud/etc. - IDE written in a new programming stack which is inherently secure to the point of running potentially-unsafe plugins.

None of those things become obsolete with capabilities.

You still need code signing because users need to be able to grant privileges in a way that sticks across upgrades. The object they want to privilege isn't a set of files on disk but a logical app as defined by (more or less) a brand name+app name even as it changes over time.

You still need antivirus software because users can be tricked into giving capabilities to programs that appear legit but are actually malicious.

Modern operating systems (iOS, Android) are capability oriented operating systems to the extent that makes sense. For some reason there's a meme that says capabilities are a magic wand that solves all security problems, but it's not the case.

Yeah not least of which because statically defined capabilities struggle when you have dynamic needs. Imagine you have S3 buckets. If your buckets are partitioned by application, that’s easy to protect with capabilities. Now what if you have an application that’s dynamically assigning buckets by tenant. You can’t statically assign that and you can’t even restrict yourself to buckets you created in the first place because you need a meta system to keep track of which buckets were created by which application but it’s doable (eg store data within the bucket indicating which app). But now you’ve got delegation challenges if you have two applications that need access to overlapping resources. There’s no consistent design solution. Everything is a special case to figure out.
> it sounds like the only architecture suitable for the internet age, where you can download and run anything from anywhere

Wasn’t that the reason why Microsoft went allout against Java? Write once, run anywhere. JVM was a “trojan horse” and theoretically could have dominated the world.

I didn't mean it in the Java way. I meant that whatever operating system you're on, you can download random programs from the internet (compiled specifically for your OS or portable) and run it on your machine. It doesn't matter what they're written in or how they're run, it's possible on any OS connected to the internet and an OS with capabilities as first class citizens would isolate any program by default, denying it access to anything by default and severely limiting program's ability to cause harm, intentionally or unintentionally.
Why do signatures/hashes/app-store verification become obsolete with a capability-based system?

If a binary has the capability to withdraw money from my account, I don't want that capability given to just any binary.

In case of updating the binary, yes, you generally want to make sure it comes from the same source and therefore cannot do damage to things it already has access to. But when you install a new program, it shouldn't have access to any resources other than the ones it creates itself, so there's no need to sign it. Further more, when installing a new program, you still have to download/import the pubkey to verify the signature from somewhere, so it's almost meaningless on the first installation. Signatures wouldn't be obsolete, but they also wouldn't be the only line of defense. Furthermore, updating can now be performed by the program itself and the program might already contain the pubkey needed to check the validity of updates.
I'll insert my standard plug for Genode/Sculpt OS here... Capability based, and used/maintained commercially:

https://genode.org/

I also like HarmonyOS, the most advanced secure OS nowadays. If they just would have fixed deadlocks also.
Never heard of HarmonyOS before - looking at the website, it doesn't seem to mention being capability based, but it is 'distributed' ??
The Market has spoken, and people use standard consumer CPU/GPU-bodge architecture in cloud data centers. Sure there are a few quality of life features different from budget retail products, but we abandoned what Sun solved with a simple encrypted mmu decades ago.

The paper adds little to TCSEC/"Orange Book"/FOLDOC publications. Yet the poster doesn't deserve all the negative karma.

On a consumer CPU/GPU/NPU, software just isn't going to be enough to fix legacy design defects. Have a great day. =3

in larger systems the utility of sharing a single cpu/gpu complex between independent authorization domains kind of goes away. if you have 10,000 units of allocation, it never makes sense to try to share one of those until you have more than 10,000 jobs, and even then.

so it seems a lot more feasible to control access and sharing between those units and write of off the intranode case as a lost cause

In such arrangements, one has essentially enforced high-latency similar context isolation using encrypted/VLAN network fabric, and pushed coordination/permissions into back-plane supervisory subsystems. Still creating a monolithic permission domain vulnerability within the entire n<10000 node cluster partition.

Likely doesn't help OS users either way. Best regards =3

you kinda missed my point. already in the cluster the important filesystem is the distributed one. the important job management system is the distributed one. the local OS just effectively supports the single process that we really care about. so the distributed context is where we add capabilities and actually manage access and resources. that is the real OS.
I think many people have had similar ideas, including I also had ideas about how to design computer and operating system, which can use proxy capabilities. (There are different kind of capability systems, and I think that proxy capabilities has more benefits than only security.)

There are still considerations when designing parts of the system to be secure, while also making them have the functions that are desired (although a proxy capability system can be used to add arbitrary further restrictions if needed), but the core system can use proxy capabilities as the core security system.

Hashes would still be useful, but that is if you want to check that the package is the one that you intended; it does not prevent you from installing or writing whatever program you want to do, nor to make the program secure, which would be done by separate mechanisms; however, knowing that the package is the one that you intended can be one of the steps of the security, but not the main one.

However, security is not the only issue in a computer and operating system design, although it is a significant issue.

In addition to capabilities, which implemented the principle of least privilege (and keep untrusted code sandboxed by default) there is a need for binary verification.

A check that a whatever is downloaded cannot exceed it's capabilities.

Part of the challenge is that hardware tried and has failed to be trustworthy in implementing security boundaries. The failure appears to be because a misalignment of incentives.

I think the premise of a capability based operating system can help a lot, but for something to work in the long term the incentives need to aligned.

binary verification. A check that a whatever is downloaded cannot exceed it's capabilities.

That's already handled by the sandbox.

Your point of view has an insidious lie at its core; that the user perfectly knows what she wants. That if we only give the user the ability to set capabilities, we will not need any other protection for her.

The reality is that we're water meatballs, we're so easy to fool, and we need the cold calculating power of code to protect us from ourselves.

It looks like you you may be interested in Qubes OS, security oriented operating system relying on strong, hardware-assisted virtualization: https://qubes-os.org. My daily driver, can't recommend it enough.
I know about it, but I'm not interested in QubeOS approach. It's VMs all the way down, while what I'm talking about is no VMs and capabilities as first class citizens and no vurtualization.
I am also surprised that capabilities weren't more widely implemented after mobile OSes demonstrated they are practical. I know Windows made a move in that direction with UAC but had to soften it due to user alert fatigue. So I guess having no legacy apps and a centralized repository helps.

I've recently been looking into Guix SD as a solution. Its package management is designed to keep programs independent of each other, so containers are cheap and lightweight. Trying out untrusted software is as easy as `guix shell --container --pure --no-cwd [program]`, which blocks access to the network, file system, and environment variables. Right now I'm adding more advanced capability management: limits on CPU, memory, storage space, network use, etc.

I use nix + bwrap, which gives a similar result. it works well enough, though I really ought to restrict reads to only the closure.
> I use nix + bwrap

In an automated way, or have implemented as hand-written wrappers? And regardless, have you published the code (and/or talked about how it works) anywhere? It'd be really nice to have a gentler onramp to sandboxing things, and nix should be well-placed for it.

an automated way, as part of a tree-based harness. I haven't published the code yet but should hopefully be able to soon!
What is wrong about virtualization? It allows to run all existing software, it doesn't restrict the owner of the device, it is extremely flexible and reliable. And it can be fast, too.
see other comment, the author describes some issues with current hardware virtualization. kvm is also pretty good, but not perfect... and completely irrelevant with GPU pass-through enabled. =3
Which other approach to security do you consider reliable? Through correctness? Through obscurity?

https://blog.invisiblethings.org/2008/09/02/three-approaches...

Publicly documented encrypted mmu, as it is the only practical way to isolate contexts on parallel cores.

Or some exotic processor no one would ever sell successfully. =3

Qubes OS was also shown to have inherent hardware virtualization sandbox vulnerabilities described by Joanna Rutkowska in an interesting lecture.

There is likely a PoC around someplace if people dig a bit. =3

Are talking about this? https://en.wikipedia.org/wiki/Blue_Pill_(software)

It happened in 2006 and never happened after that. I would consider it as secure as it gets.

Sorry, can't recall the exact lecture... It was only interesting as I was looking at a toy project to see if metastability issues were solvable. Practically speaking, it only proved the folks at Sun were very smart people choosing an encrypted mmu. =3
OS design basically stagnated in the 90s. Sure, we had NT, but that was putting a dos flavoured suit on VMS. BeOS was promising, but fizzled out quickly. Everything else has either been research or for the embedded market.
Android and iOS increased security, but at the cost of much flexibility and user agency. It's some kind of progress, but I certainly wouldn't want them for Real Computers.
Android is just Linux running a Java VM and a funky userland. iOS is just MacOS apple decided you can't do real work on. Both are still unix clones (as contrast to a unix-like like plan9 or haiku)
The kernel is obviously close to vanilla Linux, but one could also define the OS as "which lower-level services and interfaces the applications see and are programmed to use" - so the stuff on top of Linux matters.

There are more interesting, general, and lower level innovations out there - sure. But the mobile OSes do have improved security from some kind of permission system, VM (only partially because native components are common), one user account per app (IIRC) and such.

Right but what I'm trying to say is that the core architecture hasn't really changed since the 90s. All the improvements you mentioned are non-applicable to my statements. One user account per app is just a hacky way to use unixes timesharing origins for increased security, VMs and containers slap a bandaid on the problems, and permission systems like android and iOS have are just a hack that works nicely since they use sandboxed software. Plus, these are all security based, which while the topic of the discussion overall, wasn't what I meant when it came to OS innovation in general (an important part, though)
One word: Qubes.
Qubes for sure not. Xen seperation on top of Linux sounds nice, but there's still this huge, insecure monolith below. Genode, Harmony or Fuchsia sound much better. And now with a secure language for the surface and drivers it would be even better.

But even better no OS, and no attack surface. Only what you need, and properly isolated.

The problem with any secure system is that they're not usable systems. Real applications and users expect to access anything from anywhere. That's the opposite of security.
One of my friends had his credentials stolen from a trojan infostealer masquerading as a video game, sent from a rando who he mistakenly trusted. If only it had to request user permission to access files outside of its folder. There's a spectrum between full access and full lockdown.
If every app requests that permission, no app requests that permission. Also your passwords would be in your user folder so the app that needs the passwords could read them.
That condition usually doesn't hold in practice. Very few programs have a reason for reading browser history or cookies. Excel has no purpose accessing the Notepad++ appdata folder. Not all-or-nothing.
How would your browser read browser history and cookies? It gets its own app data folder? What if I want to export my browser history to another browser - which is currently impossible on Chrome for Android, precisely because no other app is allowed to access Chrome's history file?
I imagine an OS where the system remembers to keep permanent permission for a program to manage its own files. An app data folder would work. The system should pass the capability on program start.

I also imagine a system where graphical programs must call a trusted system file picker to receive a fd. Receiving the capability grants permission. Ideally, Chrome could export browser history to a file, but we live in a fallen world. In any case, an alternative browser must request access through the system file picker, selecting an exported file or selecting the Chrome app data folder. It trades automatic import with user selection. The user has ultimate power, and programs make noise when doing such requests.

Please forgive me that I don't know Android system architecture. Searching tells me something about the Storage Access Framework, but I don't know if that truly meets what I describe.