1,914 karma · joined January 20, 2011
In this case, the article is not about 'the joys of being too dumb to breathe'. It's about how 1. looking stupid is not the same as being stupid, and 2. looking stupid can be beneficial in the long run. The author does not need to actually be stupid once in order to support this idea.
And I have to worry if you think he's "bragging" about merely looking stupid, as if that weren't bad enough. Maybe if you identify as stupid I could understand the offense.
To the author, Dan Luu: I like your article and I think you're on the right track!
I don't know specifically how to fix the standard, although I've been thinking about it. A simple idea would be like the Linux mandate "don't break userspace." The language-lawyering has to stop, and more rules are unlikely to help.
The more people beat the standard drum, the worse things will get until the standard itself is fixed.
Other languages that don't have a standard don't have this problem (but they do have other problems).
That said the compiler isn't infinitely smart (thank god) and complex null derefs will "safely" make it to runtime. (What a sad world we're in.)
p2 = p1; p1 = NULL;
However if you are intentionally doing multiple ownership, then yes you can still have problems.Sorry, my mistake!
Edit to add: futures work badly in every language, so there's no shame in the borrow checker not working with them.
Edit 2: But in that case we're back to "why would Rust want async/await over (potentially green) threads with its first-class support for locks?"
I'm not saying locks are better than async/await (although they are[1]). You're saying the borrow checker itself can't handle them in real world use?
[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h...
I can see an argument for wrapping it in a macro so you can turn off nulling in debug builds (ASan might even have hooks so you can automate this, I know Valgrind does). But use-after-free is worse than just double-frees, and if you read a dangling pointer in production there's no real way to catch it AFAIK. Last I heard (admittedly been a few years since I checked), you're not supposed to deploy ASan builds because they actually increase the attack surface.
So, your program's memory is full of these dangling pointers, and at some point you will have a bug you didn't catch and use one. And you can't even write an assertion to check that it's valid. What do you propose?
And again to clarify, I'm not trying to advocate for hiding bugs. I want to catch them early (e.g. with assertions), but I also want to avoid reading garbage at runtime at all costs, because that's how programs get pwn'd.
Maybe someone else could figure out how to do it properly, since I'd definitely prefer a function.
if(x) MACRO();
else something();
expands to if(x) { ... }; // Error!
else something();If the extra code is really that bothersome, just use a macro or wrapper function.
#define FREE(ptrptr) do { \
__typeof__(ptrptr) const __x = (ptrptr); \
free(*__x); *__x = NULL; \
} while(0)
There might be a better way of doing it though. Also, __typeof__() obviously isn't standard C.Edit to add: I've honestly been moving away from using a macro and just putting both statements on one line like in the OP. For something so simple, using a macro seems like overkill.
But I'll also say that if you don't have any use-after-free bugs in the history of a large C codebase... you might not even be on the lookout for them? I still have them sometimes, mainly when it comes to multiple ownership. And those are just the ones I found eventually.
So yes, different strokes for different folks, but if you make the effort to incorporate tricks like this into your "unconscious" coding style, the ongoing effort is pretty minimal. Even if you decide this trick isn't worth it, there are countless others that you might find worthwhile. I'm always on the lookout for better ways of doing things.
Also, it's not just about vulns in security-critical code. It's also about ordinary bugs. Why not be a little more careful? It won't hurt.
> It might get unzeroed if you work with the memory.
Maybe, but it isn't very common. I'm not sure when the C standard allows changing padding bytes, but in practice the compilers I've used don't seem to do it. And again, it's just a debugging aid, if it causes too much trouble on some platform, just turn it off.
I use every tool at my disposal. Sanitizers, static analyzers... and also not leaving dangling pointers in the first place. Why would I do anything less? It doesn't cost anything except a little effort.
Take a look at this recent HN link: https://www.radsix.com/dashboard1/ . Look at all those use-after-free bugs. Even if it only happens 1% or 0.01% of the time... It's a huge class of bugs in C code. Why not take such a simple step?
The trick of checking that buffers are zeroed is purely a debugging tool, so it's okay if it doesn't work on some platforms. And if you allocate with calloc(), the padding will be zeroed for you. It's actually very rare that you will have to call memset() with this technique.
*outArg = myPtr; myPtr = NULL;
free(aPtr); aPtr = NULL;
Set your pointers to null when you free them! Set them to null when you transfer ownership! Stop leaving dangling pointers everywhere!Some people say they like dangling pointers because they want their program to crash if something is freed when they don't expect it to be. Good! Do this:
assert(ptr);
There are also many more tricks you can do once you start nulling pointers. You can use const to mark pointers that you don't own and thus can't free. You can check that buffers are all zero before you free them to catch memory leaks (this requires zeroing out other fields too of course).Please, null out your pointers and stop writing (most) use-after-free bugs!
Turns out "Rewrite it in Rust" is actually really hard when you have millions of lines of code. Even Google probably can't rewrite Chrome from scratch.
Maybe if we just try harder, communism will work. You gotta draw the line somewhere.
Let's say you send a yo now, and then you're going to send another one in 5 minutes. Then you decide to send a third in between. How much information does it contain? Less than a full timestamp's worth, because the timing is constrained.
Once you are sending yo's at the maximum line rate (certainly at least every nanosecond, it's such a critical piece of infrastructure), sending or not sending an individual yo is down to one bit of information. The time component is basically completely gone.
In other words, you cannot know the precise position of a yo at the same time as knowing the precise momentum.