How much UB is in my compiler?
Tags: cpp, programming
Published: 2024-06-11 11:13:33

C and C\+\+ developers have two bug\-related headaches: memory leaks and undefined behavior\. As you can guess, I'll talk about undefined behavior—and about "my" compiler\. To be more precise, I'll talk about the set of compilers and the tools to develop them, the one called LLVM\. Why did I say "my" compiler? Our team really likes Clang, a part of LLVM, and regularly use it\.

![](https://import.viva64.com/docx/blog/1130_LLVM_part2/image1.png)

We recently rechecked the LLVM code and even wrote [an article](https://pvs-studio.com/en/blog/posts/cpp/1126/) about it\. This is its sequel: we'll break down the errors that haven't been covered in the previous part\.

Foreword

Do you know what undefined behavior \(UB\) is? You can witness it when a programmer writes the code that the programming language rules let them write, and the code seems good, but the program works incorrectly \(or maybe correctly\.\.\.\)\. UB also means that the standard doesn't guarantee anything on your code\.

The program output depends only on the compiler and the target platform\. So, we can deduce that compilers can do almost anything with our program, even what we want them to\. However, it turns out that the error is just ably hidden, and it's just one's luck that everything is okay\.

Undefined behavior can occur for various reasons: the incorrect use of integer types, the incorrect memory handling, data races in parallel execution, and many others\. The scariest part is, it reveals only in certain scenarios\.

For example, you've already tested the program and deliver it to a client, but, suddenly, nothing works on their side\. Although, everything was fine five minutes ago on our side\. However, there are no logs or logical explanations here\. There's an explanation for this, though\. It's all UB fault\. However, we need to go through a tough debugging journey to realize it\.

Another scenario is possible: we update the compiler, build the program with it, and everything starts crushing\. All the tests are red\. Oh, gross\. All because of a couple code lines that the compiler has begun to use against you\.

The code on the agenda provides such an unusual experience that only one thought comes to mind: the developers do know something about UB that mere mortals may not\. They seem to "summon" it on purpose\.

**C\+\+ programmer's guide to Undefined behavior**

Meanwhile, we started posting a book about undefined behavior on our website\. The author is Dmitry Sviridkin, and the editor is Andrey Karpov\. Here's [the link](https://pvs-studio.com/en/blog/posts/cpp/1129/) to the first part\. You may also subscribe to the [monthly article digest](https://pvs-studio.com/en/subscribe/) so as not to miss other parts of the book and new curious content\.

**Fragment N1**

Let's start with an eternal classic in the world of errors—it's a null pointer dereference\.

```cpp
void LineTable::Dump(Stream *s, Target *target, Address::DumpStyle style,
                     Address::DumpStyle fallback_style, bool show_line_ranges) 
{
  const size_t count = m_entries.size();
  LineEntry line_entry;
  SupportFileSP prev_file;   // <=
  for (size_t idx = 0; idx < count; ++idx) {
    ConvertEntryAtIndexToLineEntry(idx, line_entry);
    line_entry.Dump(s, target, *prev_file != *line_entry.original_file_sp, // <=
                    style, fallback_style, show_line_ranges);
    s->EOL();
    prev_file = line_entry.original_file_sp;
  }
}
```

The analyzer warning: 

[V522](https://pvs-studio.com/en/docs/warnings/v522/) Dereferencing of the null pointer 'prev\_file' might take place\. LineTable\.cpp [363](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/source/Symbol/LineTable.cpp#L363)

As we can see, the analyzer points to the *prev\_file* variable\. And this is what the variable \(or rather its type\) looks like:

```cpp
typedef std::shared_ptr<lldb_private::SupportFile> SupportFileSP;
```

When we declare this way, *std::shared\_ptr* [is initialized with null](https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr#:~:text=1%2C2),empty%20shared_ptr.)\. [A](https://pvs-studio.com/en/blog/posts/cpp/0306/) null pointer dereference leads to undefined behavior\.

Let's have a special UB combo meter to count it\.

The UB combo meter: 0 —\> 1\.

**Fragment N2**

Errors usually occur throughout the code\. Want to see how just a single code fragment can plague your code with bugs?

```cpp
bool Sema::checkStringLiteralArgumentAttr(const AttributeCommonInfo &CI,
                                          const Expr *E, StringRef &Str,
                                          SourceLocation *ArgLocation) 
{
  const auto *Literal = dyn_cast<StringLiteral>(E->IgnoreParenCasts());
  ....
}
```

As you can see, the *E* pointer is dereferenced in the first line without check\. And then THIS happened\.

<spoiler title="Not for the faint hearted">

The analyzer warnings:

* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: [349](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaDeclAttr.cpp#L349), [1801](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaDeclAttr.cpp#L1801)\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: [349](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaDeclAttr.cpp#L349), [1974](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/Sema/SemaDeclAttr.cpp#L1974)\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 1984\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 1999\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 2046\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 2381\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 3188\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 3355\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 3376\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 3423\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 3529\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 3543\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 4328\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 5416\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 6353\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 6437\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 6447\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 6965\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7096\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7239\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7467\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7742\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7772\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7825\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7842\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 7872\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8224\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8305\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8455\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8602\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8819\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8827\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 8870\. SemaDeclAttr\.cpp
* V522 Dereferencing of the null pointer 'E' might take place\. The null pointer is passed into 'checkStringLiteralArgumentAttr' function\. Inspect the second argument\. Check lines: 349, 977\.  SemaDeclAttr\.cpp



</spoiler>




As proofs, I'll show you where the function is called in the first two warnings\.

Here's the first fragment:

```cpp
static void handleAssumumptionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// Handle the case where the attribute has a text message.
StringRef Str;
SourceLocation AttrStrLoc;
if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &AttrStrLoc))
  return;
....
}
```

Here's the second one:

```cpp
static void handleWeakRefAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
....
if (AL.getNumArgs() && S.checkStringLiteralArgumentAttr(AL, 0, Str))
....
}
```

As we can see, 0 is passed as the second parameter in both cases\.

"UB can make your cat get pregnant, even if you don't have one\." Looking at the number of warnings, can you imagine how many cats could get pregnant?

Do you remember the UB combo meter? So, here's: 1 —\> 36\.

**Fragment N3**

Let's take a look at the following structure:

```cpp
struct ForceCodegenLinking {
    ForceCodegenLinking() {
      // We must reference the passes in such a way that compilers will not
      // delete it all as dead code, even with whole program optimization,
      // yet is effectively a NO-OP. As the compiler isn't smart enough
      // to know that getenv() never returns -1, this will do the job.
      // This is so that globals in the translation units where these functions
      // are defined are forced to be initialized, populating various
      // registries.
      if (std::getenv("bar") != (char*) -1)
        return;

      (void) llvm::createFastRegisterAllocator();
      (void) llvm::createBasicRegisterAllocator();
      (void) llvm::createGreedyRegisterAllocator();
      (void) llvm::createDefaultPBQPRegisterAllocator();

      (void)llvm::createBURRListDAGScheduler(nullptr,
                                             llvm::CodeGenOptLevel::Default);
      (void)llvm::createSourceListDAGScheduler(nullptr,
                                               llvm::CodeGenOptLevel::Default);
      (void)llvm::createHybridListDAGScheduler(nullptr,
                                               llvm::CodeGenOptLevel::Default);
      (void)llvm::createFastDAGScheduler(nullptr,
                                         llvm::CodeGenOptLevel::Default);
      (void)llvm::createDefaultScheduler(nullptr,
                                         llvm::CodeGenOptLevel::Default);
      (void)llvm::createVLIWDAGScheduler(nullptr,
                                         llvm::CodeGenOptLevel::Default);
    }
  } ForceCodegenLinking; // Force link by creating a global definition.
}
```

In its constructor, a number of functions are called to create some entities\. We'll look only at the functions to which arguments are passed \(there are six\)\. For example, here are some of them:

```cpp
ScheduleDAGSDNodes *llvm::createBURRListDAGScheduler(SelectionDAGISel *IS,
                                                    CodeGenOptLevel OptLevel)
{
  const TargetSubtargetInfo &STI = IS->MF->getSubtarget();
  ....
}
```

Or

```cpp
ScheduleDAGSDNodes* createDefaultScheduler(SelectionDAGISel *IS,
                                           CodeGenOpt::Level OptLevel) 
{
  const TargetLowering *TLI = IS->TLI;
  const TargetSubtargetInfo &ST = IS->MF->getSubtarget();
  ....
}
```

If we look at the function call in the constructor, we see that *nullptr* is passed as its first argument\. It's that first *IS* parameter that's dereferenced in the very first line of the function\.

It's strange code\. Perhaps the LLVM developers know something and can actually control UB\. Or perhaps they just want there to be more cats in our world :D

Every function with the first zero argument gets dereferenced\.

So, here are the analyzer warnings:

* V522 Dereferencing of the null pointer might take place\. The null pointer is passed into 'createBURRListDAGScheduler' function\. Inspect the first argument\. Check lines: 'ScheduleDAGRRList\.cpp:[3147'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp#L3147), 'LinkAllCodegenComponents\.h:[40'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h#L40)\. 
* V522 Dereferencing of the null pointer might take place\. The null pointer is passed into 'createSourceListDAGScheduler' function\. Inspect the first argument\. Check lines: 'ScheduleDAGRRList\.cpp:[3161'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp#L3161), 'LinkAllCodegenComponents\.h:[42'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h#L42)\. 
* V522 Dereferencing of the null pointer might take place\. The null pointer is passed into 'createHybridListDAGScheduler' function\. Inspect the first argument\. Check lines: 'ScheduleDAGRRList\.cpp:[3175'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGRRList.cpp#L3175), 'LinkAllCodegenComponents\.h:[44'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/include/llvm/CodeGen/LinkAllCodegenComponents.h#L44)\. 
* \.\.\. \(the other three are similar\)

The UB combo meter: 36 —\> 42\.

**Fragment N4**

Another snippet, another UB:

```cpp
Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
                                           const CallExpr *E) 
{
  ....
  unsigned SrcNumElts =
        cast<llvm::FixedVectorType>(Ops[1]->getType())->getNumElements();
  ....
  int Indices[16];
    for (unsigned i = 0; i != DstNumElts; ++i)
      Indices[i] = (i >= SrcNumElts) ? SrcNumElts + (i % SrcNumElts) : i;
  ....
}
```

The analyzer warning: 

[V609](https://pvs-studio.com/en/docs/warnings/v609/) Mod by zero\. Denominator 'SrcNumElts' == 0\. CGBuiltin\.cpp:[14833](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/clang/lib/CodeGen/CGBuiltin.cpp#L14833)

The analyzer suggests us pay attention to *SrcNumElts*\. Let's get to the bottom of this\.

We can see that the ternary operator is used in the loop\. The check condition: *i* is greater than or equal to *SrcNumElts*\. When we have *SrcNumElts == 0* then* SrcNumElts \+ \(i % SrcNumElts\)* will be executed* *\(there are no checks above, *[getNumElements](https://github.com/llvm/llvm-project/blob/279a659e9772e48d95ad7d81f6deb00ee31e35e1/llvm/include/llvm/IR/DerivedTypes.h#L582C3-L582C46)* can return 0\)\. As we know, the behavior is [undefined](https://en.cppreference.com/w/cpp/language/operator_arithmetic#Built-in_multiplicative_operators:~:text=The%20result%20of%20built%2Din%20division%20is%20lhs%20divided%20by%20rhs.%20If%20rhs%20is%20zero%2C%20the%20behavior%20is%20undefined.) when [we divide by 0](https://pvs-studio.com/en/blog/terms/0085/) \(including division by modulus\)\.

The UB combo meter: 42 —\> 43\.

**Fragment N5**

Here's a small function consisting only of the *if* statements:

```cpp
static bool StopAtComponentPre(const Symbol &component) {
  if constexpr (componentKind == ComponentKind::Ordered) {
    // Parent components need to be iterated upon after their
    // sub-components in structure constructor analysis.
    return !component.test(Symbol::Flag::ParentComp);
  } else if constexpr (componentKind == ComponentKind::Direct) {
    return true;
  } else if constexpr (componentKind == ComponentKind::Ultimate) {
    return component.has<ProcEntityDetails>() ||
        IsAllocatableOrObjectPointer(&component) ||
        (component.has<ObjectEntityDetails>() &&
            component.get<ObjectEntityDetails>().type() &&
            component.get<ObjectEntityDetails>().type()->AsIntrinsic());
  } else if constexpr (componentKind == ComponentKind::Potential) {
    return !IsPointer(component);
  } else if constexpr (componentKind == ComponentKind::PotentialAndPointer) {
    return true;
  }
}
```

Here's the analyzer warning:

[V591](https://pvs-studio.com/en/docs/warnings/v591/) Non\-void function should return a value\. tools\.cpp:[1278](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/flang/lib/Semantics/tools.cpp#L1278)

As we can see, the function has no *return* for the case when all the conditions are false\. This can happen because *enum class ComponentKind* contains another *Scope* value not provided here\. In such a case, the [behavior](https://en.cppreference.com/w/cpp/language/return#:~:text=Flowing%20off%20the,is%20undefined%20behavior.) is undefined\.

Undefined behavior doesn't necessarily mean that the function will return a random value \(*true* or *false*\)\. [It's exactly whatever you want it to be](https://pvs-studio.com/en/blog/posts/cpp/0917/)\.

The UB combo meter: 43 —\> 44\.

**Fragment N6**

This is the fragment that might seem safe:

```cpp
bool AppleObjCRuntimeV2::NonPointerISACache::EvaluateNonPointerISA(
    ObjCISA isa, ObjCISA &ret_isa) {
  ....
  if (index > m_indexed_isa_cache.size())
    return false;

  LLDB_LOGF(log, "AOCRT::NPI Evaluate(ret_isa = 0x%" PRIx64 ")",
          (uint64_t)m_indexed_isa_cache[index]);
  ....
}
```

It doesn't seem bad, there's even a check for the index\.

However, there's still an error here\. If the stars align so that the *index* variable is equal to *m\_indexed\_isa\_cache\.size\(\)*, then IT will happen\. Yes, we'll get the [array overrun](https://pvs-studio.com/en/blog/terms/0071/) and, as a consequence, we'll catch undefined behavior\.

The analyzer warning:

[V557](https://pvs-studio.com/en/docs/warnings/v557/) Array overrun is possible\. The 'index' index is pointing beyond array bound\. AppleObjCRuntimeV2\.cpp [3308](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp#L3308)

To fix it, just write like this:

```cpp
if (index >= m_indexed_isa_cache.size())
    return false;
```

Here's exactly the same warning but a little lower in the code\.

Here's the analyzer warning: 

[V557](https://pvs-studio.com/en/docs/warnings/v557/) Array overrun is possible\. The 'index' index is pointing beyond array bound\. AppleObjCRuntimeV2\.cpp [3311](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp#L3311)

The UB combo meter: 44 —\> 46

**Fragment N7**

As the old saying goes, "all in good time"\. In the code fragment above, the pointer check is later than its dereference in the constructor initialization list:

```cpp
lldb_private::formatters::StdlibCoroutineHandleSyntheticFrontEnd::
    StdlibCoroutineHandleSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
    : SyntheticChildrenFrontEnd(*valobj_sp) {
  if (valobj_sp)
    Update();
}
```

The analyzer warning:

[V664](https://pvs-studio.com/en/docs/warnings/v664/) The 'valobj\_sp' pointer is being dereferenced on the initialization list before it is verified against null inside the body of the constructor function\. Check lines: [99](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/lldb/source/Plugins/Language/CPlusPlus/Coroutines.cpp#L99), 100\. Coroutines\.cpp

The UB combo meter: 44 —\> 46

**Fragment N8**

In the last code fragment, let's go back to where we started\. It's a null dereference but in a different code fragment, though\.

```cpp
void SetInsertPoint(Instruction *I) {
  BB = I->getParent();
  InsertPt = I->getIterator();
  assert(InsertPt != BB->end() && "Can't read debug loc from end()");
  SetCurrentDebugLocation(I->getStableDebugLoc());
}
```

The analyzer warning:

[V522](https://pvs-studio.com/en/docs/warnings/v522/) Dereferencing of the null pointer 'I' might take place\. The null pointer is passed into 'SetInsertPoint' function\. Inspect the first argument\. Check lines: 'IRBuilder\.h:[188'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/include/llvm/IR/IRBuilder.h#L188), 'OMPIRBuilder\.cpp:[5983'](https://github.com/llvm/llvm-project/blob/461274b81d8641eab64d494accddc81d7db8a09e/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp#L5983)\.

The analyzer recommends to pay attention to the *I* parameter\. Well, let's do as it suggests and see where the function is called:

```cpp
if (UnreachableInst *ExitTI =
        dyn_cast<UnreachableInst>(ExitBB->getTerminator())) {
  CurBBTI->eraseFromParent();
  Builder.SetInsertPoint(ExitBB);
} else {
  Builder.SetInsertPoint(ExitTI);
}
```

It's another strange snippet\. If we look at the names of the *\(ExitTI* and *ExitBB\)* pointers and functions that are called via them *\(getTerminator\)*, we may get the impression that they're intentionally dereferenced using *null* to crash the program :D Although the result will be undefined\.

We get our "terminator" *ExitTI* from *ExitBB\-\>getTerminator\(\)*, and first cast it to *UnreachableInst*\. All that occurs in the condition of the *if* statement\. If the terminator is equal to null, the *else* branch will be executed, where this "cyborg" passed as an argument to the function, and immediately dereferenced\. Hasta la vista, baby\.

The UB combo meter: 47 —\> 48

Afterword about null dereference

The null dereference topic is as deep as a rabbit hole — or even deeper than we thought\. We suggest you have a bit of fun with this fascinating talk about \*\(char\*\)0 = 0;

<https://www.youtube.com/watch?v=dFIqNZ8VbRY>

Want to read something curious? Take a look at "[Compilation of gripping C\+\+ conference talks from 2023](https://pvs-studio.com/en/blog/posts/1120/)"\.

Conclusion

The UB combo meter displays 48 and the article is coming to the end\.

The undefined behavior examples seemed marvelous\. The curious thing is that developers didn't immediately find them while writing the code\. Undefined behavior is usually found in less obvious places, and that's why it's even harder to spot\.

It's important to bear in mind that developers have to keep an eye out for UB and other bugs all the time\. This requires attention and effort\. Even experienced developers may encounter undefined behavior and errors, especially when they work with new libraries or difficult, large systems\.

That's why I recommend you not ignore compiler and static analyzer warnings and always enhance your testing experience\.