93 comments

[ 3.0 ms ] story [ 159 ms ] thread
"Defense in depth" worked fine against amateur attacks. Well-funded attackers who can work on breaking each stage and have a library of attacks to combine are a much worse threat. More attacks like that are appearing. Stuxnet is the strongest example.
From my understanding it's the contrary. The success of Stuxnet relied on the fact that siemens recommended to not change the default passwords as it could affect the operations of critical infrastructure, which is not exactly what I would call defense in depth. Modern defense in depth would require the use of 2FA for manual access (in addition to strong passwords).
That's a very pessimistic take. I think defense in depth works exactly as advertised: you need progressively disproportionate resources to mount successful attacks, to the point where you need state-level actors/funding.

E.g. modern cryptography works, the proof being that the UK wants to change the law to effectively ban it.

We've commoditized e2ee. The next step is to commoditize secure software, so that the baseline effort for any attack is too high for less determined / funded actors. You will still occasionally have to face a stuxnet, but we shouldn't need to face another epidemic of cryptolockers.

Defence in depth is a sound principle. It helps to raise the cost of successful attack, and to avoid single points of failure in your security architecture. It mitigates malicious attacks, human error and component failure.

You are right that nation states can throw more resources at an attack. All you can do is make attacks as difficult and expensive as possible for an attacker to pull off without detection (commensurate with the value of the assets being protected).

> More attacks like that are appearing.

I don't think so. Generally as software becomes more secure the hacks you hear about will become more sophisticated to match, but this does not mean that they are more prevalent.

I'm only learning Rust, but the borrow checker and ownership and lifetimes could be duplicated in C with a little extra syntax, couldn't they?
By patching C like this, wouldn't you end up with Rust or something very similar before you know it?
safe Rust isn't just the borrow checker, it's also:

  - no uninitialized memory
  - clone vs copy semantics as part of the type system
  - Send, Sync, "fearless concurrency"
  - no unchecked indexing
  - type safety (you can't just cast things like in C)
I do think these are clear complements to a borrow checker, since it's all in the interest of preventing undefined behavior. Yet, there are even more nice modern features of Rust that C lacks:

  - hygienic macros
  - namespaces
  - pattern matching
  - modern build/package system
Also proc_macros. Often Rust ends up easier to read than Python due to proc_macro abstractions, too. Serde is a great example for this.

Rust is really high-level when you use it correctly.

And I rarely need lifetime annotations. Which is especially easy if you write in a somewhat functional style.

No, there’s lots of technical reasons why. Not least of which is that c/c++ has be try very loose aliasing rules as well as the lack of a concept of move/drop (language nerds can chime in with all the other reasons). Even c++ would struggle even though committed members are aware c++ is dying and are trying to bridge the gap somehow.

If you changed the language to support a Rust-like borrow checker, you’d lose back compat which defeats the purpose of stick with C semantics in the first place. There’s the related problem that the borrow checker isn’t automatic. There are places it does good inference, but any structure that has ownership needs to have that owner ship explicitly defined (and often in functions too). That is hard to do when you have to deal with a lot of legacy code that doesn’t have it - while that part can be solved through annotations, things inexpressible in the Rust borrow checker are perfectly legal C so you’d somehow also have to change that code. Additionally the Rust borrow checker can still fail spectacularly on very reasonable code. That’s being addressed in Polonius if that project ever lands, but even then it’s just a step function improvement to solve specific what should be valid code - it doesn’t fix all issues with the borrow checker.

C++ is exploring gradual safeness to provide for optionally strengthening the safety but it’s unclear that approach actually makes sense other than as a balwark to c++ dieing completely (ie the switching cost would be weighed against the cost of fortifying the safety of the code and relying on a poor estimate of the risks involved since there’s too many unknowns)

TLDR: structural, cultural, and back compat issues will prevent anything like the borrow checker.

Why do you think C++ is dying?

Maybe in your industry, but not in general.

Every major tech organization has indicated their shifting focus of their flagship products to shift the codebases from c/c++ to rust. Microsoft (Windows, Azure maybe office, not sure), Google (Chrome and probably a bunch of internal projects), Apple (various kernel pieces to harden and fix the ability to jailbreak). Linux has dipped its toe in the water with drivers but honestly I expect that to shift away from C and then eventually C will go into a “no new subsystem in C” policy followed by a “rewrite critical subsystems in pure rust).

Dying doesn’t mean that there’s not going to be developers. It just means that it’s no longer growing and the ability to hire developers that have significant c/c++ experience will get harder and harder. It’s still very early days and c/c++ has a lot more legacy code still running than COBOL ever did and COBOL is still alive and kicking in a sense. I would still classify cobol as a largely dead language even though technically people still know it and the code is running.

As for industry, could be. I imagine you think finance but I have a hard time seeing them stick to c/c++ if the rest of tech proper has switched. For new projects, maintenance will be cheaper because of things like cargo crates and other ecosystem niceties that c/c++ will never have. And rust performance is on par with c/C++. So what would be the case for starting any new project in c/C++?

I’m not predicting a timeline. I’m just describing a clear trend. It could easily take 50+ years. Depends on investments by corporations + grass roots + how effective the c++ committee is at stopping the bleeding + whether the rust community does an own goal like trying to standardize the language through ISO which is a huge contributor for why c++ and c will be unable to adjust. But right now rust has a clear velocity and acceleration advantage and has achieved “escape” velocity for when a language will likely take over for another.

In Microsoft's case, it is still baby steps outside Azure.

When XBox, Office and Windows business units really do approach Rust support the same way as they embrace C++ over anything else, then Rust is really in the house.

Yes I am aware of Rust/WinRT and the GDI regions kernel module rewrite.

Rust/WinRT is done by the same guys that botched C++/WinRT, no big hopes with that one.

https://www.theregister.com/2023/04/27/microsoft_windows_rus...

https://www.theregister.com/2024/01/31/microsoft_seeks_rust_...

Reasonable people can disagree but I wouldn’t call that a baby step. Core windows libraries, the NT kernel all being rewritten in Rust. Not sure why 365 stuff is being rewritten from c# to rust but maybe there’s some kind of perf issues that Rust will help them with.

But your bar is also too high. This stuff happens slowly and then all at once. The slow bit is making the infra changes and figuring out how to start the porting process so that other people can start to contribute in a way that’s a positive addition.

The only kernel stuff I already mentioned, there isn't anything else.

On Office side, is yet again Azure infrastructure, which I also referred.

Game industry is solidly C++ and that’s not changing anytime soon.

It’s not just inertia. Games have very specific latency and timing requirements that required tightly integrated language and compiler features developed for this very purpose. The financial industry too has similar needs and solutions. Rust isn’t a fit for those use cases. Not without a mountain of work that no one is doing, nor would be likely to be accepted as it conflicts with the design goals of Rust.

What the games industry needs is a language packing a disruptive, 10x advancement to get away from C++. There are a lot of accumulated ideas right now that could make, perhaps, a 3x-5x difference, and are reasonably well-represented when looking at stuff like Zig, Nim, Odin, Beef, Jai, and others trying points along the frontier of "possible games languages".

I think Zig stands the best chance of turning the corner by attacking the problems at the very base of the build system, libc, etc. Doing that opens up the floodgates for experimentation over a new base layer, and that will get you to a 10x change.

I don't think games have any problems with build systems or libc. Source I am working in gamedev for some time. I simply do not see your stated 3x-5x difference. Would love to be proven wrong.
Yeah build systems and such are a solved problem in the game industry, where build issues are self contained by the engine which often has custom tooling. If there is any killer feature it is probably at the intersection of coroutine execution of programmable script elements. Lua, one of the most commonly game scripting languages, leaves a lot to be desired. But not many projects are looking at the requirements of games here. The next generation of language will probably come from within the industry, from something like Unity’s Burst compiler, Unreal’s Verse language, or an evolution of rvscript into a JIT compiled fast backend.
> Rust isn’t a fit for those use cases. Not without a mountain of work that no one is doing, nor would be likely to be accepted as it conflicts with the design goals of Rust.

Could you elaborate?

Rust's safety features and assurances get in the way of absolutely minimal latency requirements. It'd end up as a heaping layer of `unsafe{}` blocks, which would defeat the point, and I'm not even sure that would be sufficient. C++ lets you make a lot of unsafe assumptions about aliasing and such, which an optimizing compiler turns into extremely fast code but is in general unsafe. I'm not sure the Rust compiler would be able to output such code as it currently stands.

Rust's approach is to produce code that is as fast as possible, but with the assumption that safety is paramount over breaking speed records. That's opposite the tradeoff that cutting edge games want to make.

Keep in mind a game engine might make updates to 100,000 objects, 144 times per second. In that context, every nanosecond really does matter.

Investment banks / high frequency traders care a little bit more about safety guarantees. But not as much as you might think. A segfault can cost you a trade, but being 0.5ns slower than the competition will cost you your business.

You’re overstating things. First, 100k objects is for very complicated games. And even then that’s 10us per object. “Nanoseconds” is the wrong order of magnitude - it’s closer to 10s or 100s of ns and a bounds check is cheaper than that typically and amortized across all objects. Bounds checks are unlikely going to show up unless you do a bounds check for each element. And most of the time it won’t be there with idiomatic rust and you’ll need to sprinkle unsafe in a handful of hotspots at most for perf reasons. Also most games made don’t have the same requirements. I agree the ecosystem isn’t there yet. But also, gaming is the least important industry because safety there is an after thought.

As for finance, you’re hyper focusing on HFT which is only part of what they use c++ for.

And again, I think you’re being a bit overly dramatic about the overhead Rust imposes or how much “unsafe” is require to recapture the performance.

As for aliasing, you’ve got it backwards. C++ loose aliasing rules means that it can’t enable optimizations that generate better code. Rust’s stricter aliasing rules means they ask llvm to compile with strict aliasing which generates much better code because the compiler can make assumptions that aren’t possible in c++ unless you explicitly force it to (and in those cases safety can be impacted so in practice no one enabled strict-alias in a c++ codebase which we know because llvm has bugs in that code path all over the place that Rust hit and had to fix which it is).

Rust emits memory barriers that C++ won't. That can result in unnecessary* execution delays in these tight loops, that yes costs a nanosecond or so. In cases where there's no conflict the memory barrier just occupies one cycle of the instruction decoder, but it can cause the CPU to not be able to reorder instructions in the most efficient way. This will cost you an instruction or two. On a ~2GHz cpu, that's 0.5 ns.

*Unnecessary because the thing being done won't crash the game if it is accidentally interleaved with a different update or whatever, and this is exceedingly unlikely to happen in a repeatable way. At worst you'll get a visual glitch gamers are used to, but often it's something internal and non-visible anyway.

Can you give an example of a memory barrier that Rust emits that C++ won't? :) This is the first time I hear about this.
C/C++ doesn't emit them at all unless you ask it to. At least on x86-like architectures. It might have to on ARM because of its weak memory model? I'm not sure about that. But the C++ standard does basically zilch with respect to thread safety unless you use the appropriate memory fence compiler directives.
What memory fences does the Rust compiler emit without you asking?
The Rust compiler won’t compile code that mutates shared state unless there are appropriate memory fences. It makes you insert these barriers for correctness.
Can you give an example? I think relaxed atomics don't have memory fences.
It could, but we're just now getting `bool`, `true`, and `false` as keywords in C23. The children of everyone posting here will be dead & buried before WG14 adds lifetimes to C's type system.
But at least, our grandchildren's graves won't be unsafely accessed.
People can and have added extra annotations to C and C++ code for such things. Static analyzers can catch some use-after-free and iterator invalidation bugs at compile time. Problem is:

• They're opt-in instead of opt-out (meaning none of your system libraries or 3rd party dependencies have bothered using them.)

• What's been implemented in practice frequently fails to find bugs in relatively simple toy examples (I often confuse myself wondering why I've failed to turn some new bit of static analysis on, when in fact I have, and it's simply failed to catch even my demonstration bug.)

• The effective tools, on the other hand, tend to have "false" positives as well.

The conclusion I've come to is that to fully solve these problems in C or C++, you'd effectively transform it into a new language. And at that point, you might as well stop pretending that's not exactly what you're doing, and actually create said new language.

---

That said, when stuck using C or C++, static analysis and the compiler-specific half measures that are available are worth using, IMO.

https://awesomekling.github.io/Catching-use-after-move-bugs-...

https://clang.llvm.org/docs/ThreadSafetyAnalysis.html

https://clang.llvm.org/docs/AttributeReference.html#lifetime...

https://github.com/MaulingMonkey/notes/blob/master/programmi...

• `printf` type checking annotations exist, but don't necessairly work on MSVC. Often sanest to make your legacy logging macros call `printf` in a dead branch to leverage any hardcoded type checking of `printf`

Basically everything is a warning that you might consider cranking up to an error, of course.

> The effective tools, on the other hand, tend to have "false" positives as well.

That’s true for rust, too, with the difference being that rust won’t compile false positives. I haven’t used rust or recent C++ much, but it wouldn’t surprise me if the false positives that C++ gives had significant overlap with cases where the equivalent rust code would not compile.

An example of this could be trying to mutably access disjoint parts of a Vec. The Rust language today doesn't understand that is safe today, because it would require tracking all indices used somehow to verify that the accesses are indeed disjoint. But you can write unsafe code to access those fields, or even better, use Vec::split_at_mut() which uses the unsafe code you'd have to write with the appropriate precondition checks to ensure you can't misuse it in a way that causes memory unsafety.
They could, but for compatibility reasons it would be opt-in. The strength of Rust is that you can't opt out of the borrow-checker (even with unsafe).
No. This would require a significant change to the semantics of C.
The example with IEF doesn’t make sense. Whether you declare a function private or public doesn’t impact its usability as a gadget. And as I understand it reading up on IEF, the problem with removeDatabase is when it’s exposed as a remotely callable API without any auth check which is a very different problem. Am I misunderstanding what the author is saying?

I think the main problem with the article referenced in the submission is that percentages of vulnerabilities change based on the problem domain. The main threat for web facing services isn’t memory safety but that’s because most web facing services already use memory safe languages like JS, Java/JVM, Python, Ruby, or Go. It’s weird to pick Rust for its memory safety for web services. The reason to pick Rust is because you get C/C++ like performance without the memory safety issues. If you don’t need the perf, all the other languages are better choices because you’ll probably move quicker.

It’s interesting that 20% are still memory vulnerabilities. Assuming this is all the same class of attacks on web services, that means 20% of applications do care about perf and have exploitable memory safety issues. Or the methodology is shoddy and they’re comparing different classes of software. Regardless, the premise of the first article is flawed and rife with statistical analysis problems that paint a misleading picture while the response isn’t infusing me with a lot of confidence on the merits and comes off as unnecessarily fanboish trying to stretch for a reason of “but no rust still makes sense”. 70% of vulnerabilities in Chrome are memory safety. This holds for similar classes of software written in c/C++ (eg Windows). There’s a reason all major c/c++ codebases have started getting grass roots and organizational support to transition away as quickly as possible.

Rust has other advantages as well. For example, a common error when something goes wrong in Python is that a 'None' value ends up somewhere where it shouldn't be. Typically this None has traveled quite a while before some code tried to use it.

The richer and stricter type system of Rust makes such errors a lot less likely. And if an error escapes, there usually is a way better indication of the origin of the error than just 'None'.

Most of those Rust features over Python, that are commonly referred to, are available in any programming language of ML linage.

Caml Light, SML, OCaml, Haskell, F#, Scala, and whatever else is out there.

Most of them also come with a REPL, and have multiple toolchains, no need for always be waiting for the compiler.

It is very nice that those languages have those features. But it is quite rare that people who are used to C or Python code bases are switching to those languages.

In particular a strong point of Rust is that C programmers feel comfortable switching to Rust in way that has not happened with other safe languages.

C folks hardly change to anything, at least the hardcore ones, unless forced to by external forces.

Python well, it isn't as if Rust is on the same level of developer experience and friendliness.

With CPython finally getting a JIT, and Mojo, I don't force that happening long term.

In any case, hardly changes the fact of not being Rust specific type system features not available anywhere else.

There is a huge amount of C programs and libraries that were written in C because back then it was the best tool for the job. A lot of people are not convinced about C++, but for many things that were written in C, Rust is now a good option. More and more people are asking questions about how C programs avoid memory errors. The easy solution is to switch to Rust for new programs and libraries.

Python has the problem that large Python programs are often awkward. There are many reasons for that. And somehow the Python community doesn't seem to be able to solve those issues. Rust is a lot better in that respect.

Many of those random userspace C programs (i.e. stuff in the (1) manpage section) could have been rewritten in e.g. OCaml just fine ages ago, if only people were paying attention and not in the curly brace mind prison.
I disagree.

It's much more common to go from C to Python, Go or even Zig, rather than Rust.

I think the C++ to Rust transition is by far the most common one.

> In particular a strong point of Rust is that C programmers feel comfortable switching to Rust in way that has not happened with other safe languages.

I don't see this at all. C++ programmers might be switching, but C programmers aren't reaching for Rust when they need something better than C.

Ergonomic advantages / reduced defect rates != vulnerability issues. The former aren’t a problem for languages like JS which has all sorts of weird sharp edges. But vulnerabilities are treated as a separate risk factor because the issues can spill to unexpected areas which makes risk harder to quantify and reason about. A vulnerability in some small tool that barely anyone uses can be used to exfil your entire customer database or do a ransomeare attack and cost you hundreds of millions of dollars in short term damage + investigations by regulatory bodies and law enforcement. A buggy piece of software at worst causes some bad PR or a failed product which is way way smaller and localized of an issue
Some classical programming languages that don't suffer from buffer overflow or memory allocation issues, but are horribly unsafe due to type (and other issues) are shell scripts and, at least, early versions of php.

Weak type checking at interface boundaries allows for all kinds of errors including vulnerabilities.

A famous class of vulnerabilities due to poor type checking is SQL injection.

SQL injection happens on any language where people decide string concatenation is better than using precompiled query parameters exposed by database drivers APIs.

Using Rust doesn't change how those folks keep concatenating strings.

The second example given (extracting information from a web view) is also weird. The rust code has the exact same flaw as the java code, which is that they do not validate the source. I think the author didn't really understand the underlying problem here. Bounds checking was not the issue with the java (android) code
I hate to impune their skills since I don’t know the person and this could just have been a hastily written and poorly thought through article, but for me it brings into question their claims as a cyber security expert with 10 years experience.
> The reason to pick Rust is because you get C/C++ like performance without the memory safety issues. If you don’t need the perf, all the other languages are better choices because you’ll probably move quicker.

Lets not forget a competent type system as well. Go/Java are decent, but i'd not even consider dynamic langs these days for any remotely complex project. I just don't believe in the benefits of a dynamic system.

I use Rust in areas that otherwise don't require it in part because i prefer the type system.

I think the author cheats a little bit, because in the first example on "IEF" he compares a function declaration containing an explicit visibility modifier "public" to the default Rust version (private).

But in reality, omitting Java visibility keyword puts the function into the "default" visibility rules, which is package-private. So, public in the same package, but private when seen from other packages. So the impact is severily limited, depending on the architecture of the application (it's de facto public for applications contained in 1 package).

Still, Rust version is more restrictive, so the point holds, but I think it should be fixed somehow.

Yeah, I had the same reaction, it was a very bad strawman: the author had explicitly set the function to be public in the Java example, and then not done so in the Rust example, and then argued that Rust is safer. But that's absurd: you deliberately made dropDatabase public in Java, which you could just as easily do in Rust by making it pub. The author just opted not to do that in order to argue that Rust is safer.

Your argument is fair that the "default" state in Rust is more restrictive, but the difference honestly is tiny. In practice, basically all Java code I've seen always explicitly states whether every function is private, public or protected, that's just how you write Java. Should it be compiler-enforced that you always do so? Maybe, but we're really splitting hairs here.

True, I've written "the argument holds" in strictly theoretical sense that Rust version is more restrictive, but since the degree of this restriction is so small, I don't think this particular trait has any impact on security of anything.
> We should expect all our languages to be safer.

My prediction is modern C++, and even (looking at C23) modern C, will be evolved, albeit gradually, to offer enough of Rust's safety so that on a cost-benefit analysis of either updating an existing C or C++ codebase to its modern, safer dialect or rewriting it in Rust, it will be a no-brainer to stick with C or C++. Rust will ultimately become a marginal language, like (sorry) Zig:

https://www.phoronix.com/news/CPP-Linux-Kernel-2024-Discuss

C++ is already a much safer C, yet plenty insist in using it like C, full of Cisms.

When copy paste compatibility to C is available, only regulated industry practices like MISRA force devs to adopt them.

I don't know about that. The average C++ software is probably safer purely because you can delegate most memory management to standard library.

But I've found C to be much easier to reason about. You can inline all the functions and macros and all that's left is a bunch of pointer assignments and arithmetic. C++ has a much more complicated lifetime model which is surprisingly easy to violate. Combined with templates, it becomes really difficult to dig in.

Due to this, C is much more practical to use with formal verification tools like Frama-C and various model checkers.

If Rust had a restricted subset which had a footprint compared to C and allows formal verification (including functional requirements) I would switch right now.

>If Rust had a restricted subset which had a footprint compared to C

Take a look at "#![no_std]", a Rust feature.

If you do that, your Rust Hello World program will have 14 kiB (with no linker optimizations).

>and allows formal verification (including functional requirements)

See <https://rust-formal-methods.github.io/tools.html>.

Also, the Rust compiler can be used as a library and you can add whatever checks you want like that. See <https://yoric.github.io/post/uom.rs/> for an example (not used that much).

Also, I like <https://docs.rs/pre/latest/pre/attr.pre.html>. It allows you to define and then mandate specifying textual requirements (in addition to the usual conditionals).

naïvely, 14 kibibytes sounds like a factor of 2½ worse than c:

    $ cat smallhello.c
    #include <unistd.h>

    int main()
    {
      char hello[] = "hello, world\n";
      write(1, hello, sizeof hello - 1);
      return 0;
    }
    $ diet gcc -Wl,-z,noseparate-code smallhello.c -o smallhello
    $ strip smallhello
    $ ls -l smallhello
    -rwxr-xr-x 1 user user 4864 Feb  9 06:47 smallhello
    $ ./smallhello 
    hello, world
but that's mostly overhead of dietlibc and linux elf rather than overhead of c per se. it's pretty easy to use c to build embedded firmware that fits in 512 or 1024 bytes (though not always advisable)

i would argue that this is 'with no linker optimizations' because -z noseparate-code was the default until a few years ago. linker optimizations can save another 200 bytes or so in this case

note that this is without dynamic linking. there's no giant standard library being swept under the rug here. most of the executable code in the resulting program is _start pulling in stackgap, which calls find_in_auxvec, __libc_open, __libc_read, __libc_close, __setup_tls, getenv, strstr, and finally main, and strstr in turn calls memcmp (as does getenv) and strlen. main() itself is only 61 bytes of code, __libc_write is 6 bytes, mostly a tail call to __unified_syscall, which is 34 bytes. so we're already looking at overhead that's 40 times the size of the hello-world code itself, which rust apparently expands to 100 times?

but possibly not; it's easy to accidentally compile something with c that ends up 14 kilobytes too

but basically c's required runtime is just memset() and memcpy(). everything else only gets invoked if you call it explicitly

The smallest ELF executable for x86_64 rustc has produced is 138 bytes. You can absolutely get rust code as small as you need, you just have to care enough about binary size to put in the work. We do embedded Rust at work, and do care about this.
Oh yeah, also:

> but basically c's required runtime is just memset() and memcpy(). everything else only gets invoked if you call it explicitly

Rust is barely more: https://doc.rust-lang.org/stable/core/

> memcpy, memmove, memset, memcmp, bcmp, strlen

> rust_begin_panic

> rust_eh_personality

c++ is a much less safe c: https://yosefk.com/c++fqa/
So people say, specially C folks.
i have also noted that venezuelans living in argentina are highly critical of the current venezuelan government, while venezuelans living in venezuela have a much wider diversity of opinion on this point
That would only work if the new versions were willing to break backward compatibility. The safety (and correctness) of a language is not about what it can do, but what it cannot.
And about what it did in the past.

Because that built habits. Has large bodies of education and documentation around it, and often even tooling (like snippets, ai completion, linters, static analysis)

Also Morello prototype architecture / CHERI could well just end up enforcing this all at the hardware level and make language-level safety a largely moot point.
Well, if you like crashy programs as opposed to insecure ones, sure!

CHERI would stop memory issues being exploited, but it won't make your program work correctly when those same errors are encountered.

I don't think there is a language that can guarantee correct operation when you write bugs in it ;)
True, but you can use languages that avoid whole classes of bug!

I guess my only point is that CHERI may protect you from the worst abuse of those bugs, but it still doesn't mean you shouldn't use something that doesn't suffer from them in the first place!

How far should one go with that line of thinking?

Should we all be using formally verified C that e.g. the aerospace industry uses? It can prevent whole classes of logic bugs that languages like Rust couldn't dream of

Obviously, you don't need to do that for the majority of things.

My point is much simpler, if you can use something that removes bugs with no appreciable additional effort (or actually less effort), why wouldn't you?

no, but you can prove the absence of some bugs at compile time, so you don't have to figure out at run time what to do when they occur

exactly how far that can go is a matter of debate (it clearly cannot cover all possible bugs, because people have legitimate disagreements about whether a given operation is a bug or correct) but it can certainly cover the bugs cheri can detect

Crash on memory issues is the ideal, far better than having to borrow check, if you also get remotely sane crash reports. Manual memory management often leads to faster and simpler programs than RAII/BC, they're just harder to debug on current hardware.
Morello provides some safety mechanisms, but it doesn't solve all problems. It only covers certain types of memory corruption issues, for example.
I think this is very unlikely to happen.

The main value of C and C++ is in their backward compatibility. Many projects don't even use current versions the languages, and stick to decades-old subsets instead.

There are safety improvements that C++ could do right now, but is unwilling to do. For example, `[]` on vectors could have bounds checks. `->` on empty `std::optional` could trap instead of being UB. Rust accepts performance cost of these things, C++ doesn't want to.

C and C++ move too slowly and are too fragmented and ossified to make a difference in the foreseeable future. They couldn't copy even the easy stuff like Rust's painless dependency management (C++ modules are not going well). Even if a miracle-safety-feature was invented tomorrow, it will take many many years before it goes through cycles of standardization, implementation across vendors, and new compilers being available as a baseline.

Safety improvements won't be a magic compiler flag — C has already deeply invested in analyzers, sanitizers, and fuzzers to improve safety without rewrites. Rust's improvements come from a richer but stricter type system, and that requires refactorings. There are safer C dialects already that have these features, but nobody wants to add lifetime annotations and rewrite their pointer arithmetic to use slices. "Nobody's going to rewrite millions lines of code" applies the same to Safe-C.

But the more Rust-like features they adopt, the easier it will be to automatically convert them to Rust.

That is impossible.

C/C++ has DECADES of development with all chances to become good to go ignored. Is CULTURAL.

Look, no even C `if` fall-trough is fixed, and that is probably the easiest thing to fix of all.

Expecting better from them is like expecting fossil fuel companies to be green.

I actually enjoyed reading this and found this somewhat insightful. A critique of modern languages is that while they are more rigorous, there is still the issue of module visibility which is kind of weak and limited.

For example, Java has packages and a notion of package private. But that was always a bit of a weak form of modularization. So you end up with a lot of public functions just so some code in a different package can reach it. And while packages are hierarchical, visibility ignores that notion. Java of course has proper modules now. But they are a weak derivative of things like OSGI that while widely used at some point never really went mainstream. Module visibility in OSGI included explicitly defining exposed and required interfaces. If not exposed explicitly, the class loader would prevent you from calling it. Even if it was defined as public.

That kind of mechanisms are still missing in most languages. You kind of get some of this by using micro-services. But routing your function calls over a network is kind of stupid from a performance point of view. It doesn't scale. I've seen some graphql based systems that are a bit challenged on this front. There are other examples of trying to solve this problem outside of the compiler at run/deploy time. A lot of sandboxing used for docker, wasm, etc. is basically about that. But that is kind of mitigating the lack of solutions at the source code level.

OSGI was clunky but it definitely had some interesting ideas. Very sad that that never caught on.

This article indeed did not make sense. The key point it doesn't get is that the majority of issues described in the original were not deeply embedded in the language. They weren't use-after-free etc. They were simply the programmer doing something bad, where something bad is highly domain-specific.

There will never be language-level protection against e.g. trusting a URL which you shouldn't trust, or making a function public when it should be private, because languages have to be general enough to trust some URLs and make some functions public. Languages cannot take away the responsibility of the programmer not to do stupid stuff.

(comment deleted)
Agree you can't kill all the badness, but e.g. web security has gotten better in many respects primarily (I have no proof of this claim but I was a pentester for a long time) because of improvements in libraries and frameworks.

I don't think Rust offers anything special in this regard however.

Rather I think it is better understanding of the security nuances of particular problem domains that lead to api safety improvements.

Devs today are not particularly better at preventing xss or sqli than they used to be but the default tools they reach for have APIs that make it harder to do the wrong thing.

Languages can actually help here! They aren't a panacea, of course, but you can do all sorts of things like encode things into the type system ("this is a safe string, you cannot put user tainted input into it without escaping it") or be amenable to static analysis ("this variable is not declared") or even manual code review ("I can understand the entire semantics of this block of code because it is impossible to override the behavior of the operations here").
A fair point that programers mustn't do stupid stuff. But languages (and frameworks) can help a lot with that.

When a language (or framework) makes it easy to do the right thing, and hard to do the bad thing, a large category of "stupid stuff" is avoided.

I'm lazy, or tired, or stressed, or focused elsewhere. Make it so, that at these moments I'm forced to run extra miles to "do stupid stuff" and we've avoided several bugs (or code smells and such).

Like you say, that still leaves the domain logic to make mistakes in. But if we could make our mistakes only there, we've eliminated a lot of bugs too: we only have to pay extra close attention in a fraction of our codebase!

Now,we aren't there. Rust isn't, at least. There's still a lot of boilerplate or utility code where I can (and will) do stupid stuff. But rust, with it's proper defaults, secure by defaults, is a lot closer than most other languages. And rusts' emerging frameworks inherit that mindset and help us get closer even.

> trusting a URL which you shouldn't trust

That's actually a feature in Ur/Web, which is a (very niche) language explicitly built for making web servers. It's sort of a domain-specific language though, so maybe it doesn't count.

> making a function public when it should be private

Nothing's gonna stop a programming mistake like that, but in languages with capabilities[0] you can restrict some functions to only be called when you have certain capabilities, and these capabilities can only be made to be acquired after authenticating a user, for example.

[0]: https://borretti.me/article/introducing-austral#cap

This post is not very good, sorry.

Let's start with the first example about IEF: first of all, I think that the security advisory was mostly referring to web APIs that allow users without the necessary permissions to perform actions they shouldn't be able to. If you can already call Java APIs you already have access to the machine so it matters little what visibility the method was declared with. And as somebody else mentioned, the default visibility for Java methods is package-private.

Second example about the URL interception: first of all, you could have `match`ed on the result of `strip_prefix` instead of checking `starts_with` and then `unwrap`ping the result of the former. Second, why not use a URI parsing library? And third: that is not what checking the source of the URL means in this context! I'm assuming there's a way to tell what is the URL currently shown on the webview, and that you should check that before doing an RPC.

The Rust code example shown in the post is functionally identical to the Java version. Also, `&str` doesn't mean the string size is known at compile time, the bounds checks are all performed at runtime.

Third example about path traversal: again, that's functionally the same code as the Java version. What is missing in both is a check that the path is relative to a well-known list of paths and doesn't contain any `..` which could possibly lead to, well, a path traversal vulnerability!

I appreciate the effort that went into the post and I agree that Rust is still better than many other languages out there (because of its API design in many important areas), but man, this ain't it.

Even if you can only eliminate the 19% memory related ones - that’s a huge win.
No programming language is more important than its best and worst ideas.
As far as I understand there are no new ideas in Rust. Linear types (for example) was invented in academia a long time ago.

So perhaps a more accurate statement would be that it is the first practical programming language to implement those ideas?

But what about F* (F-star) for example? That is a language used by Microsoft to write a proven correct network stack used in industry (and Firefox I think). That seems very practical to me.