Showing posts with label cpp_ub. Show all posts
Showing posts with label cpp_ub. Show all posts

May 27, 2026

[C++] pointer arithmetic

Refernece:
[C++] Object Lifetimes reading minute
class Vec {
  public:
    double* data() { return &x; }

  private:
    double x,y,z;
};

Eigen::Map<...>(absl::Span<Vec>);

*(Vect::data() + 1) // Does not give us y

Take away: 

* Even though x, y, and z are allocated sequentially in memory without padding,
physical layout does not supersede semantic rules.
* The layout guarantees mean you can safely memcpy the data, or cast a Vec* to a double* to access the first element (x). It does not grant permission to use pointer arithmetic on double* to slide across the members.
* The pointer arithmetic is only guaranteed within the type of array.
* Pointer to variable only is considered as pointer to array of size 1.
* Thus any pointer arithmetic on single variable is considered out-of-bound; compiler is free to assume anything.

Explain:

Only char*, unsigned char*, and std::byte* are explicitly granted an exception in the standard to 
examine the raw object representation. double* enjoys no such privilege.

Fix:

class Vec {
 public:
  double* data() { return data_; } // Legal: returns pointer to element 0 of a 3-element array
 private:
  double data_[3]; // x=data_[0], y=data_[1], z=data_[2]
};

Apr 22, 2026

[C++] unaligned pointer convert to more strict alignment is an UB

Never create an unaligned pointer of the struct type.

Its UB to convert from one pointer with less alignment guarantees to another with more, if the underlying pointer is not aligned. 

Everything that happens after that is UB, including arithmetic. 


Reference:
https://eel.is/c++draft/expr.static.cast#12

Aug 20, 2019

[C++][Go] Optimizing away a “while(1);”

#include <iostream>

int main() {
  while(1) 
    ;
  std::cout << "Hello" << std::endl;
}
or this:
endless:
  goto endless;


ISO Reference:
https://timsong-cpp.github.io/cppwp/intro.progress#1

The implementation may assume that any thread will eventually do one of the following:
  • terminate, 
  • make a call to a library I/O function, 
  • perform an access through a volatile glvalue, 
  • or perform a synchronization operation or an atomic operation.
In C++, modifying a global (or local) variable is not a side-effecting operation. Only actions in the list above count as defined behavior thus compiler will not end an infinite loop. (in C++17 this isn't true anymore, compiler will honor an infinite loop no matter how.)

More Reference:
https://blog.regehr.org/archives/161


GO:
Go however has this issue of optimizing away the infinite loop in go 1.8:
https://github.com/golang/go/issues/19182

Sep 23, 2018

[C++] std::move(R-VALUE) undefined behavior? No.

Callee's RVO r-value is allocated on caller's stack, thus
std::move a returned r-value and reference to it isn't UB.
https://godbolt.org/z/-CllrO
#include <utility>
using namespace std;

struct T {
    int A[1000];
    T()
    {
        for (int i = 0; i < 1000; i++) {
            A[i] = 42;
        }
    }
};


T layer1()
{
    return T{};
}


T layer2()
{
    return layer1();
}

T layer3()
{
    return layer2();
}


int main()
{
    T const &ref{std::move(layer3())};
    T &&ref2{std::move(layer3())};
    int const &ref3{layer3().A[99]};
    int const &ref4{layer3().A[999]};
}

A NRVO will be allocated on caller's stack as well.
https://godbolt.org/z/iqbhCI
#include <utility>
using namespace std;

struct T {
    int A[1000];
    T()
    {
        for (int i = 0; i < 1000; i++) {
            A[i] = 42;
        }
    }
};


T layer1()
{
    T t{};
    return t;
}


int main()
{

    int const &ref4{layer1().A[999]};
}

However, if we try to out smart the compiler using std::move for NRVO, compiler has no idea what's inside the std::move thus
create the large object on callee's stack and then do a
memcpy back to caller's stack.
https://godbolt.org/z/ql7B9i
#include <utility>
using namespace std;

struct T {
    int A[1000];
    T()
    {
        for (int i = 0; i < 1000; i++) {
            A[i] = 42;
        }
    }
};


T layer1()
{
    T t{};
    return std::move(t);
}


int main()
{

    int const &ref4{layer1().A[999]};
}


Some UB examples here https://github.com/jeaye/value-category-cheatsheet
is not correct.

Further reading:
https://abseil.io/tips/11

Sep 1, 2017

[C++] Compiler undefined behavior: calls never-called function

#include <cstdlib>

typedef int (*Function)();

static Function Do;

static int EraseAll() {
  return system("rm -rf /");
}

void NeverCalled() {
  Do = EraseAll;  
}

int main() {
  return Do();
}
Assembly:
NeverCalled():                       # @NeverCalled()
        ret

main:                                   # @main
        movl    $.L.str, %edi
        jmp     system                  # TAILCALL

.L.str:
        .asciz  "rm -rf /"
Reasoning:

quote from https://www.reddit.com/user/Deaod

---
Do is initialized because its in static memory. But it's initialized to nullptr.

clang makes the assumption that a program will not run into undefined behavior. From there it reasons that since Do contains a value that will cause undefined behavior, SOMEHOW NeverCalled must have been invoked so that invoking Do will not lead to undefined behavior. And since we know that invoking Do will always call the same function, we can inline it.

EDIT: Pay special attention to what is marked as static and what isn't. If you don't mark Do as static, clang will generate the code you expected. If you declare NeverCalled static, clang will generate a ud2 instruction.
---

Reference:

May 9, 2016

[C][C++][UB] in details.

Reference:
Both true and false: a Zen moment with C

asm: test instruction:
https://web.itu.edu.tr/kesgin/mul06/intel/instr/test.html

asm: SETNE instruction :
https://web.itu.edu.tr/kesgin/mul06/intel/instr/setne_setnz.html

code:

#include <stdio.h>
#include <stdbool.h>

int main(int argc, char *argv[])
{
    volatile bool p;

    if ( p )
        puts("p is true");
    else
        puts("p is not true");

    if ( ! p )
        puts("p is false");
    else
        puts("p is not false");

    return 0;
}
asm code:
 .file   "bool1.c"
        .intel_syntax noprefix
        .section        .rodata
.LC0:
        .string "p is true"
.LC1:
        .string "p is not true"
.LC2:
        .string "p is false"
.LC3:
        .string "p is not false"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        push    rbp
.LCFI0:
        mov     rbp, rsp
.LCFI1:
        sub     rsp, 32
.LCFI2:
        mov     DWORD PTR [rbp-20], edi
        mov     QWORD PTR [rbp-32], rsi
        movzx   eax, BYTE PTR [rbp-1]
        test    al, al
        je      .L2
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        jmp     .L3
.L2:
        mov     edi, OFFSET FLAT:.LC1
        call    puts
.L3:
        movzx   eax, BYTE PTR [rbp-1]
        xor     eax, 1  // HERE, since local variable isn't init., the value could be other value than 1.
                        // Thus, an XOR will always produce True.
        test    al, al
        je      .L4
        mov     edi, OFFSET FLAT:.LC2
        call    puts
        jmp     .L5
.L4:
        mov     edi, OFFSET FLAT:.LC3
        call    puts
.L5:
        mov     eax, 0
        leave
.LCFI3:
        ret
 
Reference:
Undefined behavior can result in time travel

If there's an UB in code path, compiler could consider all code paths go to one code path.

code:
int table[4];
bool exists_in_table(int v)
{
    for (int i = 0; i <= 4; i++) {
        if (table[i] == v) return true;
    }
    return false;
}

inference:
A post-classical compiler, on the other hand, might perform the following analysis:
  • The first four times through the loop, the function might return true.
  • When i is 4, the code performs undefined behavior.
  • Since undefined behavior lets me do anything I want, I can totally ignore that case and proceed on the assumption that i is never 4. (If the assumption is violated, then something unpredictable happens, but that’s okay, because undefined behavior grants me permission to be unpredictable.)
  • The case where i is 5 never occurs, because in order to get there, I first have to get through the case where i is 4, which I have already assumed cannot happen.
  • Therefore, all legal code paths return true.


to code:
bool exists_in_table(int v)
{
    return true;
}

Reference:
What Every C Programmer Should Know About Undefined Behavior #1/3
What Every C Programmer Should Know About Undefined Behavior #2/3
What Every C Programmer Should Know About Undefined Behavior #3/3
A Guide to Undefined Behavior in C and C++, Part 1
A Guide to Undefined Behavior in C and C++, Part 2
A Guide to Undefined Behavior in C and C++, Part 3

  • Interacting Compiler Optimizations Lead to Surprising Results
  • Undefined Behavior and Security Don't Mix Well
  • Debugging Optimized Code May Not Make Any Sense.
  • "Working" code that uses undefined behavior can "break" as the compiler evolves or changes
  • There is No Reliable Way to Determine if a Large Codebase Contains Undefined Behavior


UBs:
  • Use of an uninitialized variable
  • Signed integer overflow
  • Oversized Shift Amounts
  • Dereferences of Wild Pointers and Out of Bounds Array Accesses
  • Dereferencing a NULL Pointer
  • Violating Type Rules
  • It is undefined behavior to cast an int* to a float* and dereference it (accessing the "int" as if it were a "float").


Reference:
Adventures in undefined behavior: The premature downcast

"If a nonstatic member function of a class X is called for an object that is not of type X, or of a type derived from X, the behavior is undefined."
In other words, if you are invoking a method on an object of type X, then you are promising that it really is of type X, or a class derived from it.

code:
class Shape
{
public:
    virtual bool Is2D() { return false; }
};

class Shape2D : public Shape
{
public:
    virtual bool Is2D() { return true; }
};

Shape *FindShape(Cookie cookie);

void BuyPaint(Cookie cookie)
{
    Shape2D *shape = static_cast<Shape2D *>(FindShape(cookie));
    if (shape->Is2D()) {  // ALWAYS TRUE! Since it's the type of Shape2D
       .. do all sorts of stuff ...
    }
}

Reference:
A static_cast is not always just a pointer adjustment

The rule for null pointers is that casting a null pointer to anything results in another null pointer.

Reference:
A bit of background on compilers exploiting signed overflow


------------
For infinite loop, compiler should not opt out in these conditions:
The implementation may assume that any thread will eventually do one of the following:
  • terminate,
  • make a call to a library I/O function, 
  • access or modify a volatile object, 
  • or perform a synchronization operation or an atomic operation.

Empty infinite loops are UB in C++11 and later.

Reference:
Compilers and Termination Revisited
Is this infinite recursion UB?
Optimizing away a “while(1);” in C++0x
is C implementation allowed to terminate an infinite loop?
[rust] LLVM loop optimization can make safe programs crash

--
Principles for Undefined Behavior in Programming Language Design - John Regehr

Feb 22, 2016

[C++] Is signed integer overflow still undefined behavior in C++?

http://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html

http://stackoverflow.com/a/16188846


is still overflow of these types an undefined behavior?
Yes. Per Paragraph 5/4 of the C++11 Standard (regarding any expression in general):
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [...]
The fact that a two's complement representation is used for those signed types does not mean that arithmetic modulo 2^n is used when evaluating expressions of those types.
Concerning unsigned arithmetic, on the other hand, the Standard explicitly specifies that (Paragraph 3.9.1/4):
Unsigned integers, declared unsignedshall obey the laws of arithmetic modulo 2^n where n is the number of bits in the value representation of that particular size of integer
This means that the result of an unsigned arithmetic operation is always "mathematically defined", and the result is always within the representable range; therefore, 5/4 does not apply. Footnote 46 explains this:
46) This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.

May 1, 2014

[C++] Undefined Behavior, UB


  • Signed integer overflow (but not unsigned!)
  • Dereferencing NULL pointer or result of malloc(0) // 0 point to virtual memory 0 position. Also a reason why program start memory not 0.
  • Shift greater than (or equal to) the width of the operand
  • Reading from uninitialized variables
  • Modifying a variable more than once in an expression : Sequence point
  • Buffer overflow
  • Comparing pointers into two different data structures
  • Pointer overflow : GCC and pointer overflows
  • Modifying a const object (C++) or a string literal
  • Negating INT_MIN  : Why does -INT_MIN = INT_MIN in a signed, two's complement representation?
  • Data races
  • Mismatch between new and delete
  • Calling a library routine w/o fulfilling the prerequisites
  • memcpy with overlapping buffers // copy/copy_if , The source and destination ranges cannot overlap.
  • atomic_is_lock_free requires passing in obj shall not be nullptr
  • If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
clang -fsanitize=undefined

Dec 4, 2013

[C++][NOTE][BIGINNER] signed int overflow

Reference:
Signed Overflow
why-does-integer-overflow-on-x86-with-gcc-cause-an-infinite-loop
Signed integer overflow is undefined behaviour according to the standard §5.4:


"If during the evaluation of an expression, the result is not mathematically

defined or not in the range of representable values for its type, the behavior is undefined."

Most implementations will just wrap around, so if you try it out on your machine,

you will probably see the same as if you had done

std::cout << std::numeric_limits<int>::min();
Relying on such undefined behaviour is however _not_ safe.