Not supporting type aliasing will break huge amounts of C and C++ code and will break a disproportionately high amount of TCB code. Language runtimes, kernels, and low-level data structures are the most likely code to use those.
We put a lot of effort into making this work well with CHERI because feedback from multiple vendors was that it was a deal breaker if it doesn’t work.
As a long-time systems programmer, I think you’re overstating the problem with type aliasing. Maybe your vendors are just trying to come up with excuses not to use CHERI!
CHERI is in a different boat because it has to have a story for language runtimes. It’s not clear that Fil-C has to. If you wanted to call between Fil-C and C# then you wouldn’t compile the C# runtime with Fil-C. Ditto for Java/Rust/JavaScript/etc.
I’ve already been able to convert enough code to Fil-C that I don’t think type aliasing is that big of a problem. The typical story is one of these:
Unions between types Fil-C considers “int”, like char/short/int/long/float/double/etc. Those just work, no problem. Fil-C even gives those strong semantics. Fil-C will never do the UBsan thing of trapping on “active union member” misuse.
Unions between types Fil-C considers “ptr”, like T* for any T. Those just work, no problem.
Unions that have a ptr member for no good reason. Lots of those! OpenSSL uses one to create an internal “aligned word” type. You can just remove the ptr from those unions.
Unions that are very small, like just one ptr and one int. Making those into structs is not a big deal. Also, a T* in Fil-C can “box” an int via int-to-ptr and ptr-to-int casts. So, if you really care about memory, you can replace the union with just a ptr member.
Unions that are large and have both ptrs and ints, but roughly the same number of ptrs in each union member. Making sure that all of the struct members of the union place their ptrs at the same offsets solves the problem.
I’ve yet to encounter a union I can’t fix.
Two other thoughts:
I don’t think that the CHERI approach is all that safe. Because it doesn’t prevent type aliasing, it also doesn’t prevent you from writing a custom allocator that vends übercapabilities. For example, did y’all do anything to OpenSSL’s super funny “secure” malloc?
it totally makes sense for you to use this as a selling point of CHERI relative to Fil-C, since it does mean that (a) you can run more stuff out of the box and (b) it’s neat that you can probably even run a JIT on top of CHERI.
But if we’re being honest, this is just a hard trade-off between security and compatibility. Fil-C is safer, CHERI is more compatible.
As a long-time systems programmer, I think you’re overstating the problem with type aliasing. Maybe your vendors are just trying to come up with excuses not to use CHERI!
We have compiled a lot of C and C++ codebases. Without supporting type aliasing, very few would work.
CHERI is in a different boat because it has to have a story for language runtimes.
It aims to support all TCB code, because if you don’t have memory safety in your TCB then you don’t have it anywhere.
I’ve yet to encounter a union I can’t fix.
Does that include implicit unions? For example, many consumers of uthash store integers in the void* value slots. A lot of generic data structures in C expose void* or uintptr_t entries and have consumers that store pointers, data, or both.
We have surveyed a lot of exciting C idioms and we support almost all of them. Dismissing things that we’ve seen thousands of times across diverse codebases as not important does not give me any confidence in your approach or your evaluation methodology.
Final thought: I don’t think that the CHERI approach is all that safe. Because it doesn’t prevent type aliasing, it also doesn’t prevent you from writing a custom allocator that vends übercapabilities. For example, did y’all do anything to OpenSSL’s super funny “secure” malloc?
Yes, we set bounds on allocation. The port of OpenSSL was subject to a DARPA-funded red teaming exercise where they reintroduced bugs such as Heartbleed and had professional exploit writers try to break it. They were not able to.
Your custom allocator gets memory from somewhere, whether that’s mmap, malloc, or something a layer down. The thing above will apply bounds, so bugs in nested allocators can affect their consumers but not anything else.
Does that include implicit unions? For example, many consumers of uthash store integers in the void* value slots. A lot of generic data structures in C expose void* or uintptr_t entries and have consumers that store pointers, data, or both.
Because int-to-ptr casts work, that code would work in Fil-C so long as the store operation knows it’s storing to a ptr.
We have surveyed a lot of exciting C idioms and we support almost all of them. Dismissing things that we’ve seen thousands of times across diverse codebases as not important does not give me any confidence in your approach or your evaluation methodology.
Folks will have these options:
buy special hardware (i.e. CHERI)
change a couple unions and about half of your malloc calls to use Fil-C
rewrite everything in Rust or another memory-safe language.
Nobody wants (1).
That leaves (2) and (3), and it’s definitely less work to replace a couple unions and half your mallocs than it is to rewrite in Rust.
Therefore: it’s true that you CHERI folks totally have to survey lots of idioms and work hard to make all of them Just Work. You probably have to do that and then some, because you’re trying to convince people to buy hardware that they dont want.
On the other hand, I just have to give folks an option that is easier than rewriting everything in Rust. So, I don’t have to support all the idioms.
Because int-to-ptr casts work, that code would work in Fil-C so long as the store operation knows it’s storing to a ptr.
I see. So you can support unions as long as the compiler transforms the stores into stores of pointer types. This still breaks things like C++’s std::string, which is typically implemented as a union of a pointer to a heap allocation or a
Nobody wants (1).
That’s incredibly dismissive and simply not true. What people do want is high confidence that:
Using a new memory-safe target is actually going to fix their problems,
Adopting a new memory-safe target can be done with low cost.
The performance overhead of doing this is acceptable.
The first we have shown with formal proofs of ISA properties and (to most people, ore convincingly) with red teaming exercises where experienced exploit writers try to break CHERI. The one we ran at BlueHat was not very successful because all of the attendees decided that attacking CHERI was too hard and went after the easier targets, but the ones run by DARPA have been more successful (people actually trying and failing). This was a sticking point early on, but we now have sufficient evidence that it’s accepted.
We have been demonstrating the second by porting large software stacks to Morello. This includes the entire FreeBSD userland and kernel (Linux is not quite finished), the userspace graphics stack, Weston (including 3D acceleration), the Qt and KDE core libraries, and a bunch of applications (Chromium is nearly finished). This is now at the point where people are starting to believe our claims about compatibility.
Getting good performance numbers was hard, but Morello is a sufficiently realistic implementation that the folks at Arm have been able to isolate the root causes of overhead, most of which are artefacts of incrementally tweaking the Neoverse N1 and not building in CHERI from scratch, or from not doing the normal optimisation phase in a final design because of the short timelines. They now estimate that a production-quality version of Morello would have a 2% overhead in CHERI mode relative to the AArch64 baseline. This number is important because, as the Eternal War in Memory SoK reported, security features with an overhead of over 10% are almost never deployed and ones with an overhead of over 5% are almost never deployed across entire stacks (things in the 5-10% range are used in specially targeted places).
So, to summarise, we needed to be able to show that adopting CHERI really fixes problems in the presence of an intelligent adaptive adversary who can attack any part of the stack, that it’s possible to port enormous codebases in a wide variety of coding styles to the new target, and that the overhead is under 5% for typical workloads. You will need to convince people of the same things.
Note: The biggest adoption barrier for CHERI is not the new hardware (people roll out new hardware over time and incompatible hardware ages out naturally), it’s the new ABI. Supporting two incompatible ABIs comes with a huge cost for platform vendors. The cost of shipping two versions of every shared library is sufficiently high that OS vendors want to know that it means that they have a path to treating entire categories of bug as low priority crash fixes and not urgent security updates to even consider it. If you had 100% source compatibility, then the new ABI is less important in some markets because people can recompile everything at once, but if you don’t then supporting both for years is essential and that comes with a high cost.
change a couple unions and about half of your malloc calls to use Fil-C
With your small corpus of code tested, I remain unconvinced by this. There are so many corner cases that show up in C and C++ (most desktop software and a lot of server software uses at least these two languages and requires compatible ABIs) that you have not discussed that it feels very much like where we were ten years ago.
I see. So you can support unions as long as the compiler transforms the stores into stores of pointer types. This still breaks things like C++’s std::string, which is typically implemented as a union of a pointer to a heap allocation or a
No compiler transform. You have to change your code.
I’ve changed some code to do this, it’s not terrible.
That’s incredibly dismissive and simply not true. What people do want is high confidence that:
I’m being honest with you. I appreciate your honesty, too.
I think CHERI has already achieved amazing technical goals and yet it’s not adopted yet. I think asking folks to use new hardware is insanely hard.
Note: The biggest adoption barrier for CHERI is not the new hardware (people roll out new hardware over time and incompatible hardware ages out naturally), it’s the new ABI.
ABI is just one problem. You’re right that it can be a huge problem. That said, Apple has a ptrauth ABI slice that is totally separate from what civilians on Apple platforms get, and ptrauth has nowhere near the value of CHERI. So ABI can’t be the only problem and it’s certainly not the biggest.
I can think of the following additional problems:
Folks want to invest in solutions that will work on a lot of different CPUs, since modern HW is more than just one CPU. There’s the CPU everyone thinks about and then N other, smaller CPUs to do auxiliary tasks that are often super sensitive.
NIH syndrome. You’re not just asking folks to adopt your new HW, you’re asking them to accept some humility. The psychology of it is hard. (That’s also true with programming languages, but less so, because language adoption rarely depends on any vendor, and the cost of using a new language for something is far lower than the cost of adopting new HW.)
Opportunity cost versus somehow fixing the problem in software, in a Rust-like way, so that pointers can still be thin.
A problem that Fil-C also has: the UaF story is kinda meh. Both Fil-C and CHERI preserve the integrity of their capabilities. But I think there’s likely to be a class of scary security vulnerabilities that are UaF-based, do not require memory corruption, don’t require busting any capabilities, but just rely on seeing memory you shouldn’t be able to see due to UaF. CHERI docs seem aware of this problem, with all that talk of GC. I think that scares people even more! It means that the cost you pay for CHERI buys you something that is safer, but with that shady caveat about UaF. Fil-C has this shady caveat, but implementing a high-speed barrier-based concurrent GC in Fil-C is much easier than doing it in CHERI. (As of yesterday, Fil-C even has safepoint hooks.)
With your small corpus of code tested, I remain unconvinced by this. There are so many corner cases that show up in C and C++ (most desktop software and a lot of server software uses at least these two languages and requires compatible ABIs) that you have not discussed that it feels very much like where we were ten years ago.
I’m not surprised that you’re unconvinced. Time will tell.
Because int-to-ptr casts work, that code would work in Fil-C so long as the store operation knows it’s storing to a ptr.
If I understand your comments elsewhere correctly, this would stop working as soon as the user tried to store something of another type there? Like storing a pointer, and then replacing it with an int, and then replacing the int with a pointer, and so one.
Also, I just don’t understand your claim that Fil-C is safer. If the user can choose to do things that makes CHERI less useful, surely they can just choose to not use Fil-C? If “can’t make your own alloc” is acceptable, that’s very easy to enforce on a CHERI system. All of Fil-C’s properties depend on user choice and implementation. And if you pre-suppose good user choices and implementations, then CHERI does not have the drawbacks that you claim. CHERI also has something Fil-C does not. Relevant quote:
If you wanted to call between Fil-C and C# then you wouldn’t compile the C# runtime with Fil-C. Ditto for Java/Rust/JavaScript/etc.
This means that Fil-C must simply trust that everything else in the same address space is not doing anything bad. That every Rust unsafe is definitely correct, that the JS implementation has no bugs that would allow writes elsewhere on the heap. CHERI on the other hand can allow safe single address space sharing that is also totally safe.
You probably have to do that and then some, because you’re trying to convince people to buy hardware that they dont want.
I’m pretty sure they want all vendors, such as Arm, Apple, Intel, and so on, to add CHERI to their products. So there would be no need to convince people to buy special hardware.
If I understand your comments elsewhere correctly, this would stop working as soon as the user tried to store something of another type there? Like storing a pointer, and then replacing it with an int, and then replacing the int with a pointer, and so one.
I should not have been writing comments at 1 AM, because with not tired eyes I see that you were obviously talking about boxing integers in pointers, apologies.
Also, I just don’t understand your claim that Fil-C is safer. If the user can choose to do things that makes CHERI less useful, surely they can just choose to not use Fil-C? If “can’t make your own alloc” is acceptable, that’s very easy to enforce on a CHERI system. All of Fil-C’s properties depend on user choice and implementation. And if you pre-suppose good user choices and implementations, then CHERI does not have the drawbacks that you claim. CHERI also has something Fil-C does not. Relevant quote:
If you try to compile an allocator that isn’t safely implemented on CHERI, it’ll just work - you might not even notice.
If you try to do it with Fil-C, you get a runtime error.
That’s what memory safe languages are all about - giving you feedback when you did something unsafe.
This means that Fil-C must simply trust that everything else in the same address space is not doing anything bad. That every Rust unsafe is definitely correct, that the JS implementation has no bugs that would allow writes elsewhere on the heap. CHERI on the other hand can allow safe single address space sharing that is also totally safe.
That’s true! CHERI means you don’t need a type safe language to get some kind of safety.
But you need special hardware, which makes it kinda impractical.
I’m pretty sure they want all vendors, such as Arm, Apple, Intel, and so on, to add CHERI to their products. So there would be no need to convince people to buy special hardware.
Right! That’s the key difference between CHERI and Fil-C:
For CHERI to be successful, the CHERI folks have to convince at least one vendor to ship it, and that vendor had better do a good job of it or it’ll taint the whole project. Yeah, ideally they’d convince all the vendors.
For Fil-C to be successful, I just have to either (a) convince one person to make a useful thing on top of Fil-C or (b) port some useful thing to Fil-C, where that thing becomes much more useful with the added security and not much less useful due to the perf overhead. So, the adoption bar is inherently lower. Sure, it means code changes that CHERI won’t require.
I wrote this last but decided to put it up front rather than at the end: despite all the arguing, I do find Fil-C to be pretty cool, and I hope you’re successful with it! I’m just skeptical, and disagree with your thoughts on CHERI.
If you try to compile an allocator that isn’t safely implemented on CHERI, it’ll just work - you might not even notice.
If you try to do it with Fil-C, you get a runtime error.
Nothing prevents you from enforcing something similar under CHERI. Fil-C has a single blessed alloc, and prevents other allocs from being implemented. This presumably also means blocking all system calls that might be used to implement an alloc. Ultimately, my point is that since Fil-C is software, and CHERI simply adds additional HW features, anything you can do with Fil-C, you can definitely do with CHERI. And any time you figure out how to leverage CHERI to do one of the things you want to do, the hardware is now helping enforce it.
CHERIoT has a shared heap between all compartments, and a single allocator. It enforces quotas, and gives you full spatial and temporal memory safety. As someone in the embedded space, I find it incredibly compelling.
That’s true! CHERI means you don’t need a type safe language to get some kind of safety.
But you need special hardware, which makes it kinda impractical.
But it also means that Fil-C isn’t memory safe in practice, since any Fil-C code will surely often interact with code that isn’t Fil-C. That’s one thing I like about CHERI; memory safe languages may be safe within their own specified semantics, but that falls apart as soon as any external factor may violate the invariants of the safe language. With CHERI, you can know that your nice and safe OCaml program won’t be screwed up by some C++ library you needed to use. Like, I’m a huge Rust fan, but I got to admit, CHERI plugs a big hole that currently affects all safe or semi-safe programming languages.
And idk how practical it would be to replace all not safe code. Fil-C helps a lot with some of it, but far from all.
Right! That’s the key difference between CHERI and Fil-C:
For CHERI to be successful, the CHERI folks have to convince at least one vendor to ship it, and that vendor had better do a good job of it or it’ll taint the whole project. Yeah, ideally they’d convince all the vendors.
For Fil-C to be successful, I just have to either (a) convince one person to make a useful thing on top of Fil-C or (b) port some useful thing to Fil-C, where that thing becomes much more useful with the added security and not much less useful due to the perf overhead. So, the adoption bar is inherently lower. Sure, it means code changes that CHERI won’t require.
Note that CHERI is shipping later this year in a microcontroller. And if I recall correctly, at least one other microcontroller vendor is also designing a CHERI chip. So it’ll be able to prove itself in the real world soon. Of course, there are a lot of differences between the world of embedded and the PC / server space. If the standard is “make one useful thing on top of”, then I think CHERI will succeed :)
You may be surprised, but CHERI already has a decent amount of momentum. Couple hundred million invested, a good number of companies, mostly in industries where safety is important, doing CHERI things. A few governments even recommend CHERI already, even though no hardware is generally available yet.
Also, I’m curious, you’ve mentioned that you’ve found it to be pretty easy to port C projects, but what about all the code that isn’t in C? There’s tons of C++ and Rust out there, which CHERI would make safe, but as far as I can tell, Fil-C would not. So regardless of all else we’ve talked about, CHERI casts a much broader net. Personally, I’d prefer to never write C ever again, even if it was to be made entirely safe. So in any situation where I need a “low level” language, it’d probably be Rust, possibly with some unsafe {}. So from my point of view, while both CHERI and Fil-C promise to help with (some of) the code we already have, only CHERI promises to help with new stuff.
Not supporting type aliasing will break huge amounts of C and C++ code and will break a disproportionately high amount of TCB code. Language runtimes, kernels, and low-level data structures are the most likely code to use those.
We put a lot of effort into making this work well with CHERI because feedback from multiple vendors was that it was a deal breaker if it doesn’t work.
score 5 · canonical
Good thing I don’t answer to any vendors. ;-)
As a long-time systems programmer, I think you’re overstating the problem with type aliasing. Maybe your vendors are just trying to come up with excuses not to use CHERI!
CHERI is in a different boat because it has to have a story for language runtimes. It’s not clear that Fil-C has to. If you wanted to call between Fil-C and C# then you wouldn’t compile the C# runtime with Fil-C. Ditto for Java/Rust/JavaScript/etc.
I’ve already been able to convert enough code to Fil-C that I don’t think type aliasing is that big of a problem. The typical story is one of these:
Unions between types Fil-C considers “int”, like char/short/int/long/float/double/etc. Those just work, no problem. Fil-C even gives those strong semantics. Fil-C will never do the UBsan thing of trapping on “active union member” misuse.
Unions between types Fil-C considers “ptr”, like T* for any T. Those just work, no problem.
Unions that have a ptr member for no good reason. Lots of those! OpenSSL uses one to create an internal “aligned word” type. You can just remove the ptr from those unions.
Unions that are very small, like just one ptr and one int. Making those into structs is not a big deal. Also, a T* in Fil-C can “box” an int via int-to-ptr and ptr-to-int casts. So, if you really care about memory, you can replace the union with just a ptr member.
Unions that are large and have both ptrs and ints, but roughly the same number of ptrs in each union member. Making sure that all of the struct members of the union place their ptrs at the same offsets solves the problem.
I’ve yet to encounter a union I can’t fix.
Two other thoughts:
I don’t think that the CHERI approach is all that safe. Because it doesn’t prevent type aliasing, it also doesn’t prevent you from writing a custom allocator that vends übercapabilities. For example, did y’all do anything to OpenSSL’s super funny “secure” malloc?
it totally makes sense for you to use this as a selling point of CHERI relative to Fil-C, since it does mean that (a) you can run more stuff out of the box and (b) it’s neat that you can probably even run a JIT on top of CHERI.
But if we’re being honest, this is just a hard trade-off between security and compatibility. Fil-C is safer, CHERI is more compatible.
score 4 · canonical
We have compiled a lot of C and C++ codebases. Without supporting type aliasing, very few would work.
It aims to support all TCB code, because if you don’t have memory safety in your TCB then you don’t have it anywhere.
Does that include implicit unions? For example, many consumers of uthash store integers in the void* value slots. A lot of generic data structures in C expose void* or uintptr_t entries and have consumers that store pointers, data, or both.
We have surveyed a lot of exciting C idioms and we support almost all of them. Dismissing things that we’ve seen thousands of times across diverse codebases as not important does not give me any confidence in your approach or your evaluation methodology.
Yes, we set bounds on allocation. The port of OpenSSL was subject to a DARPA-funded red teaming exercise where they reintroduced bugs such as Heartbleed and had professional exploit writers try to break it. They were not able to.
Your custom allocator gets memory from somewhere, whether that’s mmap, malloc, or something a layer down. The thing above will apply bounds, so bugs in nested allocators can affect their consumers but not anything else.
score 4 · canonical
Because int-to-ptr casts work, that code would work in Fil-C so long as the store operation knows it’s storing to a ptr.
Folks will have these options:
buy special hardware (i.e. CHERI)
change a couple unions and about half of your malloc calls to use Fil-C
rewrite everything in Rust or another memory-safe language.
Nobody wants (1).
That leaves (2) and (3), and it’s definitely less work to replace a couple unions and half your mallocs than it is to rewrite in Rust.
Therefore: it’s true that you CHERI folks totally have to survey lots of idioms and work hard to make all of them Just Work. You probably have to do that and then some, because you’re trying to convince people to buy hardware that they dont want.
On the other hand, I just have to give folks an option that is easier than rewriting everything in Rust. So, I don’t have to support all the idioms.
score 2 · canonical
I see. So you can support unions as long as the compiler transforms the stores into stores of pointer types. This still breaks things like C++’s std::string, which is typically implemented as a union of a pointer to a heap allocation or a
That’s incredibly dismissive and simply not true. What people do want is high confidence that:
The first we have shown with formal proofs of ISA properties and (to most people, ore convincingly) with red teaming exercises where experienced exploit writers try to break CHERI. The one we ran at BlueHat was not very successful because all of the attendees decided that attacking CHERI was too hard and went after the easier targets, but the ones run by DARPA have been more successful (people actually trying and failing). This was a sticking point early on, but we now have sufficient evidence that it’s accepted.
We have been demonstrating the second by porting large software stacks to Morello. This includes the entire FreeBSD userland and kernel (Linux is not quite finished), the userspace graphics stack, Weston (including 3D acceleration), the Qt and KDE core libraries, and a bunch of applications (Chromium is nearly finished). This is now at the point where people are starting to believe our claims about compatibility.
Getting good performance numbers was hard, but Morello is a sufficiently realistic implementation that the folks at Arm have been able to isolate the root causes of overhead, most of which are artefacts of incrementally tweaking the Neoverse N1 and not building in CHERI from scratch, or from not doing the normal optimisation phase in a final design because of the short timelines. They now estimate that a production-quality version of Morello would have a 2% overhead in CHERI mode relative to the AArch64 baseline. This number is important because, as the Eternal War in Memory SoK reported, security features with an overhead of over 10% are almost never deployed and ones with an overhead of over 5% are almost never deployed across entire stacks (things in the 5-10% range are used in specially targeted places).
So, to summarise, we needed to be able to show that adopting CHERI really fixes problems in the presence of an intelligent adaptive adversary who can attack any part of the stack, that it’s possible to port enormous codebases in a wide variety of coding styles to the new target, and that the overhead is under 5% for typical workloads. You will need to convince people of the same things.
Note: The biggest adoption barrier for CHERI is not the new hardware (people roll out new hardware over time and incompatible hardware ages out naturally), it’s the new ABI. Supporting two incompatible ABIs comes with a huge cost for platform vendors. The cost of shipping two versions of every shared library is sufficiently high that OS vendors want to know that it means that they have a path to treating entire categories of bug as low priority crash fixes and not urgent security updates to even consider it. If you had 100% source compatibility, then the new ABI is less important in some markets because people can recompile everything at once, but if you don’t then supporting both for years is essential and that comes with a high cost.
With your small corpus of code tested, I remain unconvinced by this. There are so many corner cases that show up in C and C++ (most desktop software and a lot of server software uses at least these two languages and requires compatible ABIs) that you have not discussed that it feels very much like where we were ten years ago.
score 6 · canonical
No compiler transform. You have to change your code.
I’ve changed some code to do this, it’s not terrible.
I’m being honest with you. I appreciate your honesty, too.
I think CHERI has already achieved amazing technical goals and yet it’s not adopted yet. I think asking folks to use new hardware is insanely hard.
ABI is just one problem. You’re right that it can be a huge problem. That said, Apple has a ptrauth ABI slice that is totally separate from what civilians on Apple platforms get, and ptrauth has nowhere near the value of CHERI. So ABI can’t be the only problem and it’s certainly not the biggest.
I can think of the following additional problems:
Folks want to invest in solutions that will work on a lot of different CPUs, since modern HW is more than just one CPU. There’s the CPU everyone thinks about and then N other, smaller CPUs to do auxiliary tasks that are often super sensitive.
NIH syndrome. You’re not just asking folks to adopt your new HW, you’re asking them to accept some humility. The psychology of it is hard. (That’s also true with programming languages, but less so, because language adoption rarely depends on any vendor, and the cost of using a new language for something is far lower than the cost of adopting new HW.)
Opportunity cost versus somehow fixing the problem in software, in a Rust-like way, so that pointers can still be thin.
A problem that Fil-C also has: the UaF story is kinda meh. Both Fil-C and CHERI preserve the integrity of their capabilities. But I think there’s likely to be a class of scary security vulnerabilities that are UaF-based, do not require memory corruption, don’t require busting any capabilities, but just rely on seeing memory you shouldn’t be able to see due to UaF. CHERI docs seem aware of this problem, with all that talk of GC. I think that scares people even more! It means that the cost you pay for CHERI buys you something that is safer, but with that shady caveat about UaF. Fil-C has this shady caveat, but implementing a high-speed barrier-based concurrent GC in Fil-C is much easier than doing it in CHERI. (As of yesterday, Fil-C even has safepoint hooks.)
I’m not surprised that you’re unconvinced. Time will tell.
score 3 · canonical
If I understand your comments elsewhere correctly, this would stop working as soon as the user tried to store something of another type there? Like storing a pointer, and then replacing it with an int, and then replacing the int with a pointer, and so one.
Also, I just don’t understand your claim that Fil-C is safer. If the user can choose to do things that makes CHERI less useful, surely they can just choose to not use Fil-C? If “can’t make your own alloc” is acceptable, that’s very easy to enforce on a CHERI system. All of Fil-C’s properties depend on user choice and implementation. And if you pre-suppose good user choices and implementations, then CHERI does not have the drawbacks that you claim. CHERI also has something Fil-C does not. Relevant quote:
This means that Fil-C must simply trust that everything else in the same address space is not doing anything bad. That every Rust
unsafeis definitely correct, that the JS implementation has no bugs that would allow writes elsewhere on the heap. CHERI on the other hand can allow safe single address space sharing that is also totally safe.I’m pretty sure they want all vendors, such as Arm, Apple, Intel, and so on, to add CHERI to their products. So there would be no need to convince people to buy special hardware.
score 5 · canonical
I should not have been writing comments at 1 AM, because with not tired eyes I see that you were obviously talking about boxing integers in pointers, apologies.
score 1 · canonical
If you try to compile an allocator that isn’t safely implemented on CHERI, it’ll just work - you might not even notice.
If you try to do it with Fil-C, you get a runtime error.
That’s what memory safe languages are all about - giving you feedback when you did something unsafe.
That’s true! CHERI means you don’t need a type safe language to get some kind of safety.
But you need special hardware, which makes it kinda impractical.
Right! That’s the key difference between CHERI and Fil-C:
For CHERI to be successful, the CHERI folks have to convince at least one vendor to ship it, and that vendor had better do a good job of it or it’ll taint the whole project. Yeah, ideally they’d convince all the vendors.
For Fil-C to be successful, I just have to either (a) convince one person to make a useful thing on top of Fil-C or (b) port some useful thing to Fil-C, where that thing becomes much more useful with the added security and not much less useful due to the perf overhead. So, the adoption bar is inherently lower. Sure, it means code changes that CHERI won’t require.
score 2 · canonical
I wrote this last but decided to put it up front rather than at the end: despite all the arguing, I do find Fil-C to be pretty cool, and I hope you’re successful with it! I’m just skeptical, and disagree with your thoughts on CHERI.
Nothing prevents you from enforcing something similar under CHERI. Fil-C has a single blessed alloc, and prevents other allocs from being implemented. This presumably also means blocking all system calls that might be used to implement an alloc. Ultimately, my point is that since Fil-C is software, and CHERI simply adds additional HW features, anything you can do with Fil-C, you can definitely do with CHERI. And any time you figure out how to leverage CHERI to do one of the things you want to do, the hardware is now helping enforce it.
CHERIoT has a shared heap between all compartments, and a single allocator. It enforces quotas, and gives you full spatial and temporal memory safety. As someone in the embedded space, I find it incredibly compelling.
But it also means that Fil-C isn’t memory safe in practice, since any Fil-C code will surely often interact with code that isn’t Fil-C. That’s one thing I like about CHERI; memory safe languages may be safe within their own specified semantics, but that falls apart as soon as any external factor may violate the invariants of the safe language. With CHERI, you can know that your nice and safe OCaml program won’t be screwed up by some C++ library you needed to use. Like, I’m a huge Rust fan, but I got to admit, CHERI plugs a big hole that currently affects all safe or semi-safe programming languages.
And idk how practical it would be to replace all not safe code. Fil-C helps a lot with some of it, but far from all.
Note that CHERI is shipping later this year in a microcontroller. And if I recall correctly, at least one other microcontroller vendor is also designing a CHERI chip. So it’ll be able to prove itself in the real world soon. Of course, there are a lot of differences between the world of embedded and the PC / server space. If the standard is “make one useful thing on top of”, then I think CHERI will succeed :)
You may be surprised, but CHERI already has a decent amount of momentum. Couple hundred million invested, a good number of companies, mostly in industries where safety is important, doing CHERI things. A few governments even recommend CHERI already, even though no hardware is generally available yet.
Also, I’m curious, you’ve mentioned that you’ve found it to be pretty easy to port C projects, but what about all the code that isn’t in C? There’s tons of C++ and Rust out there, which CHERI would make safe, but as far as I can tell, Fil-C would not. So regardless of all else we’ve talked about, CHERI casts a much broader net. Personally, I’d prefer to never write C ever again, even if it was to be made entirely safe. So in any situation where I need a “low level” language, it’d probably be Rust, possibly with some
unsafe {}. So from my point of view, while both CHERI and Fil-C promise to help with (some of) the code we already have, only CHERI promises to help with new stuff.score 3 · canonical