Nobody is trying to hide the existence of "eval" or "unsafe". You're making a categorical claim of safety that's true only under a tendentious reading of common English words. Users reading your claims will come away with a mistaken faith in your system's guarantees.
Let us each invest according to our definitions.
You know about this limitation that you keep going on about because it’s extremely well documented on fil-c.org
Voiceover: "Miracurol cures cancer."
[Couple now laughing over dinner with friends]
"Ask your doctor if Miracurol is right for you."
[Same footage continues, voice accelerates]
"In clinical trials, five mice with lymphoma received Miracurol. All five were cured. One exploded. Not tested in humans. Side effects include headache, itchiness, impotence, explosion, and death. Miracurol's cancer-free guarantee applies only to cancers covered under Miracurol's definition of cancer, available at miracurol.org. Manufacturer not responsible for outcomes following improper use. Consult your doctor."
[Couple walking golden retriever, sun flare]
Voiceover: "Miracurol. Because you deserve to live cancer-free."
Patient: "I exploded."
Miracurol: "That's extremely well documented on miracurol.org."
> I will define "trustworthy system" as one in which the author acknowledges and owns limitations
You can't then go on to complain that the author does document the limitations but considers the overall system good. Fil-C, by the definition you just espoused, is a "trustworthy system".
int arr1[] = {1, 2, 3, 4, 5};
int arr2[] = {10, 20, 30, 40, 50};
int *p1 = &arr1[1];
int *p2 = &arr2[2];
int *p = choose_between(p1,p2);
//then sometime later, a function gets passed p
// and this snippet runs
if (p == p2) {
//p gets torn by another thread
return p; // this allows an illegal index/pointer combo, possibly returning p1[1]
}
Is this program demonstrating the issue? Does this execute under Fil-C's rules without a memory fault? If not, could you provide some pseudocode that causes the described behavior?You can’t access out of bounds of whatever capability you loaded.
You're shredding your credibility for nothing. You can instead just acknowledge Fil-C provides memory safety only for code correctly synchronized under the C memory model. That's still plenty useful and nobody will think less of you for it. They'll think more, honestly.
Try it. That’s what happens.
> through its loaded capabilities, which is factually correct but a non sequitur for the subject at hand.
It’s literally the safety property that Fil-C guarantees.
Safety properties provided by languages aren’t about preventing every bad thing that users can imagine. Just because the language does something different than what you expect - even if it allows you to write a program with a security bug - doesn’t mean that the language in question isn’t memory safe.
> You're shredding your credibility for nothing. You can instead just acknowledge Fil-C provides memory safety only for code correctly synchronized under the C memory model.
Fil-C provides memory safety even for incorrectly synchronized code. That safety guarantee is easy to understand and easy to verify: you only get to access the memory of the capability you actually loaded. You’re trying to evade this definition by getting hung up on what the pointer’s intval was, and your PoC uses a pointer comparison to illustrate that. You’re right that the intval is untrusted under Fil-C rules.
I’m not going to downplay the guarantees of my technology just to appease you. Whether or not you find me credible is less important to me than being honest about what Fil-C guarantees.
> If you set the index to `((alice - bob) / sizeof(...))` then that will fail under Fil-C’s rules (unless you get lucky with the torn capability and the capability refers to Alice).
In the comment above, you write, referring to a fault on access through a torn capability
> Try it. That’s what happens.
Your position would be clearer if you could resolve this contradiction. Yes or no: does an access through a pointer with an arbitrary offset under a data race that results in that pointer's capability tearing always fault?
> You’re right that the intval is untrusted under Fil-C rules.
Can Fil-C compile C?
You can't argue, simultaneously,
1) it's the capability, not your "intval", that is the real pointer with respect to execution flow and simultaneously, and
2) that Fil-C compiles normal C in which the "intval" has semantic meaning.
Your argument is that Fil-C is correct with respect to capabilities even if pointers are transiently incorrect under data races. The trouble is that Fil-C programs can't observe these capabilities and can observe pointers, and so make control flow decisions based on these transient incorrect (you call them "untrusted") inputs.
I don't see it. Rust makes the same guarantees regardless of the unsafe keyword. The difference is only that with the unsafe keyword you the programmer are responsible for upholding those guarantees whereas the compiler can check safe Rust.
I would guess that somebody more on the pulse of C's safety efforts could tell you whether they have a definition of memory safety for C or whether they're comfortable with an existing definition from somebody else.
In short, the definitions are not important. What matters are the risks that you do or don’t run. And if your Rust code contains unsafe blocks, you are running risks that you wouldn’t be if you used Fil-C, which has no such escape hatch. (Of course this goes both ways – your Fil-C code is more likely to fail, safely, with a runtime error due to a mistake that Rust would have caught at compile time.)
Real world C software does not read like software written by people who are in fact upholding those guarantees you say C could equally have. It reads as though they think such a guarantee is a joke or an irrelevance. It's not rare for me to run into people who think C's pointers are just indexing into a massive array of all RAM (or its equivalent on today's systems with virtual addressing), that's not just not in the same ballpark as a safe C program, that's playing a different sport on another continent.
I'm curious what you make of quotemastr's point about a race causing a mismatch between the pointer's capability and its index. First off, in your estimation can this realistically be exploited to wreak havoc on extant C programs compiled using Fil-C? Second, is such a mismatch able to happen in safe Rust? Third, is such a mismatch able to happen in unsafe Rust?
Edit: clarification to narrow the question even further
"Wreak havoc" is a very vague claim. Instinctively the tearing feels like something very difficult to usefully exploit, but, we know historically that the only people who can reliably tell you whether it was difficult are the attackers actually trying to do it. Don't believe the defenders.
AIUI this capability versus value distinction is a Fil-C thing. So, that's not a thing in Rust at all. In Safe Rust the pointer types, which is what we care about here, aren't very interesting because safe Rust can't dereference them, safe Rust is fine with you making a pointer from the word "LAUGHING" (not a pointer to the string, just the literal bytes in ASCII, but treated as a pointer) or from just some random bytes you found in a data file, because it's not allowed to dereference them so, cool, whatever, no harm no foul.
In unsafe Rust we're allowed to dereference valid pointers, but it's our job to ensure we obey that rule about validity, it being our job to obey rules is what "unsafe" means. So, that silly "LAUGHING" pointer isn't valid, it's just pointer-shaped toxic material. Even if, by coincidence, a pointer you have happened to have the same address as that pointer, in both C and Rust it's not OK to just go around dereferencing invalid pointers, they are not offsets into an imaginary huge array of all memory even though some C programmers act like they are.
Ignoring the Fil-C specific capabilities, in Rust the tearing issue is a matter of synchronization, which is something Rust cares about as part of delivering "fearless concurrency". Rust's marker traits Send and Sync are good place to start learning about that. Yes, we could unsafely implement these marker traits in unsafe Rust when we shouldn't, and thus enable what I imagine you'd call havoc.
So, mostly the problem is that your question is (unintentionally) too vague to answer well but I hope I was at least somewhat helpful.
Weird execution is a term of art in the security biz. This is not that.
Weird execution happens when the attacker can control all of memory, not just objects the victim program rightly loaded from the heap.
> Your central claim is that you can take any old C program, compile it with Fil-C, and get a memory-safe C program.
Yes. Your program is memory safe. You get to access P1 if p pointed at P1.
You don’t get to define what memory safety means in Fil-C. I have defined it here: https://fil-c.org/gimso
Not every memory safe language defines it the same way. Python and JavaScript have a weaker definition since they both have powerful reflection including eval and similar superpowers. Rust has a weaker definition if you consider that you can use `unsafe`. Go has a weaker definition if you consider that tearing in Go leads to actual weird execution (attacker gets to pop the entire Go type system). Java’s definition is most similar to Fil-C’s, but even there you could argue both ways (Java has more unsafe code in its implementation while Fil-C doesn’t have the strict aliasing of Java’s type system).
You can always argue that someone else’s language isn’t memory safe if you allow yourself to define memory safety in a different way. That’s not a super useful line of argumentation, though it is amusing and fun