40 ms·
Wow. We continue to be reminded that it's hard to write fully memory secure code in a language that is not memory secure? And by hard, I mean, very hard even
by slownews45 5y ago
Wow.
We continue to be reminded that it's hard to write fully memory secure code in a language that is not memory secure?
And by hard, I mean, very hard even for folks with lots of money and time and care (which is rare).
My impression is that Apple's imessage and other stacks also have memory unsafe languages in the api/attack surface, and this has led to remote one click / no click type exploits.
Is there a point at which someone says, hey, if it's very security sensitive write it in a language with a GC (golang?) or something crazy like rust? Or are C/C++ benefits just too high to ever give up?
And similarly, that simplicity is a benefit (ie, BoringSSL etc has some value).
- jandrese 5y agoIt's hard to fault a project written in 2003 for not using Go, Rust, Haskell, etc... It is also hard to convince people to do a ground up rewrite of code that is seemingly working fine.
- zionic 5y ago>seemingly worked fine That’s just it though, it never was. That C/C++ code base is like a giant all-brick building on a fault line. It’s going to collapse eventually, and your users/the people inside will pay the price.
- lelanthran 5y ago>>seemingly worked fine >That’s just it though, it never was. That C/C++ code base is like a giant all-brick building on a fault line. It’s going to collapse eventually, and your users/the people inside will pay the price. Sure, but everything is a trade-off[1]. In this particular case (and many others) no user appeared to pay any price, which tells me that the price is a spectrum ranging from 'Nothing' to 'FullyPwned' with graduations in between. Presumably the project will decide on what trade-off they are willing to make. [1] If I understand your comment correctly, you are saying that any C/C++ project has a 100% chance of a 'FullyPwned' outcome.
- hwbehrens 5y ago> It is also hard to convince people to do a ground up rewrite of code that is seemingly working fine. I think this is an understatement, considering that it's a core cryptographic library. It appears to have gone through at least five audits (though none since 2010), and includes integration with hardware cryptographic accelerators. Suggesting a tabula rasa rewrite of NSS would more likely be met with genuine concern for your mental well-being, than by incredulity or skepticism.
- fulafel 5y agoThe article says Chromium replaced this in 2015 in their codebase. (With another memory-unsafe component, granted...)
- vlovich123 5y agoBoringSSL started as a stripped down OpenSSL. That's very different from a ground-up replacement. The closest attempt here is https://github.com/briansmith/ring https://github.com/briansmith/ring but even that borrows heavily the cryptographic operations from BoringSSL. Those algorithms themselves are generally considered to be more thoroughly vetted than the pieces like ASN.1 validation.
- VWWHFSfQ 5y agonss was also generally considered to be thoroughly vetted though
- vlovich123 5y agoThere’s a world of difference between ASN.1 validation and validation of cryptographic primitives. The serialization/deserialization routines for cryptographic data formats or protocols are where you typically get problems. Things like AES and ECDSA itself, less so, especially when you’re talking about the code in BoringSSL. Maybe some more obscure algorithms but I imagine BoringSSL has already stripped them and ring would be unlikely to copy those. Why? Cryptographic primitives don’t really have a lot of complexity. It a bytes in/bytes out system with little chance for overflows. The bigger issues are things like side channel leaks or incorrect implementations. The former is where validation helps and the latter is validated by round-tripping with one half using a known-working reference implementation. Additionally, the failure mode is typically safe - if you encrypt incorrectly then no one else can read your data (typically). If you decrypt incorrectly, then decryption will just fail. Ciphers that could encrypt in an unsafe way (ie implementation “encrypts” but the encryption can be broken/key recovered) typically implies the cipher design itself is bad and I don’t think such ciphers are around these days. Now of course something like AES-GCM can still be misused by reusing the nonce but that’s nothing to do with the cipher code itself. You can convince yourself by looking for CVEs of cryptographic libraries and where they live. I’m not saying it’s impossible, but cipher and digest implementations from BoringSSL seem like a much less likely place for vulnerabilities to exist (and thus the security/performance tradeoff probably tilts in a different direction unless you can write code that’s both safer while maintaining competitive performance).
- slownews45 5y agoWhat's somewhat interesting is memory safety is not a totally new concept. I wonder if memory safety had mattered more, whether other languages might have caught on a bit more, developed more etc. Rust is the new kid, but memory safety in a language is not a totally new concept. The iphone has gone down the memory unsafe path including for high sensitivity services like messaging (2007+). They have enough $ to re-write some of that if they had cared to, but they haven't. Weren't older language like Ada or Erlang memory safe way back?
- dagmx 5y agoAFAIK the issue with messaging isn't that the core app itself is written in an unsafe language , but that many components it interacts with are unsafe. E.g file format parsers using standard libraries to do it. Granted those should also be rewritten in safer languages but often they're massive undertakings
- xiphias2 5y agoMemory safe language that can compete with C/C++ in performance and resource usage is a new concept. AFAIK ADA guarantees memory safety only if you statically allocate memory, and other languages have GC overhead. Rust is really something new.
- openasocket 5y agoThere's different classes of memory un-safety: buffer overflow, use after free, and double free being the main ones. We haven't seen a mainstream language capable of preventing use and free and double free without GC overhead until Rust. And that's because figuring out when an object is genuinely not in use anymore, at compile time, is a really hard problem. But a buffer overflow like from the article? That's just a matter of saving the length of the array alongside the pointer and doing a bounds check, which a compiler could easily insert if your language had a native array type. Pascal and its descendants have been doing that for decades.
- slownews45 5y ago
- dkga 5y agoI think a good approach could be what curl is doing. AFAIK they are replacing some security-critical parts of their core code with Rust codebases, importantly without changing the API.
- fulafel 5y agoIn 2003 you could have generated C from a DSL, for one. Like yacc and lex had been standard practice (although without security focus) since the 80s. Or generate C from a safe GP language, eg C targeting Scheme such as Chicken Scheme / Bigloo / Gambit. People have been shipping software in memory safe languages all this time, since way before stack smashing was popularized in Phrack, after all.
- pjmlp 5y agoModula-2 (1978), Object Pascal (1980), .....
- masklinn 5y ago> or something crazy like rust? There's nothing crazy about rust. If your example was ATS we'd be talking.
- zionic 5y agoC/C++ don’t really have “benefits”, they have inertia. In a hypothetical world where both came into being at the same time as modern languages no one would use them. Sadly, I’m to the point that I think a lot of people are going to have to die off before C/C++ are fully replaced if ever. It’s just too ingrained in the current status quo, and we all have to suffer for it.
- kwertyoowiyop 5y agoC/C++ will be around for at least a hundred years. Our descendants will be writing C/C++ code on Mars.
- nickelpro 5y agoOn any given platform, C tends to have the only lingua franka ABI. For that reason it will be around until the sun burns out.
- JulianMorrison 5y agoFWIW, Go absolutely would not stop you writing unbounded data into a bounded struct. Idiomatic Go would be to use byte slices which auto-resize, unlike idiomatic C, but you still have to do it.
- slownews45 5y agoWhat's the exploit path assuming no use of unsafe? I can see situations where I could probably get go to crash, but not sure how I get go to act badly. Note: Not a go / Haskell / C# expert so understanding is light here.
- JulianMorrison 5y agoGo has no "unsafe" keyword and several parts of the language are unsafe, you're thinking of Rust which has much tighter guarantees. Go idioms, like accepting data into buffers that are resized by "append", work around the unsafe parts of the language.
- slownews45 5y agoGo has an unsafe package. Is there an example of even "bad" go code that gets you from a overflow to an exploit? I'm curious, folks (usually rust folks) do keep making this claim, is there a quick example?
- ptaq 5y agoYou can totally do this with bad concurrency in Go: read-after-write of an interface value may cause an arbitrarily bad virtual method call, which is somewhat UB. I am not aware of single goroutone exploits, though.
- slownews45 5y agoConcurrency issues / bad program flow feel a bit different don't they? I mean, I can store the action to take on a record in a string in any language, then if I'm not paying attention on concurrency someone else can switch to a different action and then when that record is processed I end up deleting instead of editing etc. I mention this because in SQL folks not being careful end up in all sorts of messed up situations with high concurrency situations.
- foobiekr 5y agoTo provide some context for my answer, I’ve seen, first hand, plenty of insecure code written in python, JavaScript and ruby, and a metric ton - measured in low vulnerabilities/M LoC - of secure code written in C for code dating from the 80s to 2021. I personally don’t like the mental burden of dealing with C any more and I did it for 20+ years, but the real problem with vulnerabilities in code once the low hanging fruit is gone is the developer quality, and that problem is not going away with language selection (and in some cases, the pool of developers attached to some languages averages much worse). Would I ever use C again? No, of course not. I’d use Go or Rust for exactly the reason you give. But to be real about it, that’s solving just the bottom most layer.
- nicoburns 5y agoC vulnerabilities do have a nasty habit of giving the attacker full code execution though, which doesn’t tend to be nearly so much of a problem in other languages (and would likely be even less so if they weren’t dependant on foundations written in C)
- foobiekr 5y agoI don’t disagree with you. But just before writing that message I was code reviewing some python that was vulnerable a to the most basic SQL injection. Who needs or wants execution authority when you can dump the users table of salted sha passwords?
- saagarjha 5y agoIn C you can just use code execution to grab the passwords before they get salted…
- foobiekr 5y agoOne is the whole database. I am very aware of code execution attacks and persistence around them. C has a class of failures that are not present in most other languages that are in current use, my point is that it really only solves part of the problem. From a security perspective, the 90% issue is the poor quality of developers in whatever language you choose.
- jtchang 5y agoI'd like to write go or rust but embedded constraints are tough. I tried and the binaries are just too big!
- dochtman 5y agoThis is not true. Lots of people are putting Rust on microcontrollers now - just have to stick to no_std.
- steveklabnik 5y agoThe smallest binary rustc has produced is 138 bytes. It is true that it’s not something you just get for free, you have to avoid certain techniques, etc. But rust can fit just fine.
- cjg 5y agoDo you have a link to an article / report about that 138 byte program? I'd be interested how to achieve that.
- haberman 5y agoI'd also like to see the smallest Rust binaries that are achieved by real projects. When the most size-conscious users use Rust to solve real problems, what is the result?
- steveklabnik 5y agoOur embedded OS, Hubris, released yesterday, with an application as well, the entire image is like, 70k? I’ll double check when I’m at a computer.
- steveklabnik 5y agoYeah, so, using arm-none-eabi-size, one of these apps I have built, the total image size is 74376 bytes. The kernel itself is 29232 of those, the rest are various tasks. RCC driver is 4616, USART driver is 6228. We have to care about binary size, but we also don't have to stress about it. There are some things we could do to make sizes get even smaller, but there's no reason to go to that effort at the moment. We have the luxury of being on full 32-bit environments, so while there isn't a ton of space by say, desktop or phone standards, it's also a bit bigger than something like a PIC. Lots of folks also seem to get the impression that Rust binary sizes are bigger than they are because they use ls instead of size; we care deeply about debugging, and so the individual tasks are built with debug information. ls reports that rcc driver is 181kb, but that's not representative of what actually ends up being flashed. You can see a similar effect with desktop Rust software by running strip on the binaries, you'll often save a ton of size from doing that.
- fleventynine 5y agoMy language selection checklist: 1. Does the program need to be fast or complicated? If so, don't use a scripting language like Python, Bash, or Javascript. 2. Does the program handle untrusted input data? If so, don't use a memory-unsafe language like C or C++. 3. Does the program need to accomplish a task in a deterministic amount of time or with tight memory requirements? If so, don't use anything with a garbage collector, like Go or Java. 4. Is there anything left besides Rust?
- deleted 5y ago[deleted]
- Twisol 5y agoI suspect Ada would make the cut, with the number of times it's been referenced in these contexts, but I haven't actually taken the time to learn Ada properly. It seems like a language before its time.
- IshKebab 5y agoAs I understand it it's only memory safe if you never free your allocations, which is better than C but not an especially high bar. Basically the same as GC'd languages but without actually running the GC. It does have support for formal verification though unlike most languages.
- pyjarrett 5y ago> if you never free your allocations Technically, it used to be memory safe before this and they rolled back the restrictions to allow "unchecked deallocation". Pointers ("Accesses") are also typed, e.g. you can have two incompatible flavors of "Widget*" which can't get exchanged which helps reduce errors, and pointers can only point at their type of pointer unless specified otherwise, are null'd out on free automatically and checked at runtime. In practice, you just wrap your allocations/deallocations in a smart pointer or management type, and any unsafe usages can be found in code by looking for "Unchecked" whether "Unchecked_Access" (escaping an access check) or "Unchecked_Deallocation". The story is quite different in Ada because of access type checks, it doesn't use null-terminated strings, it uses bounded arrays, has protected types for ensuring exclusive access and the language implicitly passes by reference when needed or directed. My experience with writing concurrent Ada code has been extremely positive and I'd highly recommend it.
- ransom1538 5y agoDumb question: Do we need to use C++ anymore? Can we just leave it to die with video games? How many more years of this crap do we need before we stop using that language. Yes I know, C++ gurus are smart, but, you are GOING to mess up memory management. You are GOING to inject security issues with c/c++.
- alfalfasprout 5y agoC/C++ is great for AI/ML/Scientific computing because at the end of the day, you have tons of extremely optimized libraries for "doing X". But the thing is, in those use cases your data is "trusted" and not publicly accessible. Similarly in trading, C/C++ abounds since you really do have such fine manual control. But again, you're talking about usage within internal networks rather than publicly accessible services. For web applications, communications, etc.? I expect we'll see things slowly switch to something like Rust. The issue is getting the inertia to have Rust available to various embedded platforms, etc.
- josephg 5y agoI'm a big proponent of rust, but I doubt rust will displace nodejs & python for web applications. Web applications generally care much more about developer onboarding and productivity than performance. And for good reason - nodejs behind nginx is fast enough for the bottom 99% of websites. Rust is much harder to learn, and even once you've learned it, its more difficult to implement the same program in rust than it is in javascript. Especially if that program relies heavily on callbacks or async/await. For all the hype, I don't see rust taking off amongst application developers. Its a great language for infrastructure & libraries though. This bug almost certainly wouldn't have existed in rust.
- rocqua 5y agoIf im going to be making code that needs to run fast, works on a bit level, and isn't exposed to the world, then I am picking up C++ It's more convenient than C. It's easier to use (at the cost of safety) compared to Rust. Perhaps this will change if I know rust better. But for now C++ is where it's at for me for this niche.
- duped 5y ago> Or are C/C++ benefits just too high to ever give up? FFI is inherently memory-unsafe. You get to rewrite security critical things from scratch, or accept some potentially memory-unsafe surface area for your security critical things for the benefit that the implementation behind it is sound. This is true even for memory-safe languages like Rust. The way around this is through process isolation and serializing/deserializing data manually instead of exchanging pointers across some FFI boundary. But this has non-negligible performance and maintenance costs.
- smolder 5y ago> FFI is inherently memory-unsafe Maybe this specific problem needs attention. I wonder, is there a way we can make FFI safer while minimizing overhead? It'd be nice if an OS or userspace program could somehow verify or guarantee the soundness of function calls without doing it every time. If we moved to a model where everything was compiled AOT or JIT locally, couldn't that local system determine soundness from the code, provided we use things like Rust or languages with automatic memory management?
- hderms 5y agoPresumably as long as FFIs are based on C calling conventions and running native instructions it would be unsafe. You could imagine cleaner FFIs that have significant restrictions placed on them (I'd imagine sandboxing would be required) but if the goal is to have it operate with as little overhead as possible, then the current regime is basically what you end up with and it would be decidedly unsafe.
- duped 5y agoThis is a really hard problem because you have to discard the magic wand of a compiler and look at what is really happening under the hood. At its most rudimentary level, a "memory safe" program is one that does not access memory that is forbidden to it at any point during execution. Memory safety can be achieved using managed languages or subsets[1] of languages like Rust - but that only works if the language implementations have total knowledge of memory accesses that the program may perform. The trouble with FFIs is that by definition, the language implementations cannot know anything about memory accesses on the other side of the interface - it is foreign, a black box. The interface/ABI does not provide details about who is responsible for managing this memory, whether it is mutable or not, if it is safe to be reused in different threads, indeed even what the memory "points to." On top of that, most of the time it's on the programmer to express to the implementation what the FFI even is. It's possible to get it wrong without actually breaking the program. You can do things like ignore signedness and discard mutability restrictions on pointers with ease in an FFI binding. There's nothing a language can do to prevent that, since the foreign code is a black box. Now there are some exceptions to this, the most common probably being COM interfaces defined by IDL files, which are language agnostic and slightly safer than raw FFI over C functions. In this model, languages can provide some guarantees over what is happening across FFI and which operations are allowed (namely, reference counting semantics). The way around all of this is simple : don't share memory between programs in different languages. Serialize to a data structure, call the foreign code in an isolated process, and only allow primitive data (like file descriptors) across FFI bounds. This places enormous burden on language implementers, fwiw, which is probably why no one does it. FFI is too useful, and it's simple to tell a programmer not to screw up than reimplement POSIX in your language's core subroutines. [1] "safe" rust, vs "unsafe" where memory unsafety is a promise upheld by the programmer, and violations may occur
- jmull 5y agoWell, there's no time machine. Also, as far as I know, a full replacement for C doesn't exists yet.
- Gigachad 5y agoAre you suggesting that this crypto library would not be possible or practical to be built with rust? What features of C enable this library which Rust does not? There is no time machine to bring rust back to when this was created, but as far as I know, there is no reason it shouldn't be Rust if it was made today.
- jmull 5y agoIt's more of whether Rust fits into every workflow, project, team, build chain, executable environment, etc., that C does. Does rust run everywhere C runs? Does rust build everywhere C builds? Can rust fit into every workflow C does? Are there rust programmers with all the same domain expertise as for C programmers? (Not to mention, the question here isn't whether to write in rust or write in C. It's whether to leave the C code as-is -- zero immediate cost/time/attention or rewrite in rust -- a significant up-front effort with potential long term gains but also significant risk.)
- staticassertion 5y agoIt only matters if rust runs everywhere that Firefox runs, which it does.
- Gigachad 5y agoConsidering firefox already uses rust components, that seems a safe bet.
- josephg 5y agoRust does not run everywhere C runs. At least not yet - there's a couple efforts to allow rust to compile to all platforms GCC supports[1]. But we don't need rust to work everywhere C works to get value out of a native rust port of OpenSSL. Firefox and Chrome (as far as I know) only support platforms which have rust support already. As I said in another comment, in my experience, porting code directly between two C-like languages is often much faster & cheaper than people assume. You don't have to re-write anything from scratch; just faithfully port each line of code in each method and struct across; with some translation to make the new code idiomatic. 1kloc / day is a reasonable ballpark figure, landing us at about one person-year to port boringssl to rust. The downside of this is that we'd probably end up with a few new bugs creeping in. My biggest fear with this sort of work is that the fuzzers & static analysis tools might not be mature enough in rust to find all the bugs they catch in C. [1] https://lwn.net/Articles/871283/ https://lwn.net/Articles/871283/
- ipaddr 5y agoWhy not write it in a language not written yet?
- tristor 5y agoIt’s not lost on me that the organization that produced NSS also invented Rust. That implies the knowledge of this need is there, but it’s not so straightforward to do.
- Barrin92 5y agoI wondered this when I recently saw that flatpak is written in C. In particular for newer projects that don't have any insane performance constraints I wonder why people still stick to non memory-managed languages.
- Enhex 5y agothis is C code. stuff like void*, union and raw arrays do not belond in modern C++. while C++ is compatible with C it provides ways to write safer code that C doesn't. writing C code in a C++ project is similar to writing inline assembly.
- pjmlp 5y agoHere, https://android.googlesource.com/platform/ndk/+/refs/heads/master/sources/android/ndk_helper/JNIHelper.h#120 https://android.googlesource.com/platform/ndk/+/refs/heads/m...
- shp0ngle 5y agoAs I replied elsewhere. You don't need to explain to Mozilla about rewriting code from C/C++ to Rust.