user2793162
user2793162

Reputation:

Evaluating the condition containing unitialized pointer - UB, but can it crash?

Somewhere on the forums I encountered this:

Any attempt to evaluate an uninitialized pointer variable
invokes undefined behavior. For example:

int *ptr; /* uninitialized */
if (ptr == NULL) ...; /* undefined behavior */

What is meant here? Is it meant that if I ONLY write:

if(ptr==NULL){int t;};

this statement is already UB? Why? I am not dereferencing the pointer right? (I noticed there maybe terminology issue, by UB in this case, I referred to: will my code crash JUST due to the if check?)

Upvotes: 2

Views: 210

Answers (7)

supercat
supercat

Reputation: 81189

Some implementations may be designed in such a way that an attempted rvalue conversion of an invalid pointer may case arbitrary behavior. Other implementations are designed in such a way that an attempt to compare any pointer object with null will never do anything other than yield 0 or 1.

Most implementations target hardware where pointer comparisons simply compare bits without regard for whether those bits represent valid pointers. The authors of many such implementations have historically considered it so obvious that a pointer comparison on such hardware should never have any side-effect other than to report that pointers are equal or report that they are unequal that they seldom bothered to explicitly document such behavior.

Unfortunately, it has become fashionable for implementations to aggressively "optimize" Undefined Behavior by identifying inputs that would cause a program to invoke UB, assuming such inputs cannot occur, and then eliminating any code that would be irrelevant if such inputs were never received. The "modern" viewpoint is that because the authors of the Standard refrained from requiring side-effect-free comparisons on implementations where such a requirement would impose significant expense, there's no reason compilers for any platform should guarantee them.

Upvotes: 0

SzG
SzG

Reputation: 12619

You're not dereferencing the pointer, so you don't end up with a segfault. It will not crash. I don't understand why anyone thinks that comparing two numbers will crash. It's nonsense. So again:

IT WILL NOT CRASH. PERIOD.

But it's still UB. You don't know what memory address the pointer contains. It may or may not be NULL. So your condition if (ptr == NULL) may or may not evaluate to true.

Back to my IT WILL NOT CRASH statement. I've just tested the pointer going from 0 to 0xFFFFFFFF on the 32-bit x86 and ARMv6 platforms. It did not crash.

I've also tested the 0..0xFFFFFFFF and 0xFFFFFFFF00000000..0xFFFFFFFFFFFFFFFF ranges on and amd64 platform. Checking the full range would take a few thousand years I guess.
Again, it did not crash.

I challenge the commenters and downvoters to show a platform and value where it crashes. Until then, I'll probably be able to survive a few negative points.

There is also a SO link to trap representation which also indicates that it will not crash.

Upvotes: -2

LihO
LihO

Reputation: 42093

Using unitialized variables invokes undefined behavior. It doesn't matter whether it is pointer or not.

int i;
int j = 7 * i;

is undefined as well. Note that "undefined" means that anything can happen, including a possibility that it will work as expected.


In your case:

int *ptr;
if (ptr == NULL) { int i = 0; /* this line does nothing at all */ }

ptr might contain anything, it can be some random trash, but it can be NULL too. This code will most likely not crash since you are just comparing value of ptr to NULL. We don't know if the execution enters the condition's body or not, we can't be even sure that some value will be successfully read - and therefore, the behavior is undefined.

Upvotes: 3

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16026

As Shafik has pointed out, the C99 standard draft declares any use of unintialized variables with automatic storage duration undefined behaviour. That amazes me, but that's how it is. My rationale for pointer use comes below, but similar reasons must be true for other types as well.

After int *pi; if (pi == NULL){} your prog is allowed to do arbitrary things. In reality, on PCs, nothing will happen. But there are architectures out there which have illegal address values, much like NaN floats, which will cause a hardware trap when they are loaded in a register. These to us modern PC users unheard of architectures are the reason for this provision. Cf. e.g. How does a hardware trap in a three-past-the-end pointer happen even if the pointer is never dereferenced?.

Upvotes: 1

Kenneth Wilke
Kenneth Wilke

Reputation: 4669

The behavior of this is undefined because of how the stack is used for various function calls. When a function is called the stack grows to make space for variables within the scope of that function, but this memory space is not cleared or zeroed out.

This can be shown to be unpredictable in code like the following:

#include <stdio.h>

void test()
{
    int *ptr;
    printf("ptr is %p\n", ptr);
}

void another_test()
{
    test();
}

int main()
{
    test();
    test();
    another_test();
    test();
    return 0;
}

This simply calls the test() function multiple times, which just prints where 'ptr' lives in memory. You'd expect maybe to get the same results each time, but as the stack is manipulated the physical location of where 'ptr' is has changed and the data at that address is unknown in advance.

On my machine running this program results in this output:

ptr is 0x400490
ptr is 0x400490
ptr is 0x400575
ptr is 0x400585

To explore this a bit more, consider the possible security implications of using pointers that you have not intentionally set yourself

#include <stdio.h>

void test()
{
    int *ptr;
    printf("ptr is %p\n", ptr);
}

void something_different()
{
    int *not_ptr_or_is_it = (int*)0xdeadbeef;
}

int main()
{
    test();
    test();
    something_different();
    test();
    return 0;
}

This results in something that is undefined even though it is predictable. It is undefined because on some machines this will work the same and others it might not work at all, it's part of the magic that happens when your C code is converted to machine code

ptr is 0x400490
ptr is 0x400490
ptr is 0xdeadbeef

Upvotes: 0

Shafik Yaghmour
Shafik Yaghmour

Reputation: 158499

The C99 draft standard says it is undefined clearly in Annex J.2 Undefined behavior:

The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.8, 6.8).

and the normative text has an example that also says the same thing in section 6.5.2.5 Compound literals paragraph 17 which says:

Note that if an iteration statement were used instead of an explicit goto and a labeled statement, the lifetime of the unnamed object would be the body of the loop only, and on entry next time around p would have an indeterminate value, which would result in undefined behavior.

and the draft standard defines undefined behavior as:

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

and notes that:

Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

Upvotes: 2

Chris Maes
Chris Maes

Reputation: 37752

your pointer is not initialized. Your statement would be the same as:

int a;
if (a == 3){int t;}

since a is not initialized; its value can be anything so you have undefined behavior. It doesn't matter whether you dereference your pointer or not. If you would do that, you would get a segfault

Upvotes: 2

Related Questions