For a change of page, today on the Coverity Development Testing Blog’s continuing series Ask The Bug Guys I’ll talk about mostly C and C++, with a little Java and C# thrown in at the end. I’ll discuss a very common question I see on StackOverflow in the “C” and “C++” tags: “here’s a clearly buggy program that I wrote; why does it not AV / segfault / crash when I run it?” Check it out!
As always, if you have questions about a bug you’ve found in a C, C++, C# or Java program that you think would make a good episode of ATBG, please send your question along with a small reproducer of the problem to TheBugGuys@Coverity.com
. We cannot promise to answer every question or solve every problem, but weβll take a selection of the best questions that we can answer and address them on the dev testing blog every couple of weeks.
Er… Where is the post?
Whoops, the link is broken! I’ll fix it!
Yay! I knew the answer to the “why does my code not crash?” as soon as I read the question. Makes me feel like maybe I’m not so rusty on the low-level stuff, despite years of focusing on C# π
“Because it’s not obliged to crash, silly”.
Really, any course of C/C++ language that doesn’t start with explaining that a) UB isn’t obliged to crash; b) compilers aren’t obliged to detect UB; c) There are tons of innocently looking code that’s actually UB β is not putting the listener into a right mindset..
The right mindset being “My god, why would anyone write application code in a language like that?” π
I agree. Or at least, if you do have some reason to use such a language, to voluntarily limit yourself to not use some language constructs.
I’ve written plenty of app code and server code – even C and C++ in performance-critical systems – and never once felt tempted to use pointer arithmetic or out-of-bounds array indices or anything like that.
Indeed. A tradesman does best when using tools he/she likes and has plenty of practice and experience with. Yet sometimes the task calls for something outside the box. I destroyed plenty of innocent blocks of timber when learning to use a circular saw. My house is not production quality but it’s “live”…ah I feel so cargo cult!
It’s worth noting that while UB is deliberately allowed to exist in C, not all languages and frameworks have it. Some languages and frameworks have rigidly defined semantics in all cases, while others will permit flexibility in some cases but require an implementation to select from among a list of options. For example, an language/framework combination could specify that `int x = 65536; log y=x*65535;` would be allowed to fail compilation, trap at run-time, set y to 4294901760 (the arithmetically-correct value of the product), or set y to -65536 (the wrapped 32-bit two’s-complement value, sign-extended to 64 bits), but would not be allowed to format anyone’s hard drive nor set `y` to 42.
While allowing UB has historically had some performance benefits, there can be substantial advantages–especially in security-related code–to having a language and framework be more specific about what will happen when code attempts various forbidden operations.
Absolutely. Emphasis on “historically”.
Pingback: Dew Drop – May 8, 2014 (#1772) | Morning Dew
It’s all Microsoft’s fault. π All Microsoft C/C++ compilers I’ve ever worked with would by default initialise an uninitialised pointer to null so of course, any attempt to dereference it would result in AV.
So when people switch to another, proprietory compiler (in my case, it was a not-yet-smart mobile phone we worked on in 2004) they expect the same behaviour (not knowing anything about what’s happening under the bonnet when you dereference a pointer, of course). That’s why one of my younger colleagues was very surprised that the code below (I hope WordPress won’t eat it up π ) worked 3 times out of five, and every time it crashed it did so in a different, unpredictable way:
DodgyStrucure* ds;
ds->Member = something;
When I told her that she needs to use malloc or just declare a variable instead of a pointer, she was like, ‘but I’ve done this before and it worked…’
…I don’t envy the people who bought those phones! π
The fundamental evil, of which many compiler-vendors are guilty, is allowing “normal” pointer arithmetic on a null pointer to yield a non-null pointer, even when “null-pointer” checks are supposedly enabled. I suspect this may stem from a fact that in some systems a data structure exists at address zero which some code may need to access (at least read), and there’s no way to distinguish pointers whose address should be regarded as legitimate even if it happens to be zero, from common “nullable” pointers, which should trap on any operation including pointer arithmetic.
Oh yes, the late MS DOS comes to mind! π