Hacker News new | past | comments | ask | show | jobs | submit login

> I would much rather have my computer run slower than have to continuously deal with security vulnerabilities. My time is much more valuable than CPU time.

To be devil’s advocate and take the other side of this argument: I would rather the CPU I paid for spend its cycles performing actual application logic. Every cycle spent executing all this “safety code” overhead feels like me having to pay for a developer’s sloppiness.

I feel end users pay a high cost for all this runtime checking and all these frameworks and levels of abstraction.




First, not all language safety features have to manifest themselves as run-time checks. A properly designed language will allow many safety checks to be done at compile-time.

And second, would you really rather deal with security holes on an on-going basis?

The problem with C is not that you can write unsafe code in it, it is that the design of the language -- and pointer aliasing in particular -- makes it impossible to perform even basic sanity checks at compile time [EDIT: or even at run-time for that matter]. For example:

int x[10];

...

int y = x[11];

In any sane language, that would be a trivially-checkable compile-time error. But in C it is not because there are all kinds of things that could legally happen where the ellipses are that would make this code not violate any of C's safety constraints. That is the problem with C, and it is a fundamental problem with C's design, that it conflates pointers and arrays. C was designed for a totally different world, and it is broken beyond repair.


I'm not here to defend C, but to point out a problem that many advocates make when they cherry pick examples of how their language is both safer and more performant than C. Simply put, that example is irrelevant when the size of the array is not known at compile time. The moment that you have a dynamically allocated array, you are either dropping safety (e.g. expecting the developer to perform bounds checks when necessary) or performance (e.g. the compiler inserts bounds checks at runtime).

It is also worth considering that there is nothing preventing C compilers from inserting compile time checks to address examples such as yours. I just tried to compile a similar example in gcc and it does catch the unsafe code with warnings and the optimizer turned on.


> The moment that you have a dynamically allocated array, you are either dropping safety (e.g. expecting the developer to perform bounds checks when necessary) or performance (e.g. the compiler inserts bounds checks at runtime).

Yes, that's true. So? The original claim is that it is self-evident that this tradeoff should be resolved in favor of performance in the design of the language, and it just isn't (self-evident). If anything, it is self-evident to me that the tradeoff should be resolved in favor of safety in today's world.


There are patterns, though, that can help. C compilers are capable of recognising and optimising many forms of iteration, but being able to tell the compiler explicitly that you're iterating over a collection gives you that much more confidence that the compiler will do the right thing.

Especially when to get the compiler to do the right thing safely you need to add manual bounds checking with the expectation that the compiler will optimise it away, but without any mechanism to ensure that it actually happens.

It depends greatly on the problem at hand, but there are definitely cases where even with a dynamic array size we can unroll our loop such that we check bounds less than once per iteration.


Bad example. There's nothing[0] you could put in the ellipsis to make that code valid, and both gcc and clang will warn about it (clang on defaults, gcc with -Wall).

[0] Ok, I guess you could #define x something, but that's not interesting from a static analysis perspective.


Warning is one thing, but crashing is better. That's possible to do in a C compiler too of course, because in this example the array hasn't decayed to a pointer and its size can be recovered.

The issue is when you pass the array to another function, it can't track the size without changing the ABI.


If the choice is between a compile time warning and a runtime crash, I will take the warning every single time: much closer to the actual error. You're probably asking for a compile time error instead.

Indeed the `-Werror` option is often a good thing to have (though I don't set it by default on my free software projects, because other people might use other compilers with different warnings, and I don't want to block them outright).


-Werror is an interesting case -- it's an example of a key difference between C and Rust.

Rust's compiler will reject programs unless it can prove them to be valid. C compilers will accept programs unless they can prove them to be invalid. But then C warnings can lead to an indeterminate state: code that looks iffy may be rejected, but we've not necessarily proven that the code is wrong. We're still trusting the programmers' claim that code which may exhibit undefined behaviour with certain inputs won't ever receive those inputs.


I meant a crash. Obviously both at once is best, but you can detect the possibility of the crash at compile time (disassemble your program and see the bounds check), and it turns a possible security issue into a predictable crash so that’s safer.

I don’t really love forcing errors; when a program is “under construction” you should be able to act like it is and not have to clean up all the incomplete parts. It also annoys people testing new compilers against your code.


> Indeed the `-Werror` option is often a good thing to have (though I don't set it by default on my free software projects, because other people might use other compilers with different warnings, and I don't want to block them outright).

Another problem with C. There's way too much implementation-dependent behavior.


Not with Monocypher. That project has one implementation defined behaviour (right shifts of negative integers), and it's one where all platforms all behave exactly the same (they propagate the sign bit). In over 5 years, I haven't got a single report of a platform behaving differently (which would result in public key crypto not working at all).

However I do get spurious warnings, such as mixing arithmetic and bitwise operations even in cases where that's intended. Pleasing every compiler is not trivial.


Once upon a time you could do this:

int z[20];

x = z;

because arrays were formally equivalent to int*'s.

But apparently things have changed since the last time I looked.


What do you suppose you could put in the ellipsis that would make your second statement defined behavior other than preprocesor stuff or creating a new scope with a different variable named x? (Neither of which would frustrate a compiler wanting to give you a warning)


The real overhead is not safety code but inefficient design and unnecessary (and often detrimental to the user) features. The added 10% of safety checks is nothing compared to orders of magnitude of bloat modern software has. Moreover, a lot of C codebases have the same safety checks manually implemented into them, sacrificing performance in order to have less bugs. And when it comes to designing a modern C alternative, it's not just a performance/safety tradeoff, the are parts of C that are simply bad, like null-terminated strings, and are long due to be replaced with something better.


I'm suspicious that people arguing that safety checks making things slow are basing that on the very stale now idea that CPU's work sequentially. Which isn't true for super scalar machines at all. And also forgetting compilers will optimize away a lot of them.

There is an asymmetrical risk here. On one hand the compile might emit checks that aren't needed. On the other that the programmer will fail to insert a check that is needed.


Both yours and parent arguments are strong and reasonable. Compared with other overhead that developers deliberately add to their programs, language safety checks are probably a drop in the bucket. We're in a world where developers think it's reasonable to run a chat app on top of an entire browser framework on top of the platform SDK on top of the OS. The runtime performance of checking an array's bounds are the least of our concerns.


>I would rather the CPU I paid for spend its cycles performing actual application logic.

The problem is that for CPU programming errors are application logic too and they are run, cf. protected memory. You don't run everything in ring0 do you?


Is Rust slower then C in general though? And how much are you paying? Probably in true terms it costs less if there are fewer bugs and vulns.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: