From 754bbf7a25a8dda49b5d08ef0d0443bbf5af0e36 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 7 Apr 2024 13:41:34 -0500 Subject: new repository --- devdocs/c/language%2Fbehavior.html | 166 +++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 devdocs/c/language%2Fbehavior.html (limited to 'devdocs/c/language%2Fbehavior.html') diff --git a/devdocs/c/language%2Fbehavior.html b/devdocs/c/language%2Fbehavior.html new file mode 100644 index 00000000..0a883ffc --- /dev/null +++ b/devdocs/c/language%2Fbehavior.html @@ -0,0 +1,166 @@ +

Undefined behavior

The C language standard precisely specifies the observable behavior of C language programs, except for the ones in the following categories:

+

(Note: Strictly conforming programs do not depend on any unspecified, undefined, or implementation-defined behavior)

+

The compilers are required to issue diagnostic messages (either errors or warnings) for any programs that violates any C syntax rule or semantic constraint, even if its behavior is specified as undefined or implementation-defined or if the compiler provides a language extension that allows it to accept such program. Diagnostics for undefined behavior are not otherwise required.

+

UB and optimization

Because correct C programs are free of undefined behavior, compilers may produce unexpected results when a program that actually has UB is compiled with optimization enabled:

+

For example,

+

Signed overflow

int foo(int x)
+{
+    return x + 1 > x; // either true or UB due to signed overflow
+}

may be compiled as (demo)

+
foo:
+        mov     eax, 1
+        ret

Access out of bounds

int table[4] = {0};
+int exists_in_table(int v)
+{
+    // return 1 in one of the first 4 iterations or UB due to out-of-bounds access
+    for (int i = 0; i <= 4; i++)
+        if (table[i] == v)
+            return 1;
+    return 0;
+}

May be compiled as (demo)

+
exists_in_table:
+        mov     eax, 1
+        ret

Uninitialized scalar

_Bool p; // uninitialized local variable
+if (p) // UB access to uninitialized scalar
+    puts("p is true");
+if (!p) // UB access to uninitialized scalar
+    puts("p is false");

May produce the following output (observed with an older version of gcc):

+
p is true
+p is false
size_t f(int x)
+{
+    size_t a;
+    if (x) // either x nonzero or UB
+        a = 42;
+    return a;
+}

May be compiled as (demo)

+
f:
+        mov     eax, 42
+        ret

Invalid scalar

int f(void)
+{
+    _Bool b = 0;
+    unsigned char* p = (unsigned char*)&b;
+    *p = 10;
+    // reading from b is now UB
+    return b == 0;
+}

May be compiled as (demo)

+
f:
+        mov     eax, 11
+        ret

Null pointer dereference

int foo(int* p)
+{
+    int x = *p;
+    if (!p)
+        return x; // Either UB above or this branch is never taken
+    else
+        return 0;
+}
+ 
+int bar()
+{
+    int* p = NULL;
+    return *p;    // Unconditional UB
+}

may be compiled as (demo)

+
foo:
+        xor     eax, eax
+        ret
+bar:
+        ret

Access to pointer passed to realloc +

+

Choose clang to observe the output shown

+
#include <stdio.h>
+#include <stdlib.h>
+ 
+int main(void)
+{
+    int *p = (int*)malloc(sizeof(int));
+    int *q = (int*)realloc(p, sizeof(int));
+    *p = 1; // UB access to a pointer that was passed to realloc
+    *q = 2;
+    if (p == q) // UB access to a pointer that was passed to realloc
+        printf("%d%d\n", *p, *q);
+}

Possible output:

+
12

Infinite loop without side-effects

+

Choose clang to observe the output shown

+
#include <stdio.h>
+ 
+int fermat()
+{
+    const int MAX = 1000;
+    // Endless loop with no side effects is UB
+    for (int a = 1, b = 1, c = 1; 1;)
+    {
+        if (((a * a * a) == ((b * b * b) + (c * c * c))))
+            return 1;
+        ++a;
+        if (a > MAX)
+        {
+            a = 1;
+            ++b;
+        }
+        if (b > MAX)
+        {
+            b = 1;
+            ++c;
+        }
+        if (c > MAX)
+            c = 1;
+    }
+    return 0;
+}
+ 
+int main(void)
+{
+    if (fermat())
+        puts("Fermat's Last Theorem has been disproved.");
+    else
+        puts("Fermat's Last Theorem has not been disproved.");
+}

Possible output:

+
Fermat's Last Theorem has been disproved.

References

+ + + + + + + +
1. +What Every C Programmer Should Know About Undefined Behavior #1/3
2. +What Every C Programmer Should Know About Undefined Behavior #2/3
3. +What Every C Programmer Should Know About Undefined Behavior #3/3
4. +Undefined behavior can result in time travel (among other things, but time travel is the funkiest)
5. +Understanding Integer Overflow in C/C++
6. +Undefined Behavior and Fermat’s Last Theorem
7. +Fun with NULL pointers, part 1 (local exploit in Linux 2.6.30 caused by UB due to null pointer dereference)

See also

+
C++ documentation for Undefined behavior
+

+ © cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
+ https://en.cppreference.com/w/c/language/behavior +

+
-- cgit v1.2.3