Yes, as Hoare stated in 1981, regarding Algol design:
"The first principle was security: The principle that every syntactically incorrect program should be rejected by the compiler and that every syntactically correct program should give a result or an error message that was predictable and comprehensible in terms of the source language program itself. Thus no core dumps should ever be necessary. It was logically impossible for any source language program to cause the computer to run wild, either at compile time or at run time. A consequence of this principle is that every occurrence of every subscript of every subscripted variable was on every occasion checked at run time against both the upper and the lower declared bounds of the array. Many years later we asked our customers whether they wished us to provide an option to switch off these checks in the interests of efficiency on production runs. Unanimously, they urged us not to - they already knew how frequently subscript errors occur on production runs where failure to detect them could be disastrous. I note with fear and horror that even in 1980, language designers and users have not learned this lesson. In any respectable branch of engineering, failure to observe such elementary precautions would have long been against the law."
Note: Not to mention Unisys still makes tons of money off their ALGOL machines from Burroughs. IBM & Fujitsu still service PL/1. So, old ones too.
And at least in C++, thanks to the STL or similar library there is the option to always enable bounds checking.
So even if the market didn't got hold of Algol, except for C and languages that offer copy-paste compatibility with it, all other ones do offer sane defaults in terms of security.
I've always liked that part. Once they saw the problems, they'd put up with whatever sacrifices it took to avoid them.
We've had some cargo culted patches to call _mesa_error_no_memory() when malloc fails, but it was recently noted that this can't possibly work because it internally calls fprintf, and there's no guarantee that fprintf doesn't call malloc! In fact, glibc's implementation does.
Suggestions for best practices welcome.
Of course, it would be better to avoid getting into a situation where either a graceful return isn't possible, or poorly-tested recovery code has to be run to deal with the failure. I read somewhere that seL4's strategy is to divide the work in two phases. The first phase allocates all the resources it will need and checks all preconditions, but changes no state; the second phase has no allocations, does all the work, and will never fail. That way, any error recovery is a simple release of the resources it had already allocated.
You can also use that wrapping technique to test _mesa_error_no_memory(), by setting the state to "all mallocs fail" and then calling it. Hint: Don't call fprintf, don't use stdio, call write() directly.
Fault injection can't cover all of the combinations of all inputs and all places to fail, but it's a lot better than nothing.
One (naughty) option is to malloc a decent sized buffer at startup (50MB say), and then when malloc fails free it, and immediately display a warning box.
One most modern 64-bit systems, malloc will never fail anyway, you'll get killed due to memory overcommit.
An abort with a reason is a far better choice.
Library code should in general punt out-of-memory conditions back to the caller.
It is if you ever want to exercise the code paths. Overcommit means you never get an out of memory error. Your process just dies. Kind of like it would if you just aborted.
...but that doesn't even really matter, because in order to fully exercise those code paths you need precise control over exactly which call to malloc() returns NULL, which in practice means you need to interpose malloc() with a debugging framework to force a NULL return.
Except if a virtual memory resource limit is set ("help ulimit").
$ sh -c 'ulimit -S -v 1000 ; perl -e 1'
Segmentation fault
$ sh -c 'ulimit -S -v 2000 ; perl -e 1'
perl: error while loading shared libraries: libm.so.6: failed to map segment from shared object: Cannot allocate memory
$ sh -c 'ulimit -S -v 20000 ; perl -e "qw{a}x100000000"'
Out of memory!wouldn't a simple 'ulimit -m' set appropriate limits on memory for the process ? and then use that to simulate failures, and hopefully see what breaks, fix it, and rinse-lather-repeat ?
edit-1: this is ofcourse predicated on the fact that you are using a unix like system.
To be clear, in systems designed with the "worse is better" approach, responsibility for correctness can be pushed up the stack, from e.g. kernel programmers to user-space programmers, if doing the Right Thing lower in the stack is hard. The example given in the paper is a syscall being interrupted -- in unix, it just fails with EINTR, and it's the application author's responsibility to retry. (I agree that malloc returning null is similar.)
But worse is better isn't saying that programs shouldn't be correct if it's complicated; it's just a question of where the responsibility for correctly handling API edge cases should lie.
Moreover, if one reads the "worse is better" paper carefully, you'll notice he doesn't say that "worse is better" actually is better, overall. The argument is only that it has better "survival characteristics". Meaning, as I read it, that they can gain and maintain momentum against competitors because they can move faster.
Which, tangentially, is all very interesting to me, because the three modern systems with the best survival characteristics are unix, C, and the web, the latter of which is definitely not worse is better. (However bad you may think the web is as an API, "worse is better" is a design philosophy about giving users simple, low-level APIs, whereas the web is all about high-level APIs where the browser implements lots of logic to prevent you from shooting yourself in the foot.) So maybe we need to reconsider the conclusions of the worse is better paper in light of the giant counterexample that is the web.
Modula-3 or maybe Eiffel would be closer to Right Thing examples in that category.
The right answer is, I think:
* Don't add checking code at individual malloc call-sites.
* If you have an allocation regime where you need to do something better than abort in response to failure, don't use malloc directly for those allocations.
* Run your program with malloc rigged to blow up if it fails.
In an ideal world, someone who did that should be taken out back and shot. In this real world, someone who did that should be immediately walked out the door.
It's astonishing to me just how lazy programmers can be.