Live data from Hacker News

I Do Not Know C: Short quiz on undefined behavior (2015)

kukuruku.co

151–160 of 189 posts

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#151
post #145

Earlier quoted context omitted.

A common class assignment or interview question is to write your own memcpy. Towards the end you usually start optimizing it by copying multiple bytes at once. That is undefined behavior. You cannot just cast a pointer to uint32_t* and start using it, unless the underlying object is actually uint32_t. In practice it works fine, so people don't care. We'll see what future compilers will do, especially when the homemad…

You can cast from char* to uint32_t* and start using it. I forget the exact standardese, but there is an execption for char*.

Only if the pointer is correctly aligned for the uint32_t data type. Otherwise you might get problems with unaligned memory acesses. (Like when you get some data over the wire that is clearly just a memory dump of a C struct, so you just do a pointer cast. Boom, unaligned read.)

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#152

Earlier quoted context omitted.

If you don't like the "ridiculous" standard, maybe you shouldn't be writing the language that it defines. There are plenty of discussions online what parts of the standard should be changed to get a "friendly C" [1], unfortunately there is no consensus that could be implemented. [1] http://blog.regehr.org/archives/1287

My prediction is that the standard will eventually follow the change in practice and eliminate that compiler "optimization".

In order to do that, the standard would have to define that dereferencing a null pointer must produce a deterministic behavior. There are only two possible behaviors:

1. The program successfully reads/writes that memory location and retrieves/overwrites whatever is there without crashing. Then the program can continue on and execute the if even if the pointer was null.

2. The program crashes immediate whenever a null pointer is read/written.

#1 is problematic, because NULL is a single pointer value that can be applied to a variety of pointer types. What happens if you first write to (long )NULL and then read from (FILE )NULL?

#2 is very useful, and most platforms already crash any program that tries to read or write NULL. But if the standard requires this behavior, then this introduces an even stronger guarantee that a dereferenced pointer is not null, so there's no reason to remove that optimization.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#153

Earlier quoted context omitted.

It means that empty loops (loops with empty bodies) can be completely removed if the controlling expression has no side effects. > This is intended to allow compiler transformations such as removal of empty loops even when termination cannot be proven. It means while(i) {} can be eliminated as if i were 0, because there are no side effects in the loop expression or the loop body, and what would be the point of the lo…

It's quite common in embedded systems to have the fault handler end with an infinite loop, to give the programmer a chance to attach a debugger an inspect the call stack. Sometimes this behavior is turned on or off with a debug flag, which can trigger this unexpected optimization if the flag is not a compile time constant.

You could also add a volatile declaration

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#154
post #124
post #52

Earlier quoted context omitted.

Under what set of logic does being able to de-reference a pointer confer that it's value is not 0 (which is what the test equates to)? Normal deductive logic? * No NULL pointer can be dereferenced. * x is dereferenced. * Therefore, x is not a NULL pointer. Of course, the compiler is presuming that your code is correct. That's a reasonable presumption when dealing with computer programming languages. Programming langu…

* No NULL pointer can be dereferenced. NULL pointers CAN be dereferenced, it all depends on the environment you run on.

In standard C NULL pointers cannot be dereferenced. Full stop, there is nothing to argue about it.

There are environments that either lack memory protection and do not allow invalid pointer derereferences to be caught, which means that you can't rely on the MMU to catch mistakes. In this case either deal with silent errors or get a compiler that is able (at a cost) to sanitize any pointer access.

There are other systems in which memory address 0 is a perfectly valid address. The ABI of these systems should pick a different bit pattern for NULL pointers, but often don't so compilers sometime offer as a conforming extension extension the option to allow null pointers to be treated as valid (in effect not having null pointers at all).

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#155

Earlier quoted context omitted.

It means that empty loops (loops with empty bodies) can be completely removed if the controlling expression has no side effects. > This is intended to allow compiler transformations such as removal of empty loops even when termination cannot be proven. It means while(i) {} can be eliminated as if i were 0, because there are no side effects in the loop expression or the loop body, and what would be the point of the lo…

It's quite common in embedded systems to have the fault handler end with an infinite loop, to give the programmer a chance to attach a debugger an inspect the call stack. Sometimes this behavior is turned on or off with a debug flag, which can trigger this unexpected optimization if the flag is not a compile time constant.

You could always say

    if (flag) while (1);

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#156
post #145

Earlier quoted context omitted.

You can cast from char* to uint32_t* and start using it. I forget the exact standardese, but there is an execption for char*.

There is an exception for accessing any object through a character type pointer, but not the other way around. uint32_t is not a character type, and it doesn't matter if it was casted to a char* first. Also, apparently uint8_t may not be a character type.

> Also, apparently uint8_t may not be a character type.

I think that goes hand-in-hand with the fact that `char` is not guaranteed to be 8-bits wide in C, so by that fact `uint8_t` may not be a `char`. In practice I highly doubt this distinction really matters: Platforms without an 8-bit `char` are basically guaranteed to not support (a standards compliant) `uint8_t` anyway, and it is reasonable to target 8-bit `char` systems in which case it's safe to assume `uint8_t` and `char` are the same thing. (Well, `unsigned char`)

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#157

Earlier quoted context omitted.

There is an exception for accessing any object through a character type pointer, but not the other way around. uint32_t is not a character type, and it doesn't matter if it was casted to a char* first. Also, apparently uint8_t may not be a character type.

> Also, apparently uint8_t may not be a character type. I think that goes hand-in-hand with the fact that `char` is not guaranteed to be 8-bits wide in C, so by that fact `uint8_t` may not be a `char`. In practice I highly doubt this distinction really matters: Platforms without an 8-bit `char` are basically guaranteed to not support (a standards compliant) `uint8_t` anyway, and it is reasonable to target 8-bit `char…

Yes, a system with char >8 bits wouldn't have a uint8_t type at all. But, even when char and (u)int8_t are the same size, it may not be the same type. Compilers could go and apply strict aliasing for pointers to uint8_t. I'm at the limit of my C types knowledge here, so correct me if I'm just wrong.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#158

Earlier quoted context omitted.

It's quite common in embedded systems to have the fault handler end with an infinite loop, to give the programmer a chance to attach a debugger an inspect the call stack. Sometimes this behavior is turned on or off with a debug flag, which can trigger this unexpected optimization if the flag is not a compile time constant.

You could always say if (flag) while (1);

Yes, that would make more sense. I have never seen this optimization actually bite anyone.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#159
post #152

Earlier quoted context omitted.

My prediction is that the standard will eventually follow the change in practice and eliminate that compiler "optimization".

In order to do that, the standard would have to define that dereferencing a null pointer must produce a deterministic behavior. There are only two possible behaviors: 1. The program successfully reads/writes that memory location and retrieves/overwrites whatever is there without crashing. Then the program can continue on and execute the if even if the pointer was null. 2. The program crashes immediate whenever a null…

C is not Haskell or Java. The C programmer may intend to interact with actual hardware and is not required to interact with some abstract machine. The standard can reflect this or it can attempt to convert C into a poorly designed high level language. Dereferencing the null pointer should be implementation dependent, but the compiler should be required to either detect and flag this as an error or compile it into the machine operations indicated. The actual execution in the second case may depend on the environment.

Re: I Do Not Know C: Short quiz on undefined behavior (2015)

#160
post #143

Earlier quoted context omitted.

the post that brought this to my attention discussed how a security error in Linux was created by this "optimization".

This? http://blog.regehr.org/archives/970 The compiler didn't create a security bug by removing the null check. The bug was created by the programmer when he didn't check for null before dereferencing the pointer. Even with the check, the program contained a bug.

The compiler converted a buggy program that was prevented from opening a security hole by defense in depth into a program with a security hole. It transformed a careless error into a systemic error, all in the cause of a micro-optimization that didn't.
Post reply on HN