10 ms·
Confession Of A C/C++ Programmer (2017)
- zabzonk 6y agoBut can you write "consistently safe" (whatever that may mean) programs in any other language?
- oytis 6y agoShould be a bit easier with Rust or Ada I think.
- deleted 6y ago[deleted]
- jerf 6y agoObviously, this depends on your definition of "safe". But there's a fairly large set of "safety" issues that, in 2021, effectively only C and C++ have. Other than straight assembler, which by its nature will always be with us in some sense, there aren't any other languages in common use anymore with similar memory safety issues. I'm not sure I can think of any other languages with "pointer arithmetic". Almost nobody else is using NULL-terminated strings. And so on and so on. (Please read that carefully. I'm not saying every language other than C and C++ are completely safe by any definition. I'm saying there are significant weaknesses that only C and C++ have nowadays. Threads unsafety, mutable state management issues, bad/unhelpful type systems, plenty of common unsafety out there today, but C/C++ have their own nearly-unique entries on that list.) C++ nominally has solutions to any given weakness, but in practice they're at the very least difficult to use in isolation, and very, very complicated to completely correctly use in combination with each other, to say nothing of code bases that inevitably end up having to deal with multiple solutions intersecting in the same code base because of two important libraries that have to do things differently or whatever.
- zabzonk 6y agoThere are lots of languages that are not "safe" in your terms. I'll limit them to ones I am fairly expert in - assembler, FORTH, Object Pascal, but there are many, many others.
- jerf 6y agoFORTH and Object Pascal are not in common use. Languages actually 100% dying are rare. I'm very, very confident that there are people out there working in them full time, and making a good living. But they are not even remotely in the same tier as Java, Python, Go, C++, C, Objective C, etc. Again, this depends on your definition of "common", but... can anyone tell me with a straight face that Java and FORTH are in the same class of usage? Also, I already called out assembler as an exception. By its nature it will always be unsafe. This is fine by me, because any restriction that it lays down will be something that no language above it can possibly get around, no matter how good an idea that may become in a future, which is dangerously restrictive over the long term. I don't look to assembler to provide language-level safety. I don't want assembler to, say, rigidly protect the "private" fields of objects from exterior access, because then my debuggers and state viewers and other tools become impossible.
- pjmlp 6y agoObject Pascal is definitly safer. - bounds checking - proper strings - strong typed enumerations - parameter references cannot be forced to nil (C++ ones can be tricked into null and C doesn't have them anyway)
- yakubin 6y agoIs there a spec for Object Pascal? I'm wondering what document I should follow, if I wanted to write a compiler for it. UPDATE: From what I see[1], there is a proposed standard, but no finalized one. [1]: <http://pascal-central.com/standards.html http://pascal-central.com/standards.html>
- bluetomcat 6y agoC is assembly on steroids which gives you just the essential features of an HLL, without any of the fancy stuff which would complicate the translation and would potentially require a runtime environment. Any C construct/operation has a canonical representation as a short series of instructions found on most machines. Essentially, what you get is expression-oriented syntax (operations are expressions yielding a result), support for structured programming (loops, conditionals), automatic register allocation and stack frame management through named local variables, abstraction for calling conventions and a rudimentary type system around integers and pointers.
- userulluipeste 6y ago"issues that, in 2021, effectively only C and C++ have" "I'm not sure I can think of any other languages with «pointer arithmetic». Almost nobody else is using NULL-terminated strings. And so on and so on." The thing is, at least a while ago, these were features that got C and C++ chosen for, precisely because they enable someone to go do things quick and dirty (mostly for "quick", with "dirty" only as an assumed consequence, but I've also seen both C and C++ for "dirty" alone plenty of times). We had safer languages (the one named after lovely Mrs. Lovelace, for instance), yet those options simply could not persuade the people that like it raw. Now we clearly have a stronger techno-political pressure lobbying for wearing straighter jackets, so at least it's interesting to watch.
- tuckerpo 6y agoPlethora of static analysis tools make it possible to write code that's "safe enough" I would imagine, for some value of "safe enough"
- felixguendling 6y agoDon't forget Sanitizers combined with comprehensive test suits. I would always recommend doing both, static and dynamic analysis.
- pjmlp 6y agoNow if people would actually use them. > Which of the following tools do you or your team use for guideline enforcement or other code quality or analysis? https://www.jetbrains.com/lp/devecosystem-2020/cpp/ https://www.jetbrains.com/lp/devecosystem-2020/cpp/ With the best value being 36%.
- staticassertion 6y agoThen why have we seen ITW exploits against Chrome, or Linux? These are C and C++ codebases that undergo tons of static analysis and testing - tons of research goes into both of those projects to make them safer. Still vulns. Still exploits.
- RcouF1uZ4gsC 6y agoI have found that most people who classify themselves as a “C/C++ programmer” are incompetent at both C and C++. There are many competent C and there are competent C++ programmers, but people who try to be C/C++ programmers end up with the worst of both worlds. They lose the simplicity of C without going far enough to gain the full benefits of C++.
- zabzonk 6y agoI agree that people that think there is a language called C/C++ probably don't have much of a clue, but it is certainly possible to program in both C and C++ and do a good job in both.
- flohofwoe 6y agoThe common subset of C and C++ is different enough from "idiomatic" C and C++ that it almost can be called a separate "C/C++" language ;)
- zabzonk 6y agoThe common subset should probably be called something like "better C" - it won't be able to use any major (or many minor) C++ features. Basically, it will be ANSI C (give or take). Noticeably, the code for the 2nd Ed of The C Programming Language was tested using a C++ compiler, not a C compiler
- flohofwoe 6y agoThe common C/C++ subset is an outdated and non-standard C though. For instance standard-conforming C++ compilers cannot compile C99 code and onward, they're stuck at a non-standard C version that would roughly be C95, minus some things that are valid C but not valid C++.
- pjmlp 6y agoThey can compile more C11 code than C99, thanks to the stuff dropped in C11. They also support most of the C standard library additions. As of C++20, the biggest difference is that designated initializers must follow field declaration order, restrict and _Generic aren't supported, and the type safety semantics are stronger.
- masklinn 6y ago> I've heard maybe Daniel J. Bernstein can DJB simply redefines his unsafe code as safe[0] and asserts that you're Using It Wrong: -- In May 2005, Georgi Guninski published "64 bit qmail fun", three vulnerabilities in qmail (CVE-2005-1513, CVE-2005-1514, CVE-2005-1515): http://www.guninski.com/where_do_you_want_billg_to_go_today_4.html http://www.guninski.com/where_do_you_want_billg_to_go_today_... Surprisingly, we re-discovered these vulnerabilities during a recent qmail audit; they have never been fixed because, as stated by qmail's author Daniel J. Bernstein (in https://cr.yp.to/qmail/guarantee.html https://cr.yp.to/qmail/guarantee.html): > This claim is denied. Nobody gives gigabytes of memory to each qmail-smtpd process, so there is no problem with qmail's assumption that allocated array lengths fit comfortably into 32 bits. Indeed, the memory consumption of each qmail-smtpd process is severely limited by default (by qmail-smtpd's startup script); for example, on Debian 10 (the latest stable release), it is limited to roughly 7MB. Unfortunately, we discovered that these vulnerabilities also affect qmail-local, which is reachable remotely and is not memory-limited by default (we investigated many qmail packages, and all of them limit qmail-smtpd's memory, but none of them limits qmail-local's memory). As a proof of concept, we developed a reliable, local and remote exploit against Debian's qmail package in its default configuration. This proof of concept requires 4GB of disk space and 8GB of memory, and allows an attacker to execute arbitrary shell commands as any user, except root (and a few system users who do not own their home directory). We will publish our proof-of-concept exploit in the near future. About our new discovery, Daniel J. Bernstein issues the following statement: > https://cr.yp.to/qmail/guarantee.html https://cr.yp.to/qmail/guarantee.html has for many years mentioned qmail's assumption that allocated array lengths fit comfortably into 32 bits. I run each qmail service under softlimit -m12345678, and I recommend the same for other installations. -- [0] https://www.openwall.com/lists/oss-security/2020/05/19/8 https://www.openwall.com/lists/oss-security/2020/05/19/8 PS: HN's markup keeps sucking and making comments unreadable
- knorker 6y agoIn other words the instructions on how to run qmail without security holes were clearly displayed in the bottom of a locked filing cabinet stuck in a disused lavatory with a sign on the door saying ‘Beware of the Leopard.
- 6y ago
- Out_of_Characte 6y agoProgramming today is fundamentally designed to function rather than be correct. correctness is the second stage that any programmer experiences when you rewrite a naive implementation and get a performance boost or behaviour that is less likely to fail. but really all that has happend is any first completely new implementation has unknown unknowns and rewriting it improves your code and turns them into known unknowns. but there is still tons of stuff that I dont understand that will fail for unknown reasons. There's just hopefully less of them.
- zoomablemind 6y ago> Programming today is fundamentally designed to function rather than be correct... As long as such code is coupled with reasonably complete tests for the expected behavior (think of fuzzing as one of such tests), it's a quite valid and "safe" approach. Of course, it's possible to write "unsafe" code, yet pass most of business-related tests. But this is equally possible with any programming language. Unsafe assumptions will lead to unsafe implementation.
- xuhu 6y agoRust looks like Facebook, while C++ looks like every publishing system out there (Wordpress, Django, Drupal, MediaWiki, static sites, etc). You're a lot more restricted on FB but you also can't host your online store, or really anything except: messages, groups, events, photos and videos. C++ could probably be more regulated for certain scenarios, but if people don't paint self-hosted blogs as evil, why paint C++ that way ?
- munchbunny 6y agoC++ could probably be more regulated for certain scenarios At that point, a different language might be better anyway. My experiences with C++ (I dust it off every year because of something that needs doing that nobody else wants to touch) are that I've usually dipped into C++ specifically for some combination of: 1. Invoking API's that have been around for a long time, usually meaning OS API's 2. Hand-optimized performance sensitive code where, for example, you're auditing every runtime memory allocation that has to be done in the inner loops 3. Modifying legacy code that's written in C++ In all of those cases you have to dip into unsafe territory regularly, so you end up with pretty much what Rust does with safe/unsafe, but with less in the way of the language helping you to ferret out your memory handling bugs. Every time I do it, even if I don't think I've created memory handling bugs, and even when my tools don't catch anything, I always leave with a low grade fear that my code will end up in a CVE somewhere.
- ivanbakel 6y agoCould you explain what you mean in more concrete terms? In what way does Rust restrict the programmer so much that it's comparable to the difference between an FB page and a site-building system?
- bilkow 6y agoThat's really hyperbolic, I don't believe there's anything you can do in C++ that you can't do in Rust. Your argument seems a lot more valid for Python, Java, as AFAIK in those languages you can't access raw memory. Do you have a specific example?
- 6y ago
- deleted 6y ago[deleted]
- aledthemathguy 6y agoLayman warning: I never really understood (as an outsider who does not code) why code can be unsafe. Is code more like art (painting) than math (writing an equation which balances itself)?
- lbrandy 6y agoSoftware is more like building a machine than either math or art. There've been attempts to make formally-provable programs (so it _is_ like math) but these are not in widespread use. Go watch the Lockpicking Lawyer on youtube pick locks and trivially crack/open every lock ever made. This is, roughly, the best physical analog to what happens with computer programs and safety. The creators are trying but they have to be correct everywhere, from every angle, and the attacker only needs to find one weakness to break it.
- extrememota 6y agoThe code can be unsafe because it is physically impossible to test for every input in a computer. This is where various engineering designs come in which reduce the area of testing based on some theories. Also, even in math, there are enough mistakes in publications (not just typos, but reasoning errors) which hopefully do not affect the eventual results in any fundamental way. The equivalent of safe code in computer science would be equivalent of completely formal proofs in mathematics (like in Coq and similar languages), but probably much more difficult due to existence of temporal conditions.
- Ar-Curunir 6y agoErr there are other ways to prove (memory) safety than exhaustive testing, such as better type systems and static analysis (Rust) or better run time checks (any garbage collected language)
- extrememota 6y agoMemory safety is not the only type of safety though. There are race conditions for example.
- lainga 6y agoHas this changed in 3 years? Was the author really writing about modern C++, or C++ as he remembers it? One of the comments on that post is pointing out the introduction of std::array with C++11.
- mumblemumble 6y agoStrictly speaking, it can never change. The features that create safety pitfalls are still there, and they can never be removed without breaking backward compatibility. And you can't simply avoid using those features, because their use is baked into the standard library, or into some other de facto standard library that you can't realistically live without.
- 0xTJ 6y agoEveryone makes mistakes, but trying to write correct code is important. You have to be conscious of the consequences of what you write. You shouldn't rely on non-fixed-width types having specific lengths or on signed integer overflow, unless your code guarantees that things will work (preprocessor/static_assert checks against standard/compiler-specific properties). The worst offenders I see often is violating aliasing rules, especially in ways that could cause alignment issues. In C, function isn't an object, you can't convert a function pointer as a void pointer and back, and use it. Yet I've seen that done many times, and it's probably safe on things that act as if they have a von Neumann architecture. If I'm relying on implementation-dependent functionality, I try to make it so that whatever it is will fail to compile instead of having incorrect behaviour.
- zabzonk 6y agoBetter than you being aware, make sure your build system is aware - for example, compile your code with the maximum level of warnings and errors turned on, and don't commit it until you have resolved them all.
- zoomablemind 6y ago+1. Yet this should be done from the very start of the project. Too often projects start in a permissive way, and then get released. At that point switching to strict way may become an unsurmountable task practically or politically.
- 0xTJ 6y agoDefinitely, `-Wall -Wextra -pedantic` are always there, and only turning off individual ones if there's a good reason.
- gpderetta 6y ago> In C, function isn't an object, you can't convert a function pointer as a void pointer and back, and use it. It is not defined by the C standard, but POSIX requires it. So any environment (including the compiler) claiming POSIX conformance must support it. Not sure how it works on Itanium though as, IIRC, function pointers there are twice as wide as normal pointers.
- password321 6y agoI'm guessing this post is another low-key Rust promotion.
- pjmlp 6y agoThere are plenty of safe alternatives.
- beached_whale 6y agoOne nice thing about newer C++ is that you can write safer(in some sense) functions. To a point. constexpr/consteval functions that are tested at compile time do not have UB and do not interact with global state. This is enforced by the compiler. So building up from this, you get safer systems. One cannot say they are safe though, as it is complicated. This comes back to is the whole system safe, what do I trust, and how have I mitigated my mistakes(there are always mistakes). No purported safe system will stop me from making logic errors in my conditionals. They will stop an access outside a valid objects or ranges. But C++ also has tools to mitigate this, is a for loop good here or should it be a function that works on a range and is tested properly. Can I enforce my constraints in a type so that the compiler does the work for me.
- dvfjsdhgfv 6y ago> One nice thing about newer C++ is that you can write safer(in some sense) functions. But in practice you rarely find these in the codebase you inherit.
- beached_whale 6y agoThat goes for any system, legacy code is good and bad. Often it's battle hardened, but looks super complicated because of the post release organic growth with fixes. Green projects are able to be engineered nicely(if you have the time...) but are missing real world exposure to find all our incorrect assumptions and errors.
- Nursie 6y ago>> I cannot consistently write safe C/C++ code. I think it's going to depend on your attack surface. Could I sit down and write a web-facing server from scratch and have a reasonable expectation of safety? Probably not. Can I write a piece of firmware for an embedded device with no internet access and a very limited serial protocol, and have a reasonable expectation of that being 'safe'? That seems more likely, particularly if I follow good practice and use available analysis tools where possible. I think the biggest problem with these languages is that when something goes wrong (as it so often does), the keys to the kingdom are up for grabs as the whole of everything can suddenly be manipulated, to read out arbitrary memory (e.g. heartbleed)
- mumblemumble 6y agoIt almost goes both ways. Yes, being on an embedded device with no Internet access reduces your external attack surface. But it also limits your ability to use lots of newer language features designed to improve safety, because those features often cost CPU cycles or memory. My (completely uninformed) guess is that most of any net benefit from being on an embedded system comes from the simple fact that "embedded" implies "constrained", which implies "less code," which, in turn, implies "fewer opportunities to create bugs in the first place."
- Nursie 6y agoIt certainly plays into it - fewer moving parts means less opportunity for things going wrong.
- MarcusE1W 6y agoI don’t think that you are limited regarding newer language features when you develop for embedded devices or limited in the use of more safety focused languages. E.g. Ada, despite being in the older age for computer languages was specifically designed with embedded programming and safety features in mind and does this quite good. It also supports OOP if you want that sort of thing and more crucially has pretty good concurrency support build into the language while still being reasonably safe. With SPARK/Ada you even get a language variant that can be formally proven. To name some more modern use cases.
- OnlyOneCannolo 6y ago> I cannot consistently write safe C/C++ code. There's a pretty big ecosystem of tools and techniques beyond just the compiler for writing safe code. Unfortunately, they all have usability issues, so the burden of writing safe programs often falls almost entirely on the individual.
- lasagnaphil 6y agoValgrind helps a lot though, it usually solves my memory errors. (If that doesn't do it, then it's either just a logic bug, or you have a really strange bug in the first place!) The main issue people have with it is that its terminal interface is almost always unusable (you get thousands of lines of gibberish text), and there aren't many good Valgrind GUI frontends available. Thankfully CLion has an integrated valgrind inspector that really helps a lot.
- OnlyOneCannolo 6y agoValgrind is good but it's a dynamic tool, so it won't catch anything you don't exercise. There are better options, but there are usually some extra barriers to using them. By usability I also meant getting it to work for your setup and learning how to use it.
- dang 6y agoDiscussed at the time: https://news.ycombinator.com/item?id=14785867 https://news.ycombinator.com/item?id=14785867