zlacker

[parent] [thread] 7 comments
1. zeotro+(OP)[view] [source] 2022-01-20 16:03:26
On most current archs:

> Any piece of code running in a process can construct an integer value and, if this integer corresponds to a valid location in the process’ address space, then it can access memory at that location.

What this adds:

> CHERI changes this. Every load or store instruction and every instruction fetch must be authorized by an architectural capability.

So it should be possibly to call into any function (e.g. from an untrusted blob, and given the capabilities are set up) and on return have the guarantee that none of the callers memory has been touched and all the side effects are contained in the return value, and maybe selected whitelisted addresses?

I remember the mill architecture[1] also claims to have that capability, I think they called these calls "Portals". Btw the talks by Ivan Godard are a must watch if you have any interest in hardware architecture.

But how can existing code be just a recompile away from benefiting from these features, don't the capabilities have to be set up somehow (unless it is purely functional language)?

1: https://millcomputing.com/docs/

replies(3): >>jrtc27+n3 >>ameliu+DA >>Findec+sg1
2. jrtc27+n3[view] [source] 2022-01-20 16:16:41
>>zeotro+(OP)
The C startup code (for statically-linked binaries) and run-time linker (for dynamically-linked binaries) carve up initial capabilities provided by the kernel into capabilities that cover the various global variables and function pointers needed by the program and libraries, similar to how pointers are initialised for position-independent code (more complex, but same principle, just scan through all the relocations and apply them). When you mmap(2) memory from the OS, you get back a capability with bounds covering that memory. When you malloc(3) memory from your libc, it finds space in an existing mapping, takes that capability and restricts its bounds to the allocation size. When you take a pointer to a stack-allocated variable, the compiler inserts an instruction to set the bounds of that capability to just the memory it allocated for that variable. Every pointer, whether "language-level" (what is exposed in the language) or "sub-language-level" (the pointers in the implementation, like return addresses on the stack or the stack pointer itself), is a capability, and all you need to do is insert a bounds-setting instruction at the point of allocation to restrict its bounds. So your libc's malloc needs modifying, as does your kernel, but your C program that calls them just needs to be recompiled for the pure-capability ABI.

Edit: To answer the first question, yes, that is the primitive which enables CHERI to be used for in-address-space compartmentalisation rather than relying on an MMU for process-based separation and all the overheads that come from context switching address spaces.

replies(1): >>zeotro+aO
3. ameliu+DA[view] [source] 2022-01-20 18:46:54
>>zeotro+(OP)
> Every load or store instruction and every instruction fetch must be authorized by an architectural capability.

This sounds great. But on the other hand ... Yikes! What if this tech falls into the hands of a big corporation and some manager needs a raise?

replies(2): >>als0+FB >>jrtc27+4C
◧◩
4. als0+FB[view] [source] [discussion] 2022-01-20 18:51:36
>>ameliu+DA
This isn’t a digital signature or anything like that. Instead, it’s increasing the size of pointers to include bounds and permission rights (R/W/X/etc), plus an extra tag stored somewhere else to prevent forgeries or mitigate corruption. So the only thing big corp gets out of this is software more resilient to memory corruption :-)
◧◩
5. jrtc27+4C[view] [source] [discussion] 2022-01-20 18:53:26
>>ameliu+DA
Then great, they push for the adoption of the technology and the world's computers become more secure. Arm's a big corporation and they're obviously pretty involved, and both Microsoft and Google are invested in the project.
◧◩
6. zeotro+aO[view] [source] [discussion] 2022-01-20 19:44:44
>>jrtc27+n3
Thanks for the explanation!

So these bounds are set by the software (and are guarded against manipulation). Then each read or write to memory is checked against these bounds by the "fine grained MMU" hardware.

replies(1): >>jrtc27+JT
◧◩◪
7. jrtc27+JT[view] [source] [discussion] 2022-01-20 20:12:30
>>zeotro+aO
Yes, though "software" is rather broad; where exactly the bounds setting happens is important as if you get it wrong it allows malicious software to not set bounds and be able to access memory outside of its allocations. Pushing it to the same place the actual allocation happens or, in the case of referencing global variables, the same place the loading and relocating happens, ensures that the only thing malicious software can do by not setting bounds is make itself insecure.
8. Findec+sg1[view] [source] 2022-01-20 21:40:38
>>zeotro+(OP)
> I remember the mill architecture[1] also claims to have that capability, I think they called these calls "Portals".

The Mill's "Portals" are more intended for inter-process calls than for compartmentalisation within a process. The Mill can have fine-grained protection of memory temporarily given to a callee because protection ranges are separate from paging. I believe there have been several research OS:es that did pass full pages back and forth over IPC, but at the cost of setting these up and restricting them to that purpose.

Because Portals are so cheap they could probably be useful for compartmentalising larger complex applications such as a modern-day web browser into smaller chunks than today.

BTW, Goddard has told that the Mill team once considered a capability-oriented architecture but they chose not to because the model they had in mind broke C's pointer semantics in some way. They have chosen to prioritise compliance with the C spec, so as to be able to market the architecture.

[go to top]