| Start |
|---|
Dereferencing a pointer to variable beyond its lifetime Some compilers helpfully point this out. For example, and for the above code. But compilers may not be able to help in complex code. (1) Returning reference to variable declared (2) According to ISO/IEC 9899:2011 6.2.4 §2, "The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime." (3) Dereferencing the pointer returned by the function |
| Copying overlapping memory A wide variety of standard library functions have among their effects copying byte sequences from one memory region to another. Most of these functions have undefined behavior when the source and destination regions overlap. For example, this ... ... attempts to copy 10 bytes where the source and destination memory areas overlap by three bytes. To visualize: Because of the overlap, the resulting behavior is undefined. Among the standard library functions with a limitation of this kind are If copying takes place between objects that overlap, the behavior is undefined. The The distinction reflects an efficiency vs. generality tradeoff. Copying such as these functions perform usually occurs between disjoint regions of memory, and often it is possible to know at development time whether a particular instance of memory copying will be in that category. Assuming non-overlap affords comparatively more efficient implementations that do not reliably produce correct results when the assumption does not hold. Most C library functions are allowed the more efficient implementations, and |
| Signed integer overflow Per paragraph 6.5/5 of both C99 and C11, evaluation of an expression produces undefined behavior if the result is not a representable value of the expression's type. For arithmetic types, that's called an overflow. Unsigned integer arithmetic does not overflow because paragraph 6.2.5/9 applies, causing any unsigned result that otherwise would be out of range to be reduced to an in-range value. There is no analogous provision for signed integer types, however; these can and do overflow, producing undefined behavior. For example, Most instances of this type of undefined behavior are more difficult to recognize or predict. Overflow can in principle arise from any addition, subtraction, or multiplication operation on signed integers (subject to the usual arithmetic conversions) where there are not effective bounds on or a relationship between the operands to prevent it. For example, this function: is reasonable, and it does the right thing for small enough argument values, but its behavior is undefined for larger argument values. You cannot judge from the function alone whether programs that call it exhibit undefined behavior as a result. It depends on what arguments they pass to it. On the other hand, consider this trivial example of overflow-safe signed integer arithmetic: The relationship between the operands of the subtraction operator ensures that the subtraction never overflows. Or consider this somewhat more practical example: As long as that the counters do not overflow individually, the operands of the final subtraction will both be non-negative. All differences between any two such values are representable as |
Use of an uninitialized variable The variable Note: Variables with static or thread local storage, including global variables (opens new window) without the A very common mistake is to not initialize the variables that serve as counters to 0. You add values to them, but since the initial value is garbage, you will invoke Undefined Behavior, such as in the question Compilation on terminal gives off pointer warning and strange symbols (opens new window). Example: Output: The above rules are applicable for pointers as well. For example, the following results in undefined behavior Note that the above code on its own might not cause an error or segmentation fault, but trying to dereference this pointer later would cause the undefined behavior. |
| Data race C11 introduced support for multiple threads of execution, which affords the possibility of data races. A program contains a data race if an object in it is accessed1 by two different threads, where at least one of the accesses is non-atomic, at least one modifies the object, and program semantics fail to ensure that the two accesses cannot overlap temporally.2 Note well that actual concurrency of the accesses involved is not a condition for a data race; data races cover a broader class of issues arising from (allowed) inconsistencies in different threads' views of memory. Consider this example: The main thread calls Among the ways this program could avoid the data race are
As the mutex option demonstrates, avoiding a data race does not require ensuring a specific order of operations, such as the child thread modifying 1 Modifying or reading an object. 2 (Quoted from ISO:IEC 9889:201x, section 5.1.2.4 "Multi-threaded executions and data races") |
| Read value of pointer that was freed Even just reading the value of a pointer that was freed (i.e. without trying to dereference the pointer) is undefined behavior(UB), e.g. Quoting ISO/IEC 9899:2011, section 6.2.4 §2: […] The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime. The use of indeterminate memory for anything, including apparently harmless comparison or arithmetic, can have undefined behavior if the value can be a trap representation for the type. |
| Modify string literal In this code example, the char pointer However, modifying a mutable array of That's because the string literal is effectively copied to the array each time the array is initialized (once for variables with static duration, each time the array is created for variables with automatic or thread duration — variables with allocated duration aren't initialized), and it is fine to modify array contents. |
| Using incorrect format specifier in printf Using an incorrect format specifier in the first argument to Here is another example Above line of code is undefined behavior. Note that your compiler usually can help you avoid cases like these, if you turn on the proper flags during compiling ( |
Modifying any object more than once between two sequence points Code like this often leads to speculations about the "resulting value" of Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored. (C99 standard, section 6.5, paragraph 2) That scheme proved to be a little too coarse, resulting in some expressions exhibiting undefined behavior with respect to C99 that plausibly should not do. C2011 retains sequence points, but introduces a more nuanced approach to this area based on sequencing and a relationship it calls "sequenced before": If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings. (C2011 standard, section 6.5, paragraph 2) The full details of the "sequenced before" relation are too long to describe here, but they supplement sequence points rather than supplanting them, so they have the effect of defining behavior for some evaluations whose behavior previously was undefined. In particular, if there is a sequence point between two evaluations, then the one before the sequence point is "sequenced before" the one after. The following example has well-defined behaviour: The following example has undefined behaviour: As with any form of undefined behavior, observing the actual behavior of evaluating expressions that violate the sequencing rules is not informative, except in a retrospective sense. The language standard provides no basis for expecting such observations to be predictive even of the future behavior of the same program. |
| Passing a null pointer to printf %s conversion The However, the undefined behavior does not always mean that the program crashes — some systems take steps to avoid the crash that normally happens when a null pointer is dereferenced. For example Glibc is known to print for the code above. However, add (just) a newline to the format string and you will get a crash: In this case, it happens because GCC has an optimization that turns Note that null pointer is different from an empty string. So, the following is valid and has no undefined behaviour. It'll just print a newline: |
| Freeing memory twice Freeing memory twice is undefined behavior, e.g. Quote from standard(7.20.3.2. The free function of C99 ): Otherwise, if the argument does not match a pointer earlier returned by the calloc, malloc, or realloc function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined. |
| Bit shifting using negative counts or beyond the width of the type If the shift count value is a negative value then both left shift and right shift operations are undefined1: If left shift is performed on a negative value, it's undefined: If left shift is performed on a positive value and result of the mathematical value is not representable in the type, it's undefined1: Note that right shift on a negative value (.e.g 1 Quoting ISO/IEC 9899:201x, section 6.5.7: If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined. |
| Accessing memory beyond allocated chunk A a pointer to a piece of memory containing The third line accesses the 4th element in an array that is only 3 elements long, leading to undefined behavior. Similarly, the behavior of the second line in the following code fragment is also not well defined: Note that pointing past the last element of an array is not undefined behavior ( |
Returning from a function that's declared with _Noreturn or noreturn function specifier The function specifier A function that's declared with In the following example,
An example using |
| Dereferencing a null pointer This is an example of dereferencing a NULL pointer, causing undefined behavior. A |
| Reading an uninitialized object that is not backed by memory Reading an object will cause undefined behavior, if the object is1:
The variable a in the below example satisfies all those conditions: 1 (Quoted from: ISO:IEC 9899:201X 6.3.2.1 Lvalues, arrays, and function designators 2) |
| Addition or subtraction of pointer not properly bounded The following code has undefined behavior: According to C11, if addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that does not point into, or just beyond, the same array object, the behavior is undefined (6.5.6). Additionally it is naturally undefined behavior to dereference a pointer that points to just beyond the array: |
Modifying a const variable using a pointer Quoting ISO/IEC 9899:201x, section 6.7.3 §2: If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined. [...] (1) In GCC this can throw the following warning: |
| Using fflush on an input stream The POSIX and C standards explicitly state that using There is no standard way to discard unread characters from an input stream. On the other hand, some implementations uses See Using |
Missing return statement in value returning function When a function is declared to return a value then it has to do so on every possible code path through it. Undefined behavior occurs as soon as the caller (which is expecting a return value) tries to use the return value1. Note that the undefined behaviour happens only if the caller attempts to use/access the value from the function. For example, The 1 (ISO/IEC 9899:201x, 6.9.1/12) If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined. 2 (ISO/IEC 9899:201x, 5.1.2.2.3/1) reaching the } that terminates the main function returns a value of 0. |
Division by zero or or For the second line in each example, where the value of the second operand (x) is zero, the behaviour is undefined. Note that most implementations (opens new window) of floating point math will follow a standard (opens new window) (e.g. IEEE 754), in which case operations like divide-by-zero will have consistent results (e.g., |
Inconsistent linkage of identifiers C11, §6.2.2, 7 says: If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined. Note that if an prior declaration of an identifier is visible then it'll have the prior declaration's linkage. C11, §6.2.2, 4 allows it: For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible,31) if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage. |
| Conversion between pointer types produces incorrectly aligned result The following might have undefined behavior due to incorrect pointer alignment: The undefined behavior happens as the pointer is converted. According to C11, if a conversion between two pointer types produces a result that is incorrectly aligned (6.3.2.3), the behavior is undefined. Here an
Observe that the C standard requests that already the cast operation is undefined. This is imposed because on platforms where addresses are segmented, the byte address Casting You can avoid the undefined behavior arising from misaligned pointer conversion by using Here no pointer conversion to This copy operation for our example only leads to valid value of
|
| Modifying the string returned by getenv, strerror, and setlocale functions Modifying the strings returned by the standard functions The getenv() function, C11, §7.22.4.7, 4, says: The getenv function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the getenv function. The strerror() function, C11, §7.23.6.3, 4 says: The strerror function returns a pointer to the string, the contents of which are localespecific. The array pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the strerror function. The setlocale() function, C11, §7.11.1.1, 8 says: The pointer to string returned by the setlocale function is such that a subsequent call with that string value and its associated category will restore that part of the program’s locale. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the setlocale function. Similarly the The localeconv() function, C11, §7.11.2.1, 8 says: The localeconv function returns a pointer to the filled-in object. The structure pointed to by the return value shall not be modified by the program, but may be overwritten by a subsequent call to the localeconv function. RemarksWhat is Undefined Behavior (UB)? Undefined behavior is a term used in the C standard. The C11 standard (ISO/IEC 9899:2011) defines the term 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 What happens if there is UB in my code? These are the results which can happen due to undefined behavior according to standard: NOTE 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). The following quote is often used to describe (less formally though) results happening from undefined behavior: “When the compiler encounters [a given undefined construct] it is legal for it to make demons fly out of your nose” (the implication is that the compiler may choose any arbitrarily bizarre way to interpret the code without violating the ANSI C standard) Why does UB exist? If it's so bad, why didn't they just define it or make it implementation-defined? Undefined behavior allows more opportunities for optimization; The compiler can justifiably assume that any code does not contain undefined behaviour, which can allow it to avoid run-time checks and perform optimizations whose validity would be costly or impossible to prove otherwise. Why is UB hard to track down? There are at least two reasons why undefined behavior creates bugs that are difficult to detect:
Consider null-pointer dereference: the compiler is not required to diagnose null-pointer dereference, and even could not, as at run-time any pointer passed into a function, or in a global variable might be null. And when the null-pointer dereference occurs, the standard does not mandate that the program needs to crash. Rather, the program might crash earlier, later, or not crash at all; it could even behave as if the null pointer pointed to a valid object, and behave completely normally, only to crash under other circumstances. In the case of null-pointer dereference, C language differs from managed languages such as Java or C#, where the behavior of null-pointer dereference is defined: an exception is thrown, at the exact time ( Additional information There are several such situations that should be clearly distinguished:
Also have in mind that in many places the behavior of certain constructs is deliberately undefined by the C standard to leave room for compiler and library implementors to come up with their own definitions. A good example are signals and signal handlers, where extensions to C, such as the POSIX operating system standard, define much more elaborated rules. In such cases you just have to check the documentation of your platform; the C standard can't tell you anything. Also note that if undefined behavior occurs in program it doesn't mean that just the point where undefined behavior occurred is problematic, rather entire program becomes meaningless. Because of such concerns it is important (especially since compilers don't always warn us about UB) for person programming in C to be at least familiar with the kind of things that trigger undefined behavior. It should be noted there are some tools (e.g. static analysis tools such as PC-Lint) which aid in detecting undefined behavior, but again, they can't detect all occurrences of undefined behavior. |
Read Article
https://develop.horje.com/learn/1434/reference