What happens when dereferencing a nullptr?
NULL interface pointer - Adobe After Effects - Creative COW
Please help, I have a problem
Please help. Can't import Alexa footage into my project. Error: A NULL interface pointer has been dereferenced
I saw this code in A Tour of C++, but with a bit modify for illustration:
#include <iostream>
int main() {
char s = 'a';
char *p = &s;
while (*p) {
std::cout << *p;
p++;
}
p = nullptr;
//std::cout << (*p == true);
*p == true;
}
I do not know how does while (*p) { end while I do not know what happens when p is nullptr. And std::cout << (*p == true) will induce segment fault but *p == true does not.
I am trying to import footage from the Arri Alexa into my project. It's ProRes 4444 (3200x1800) in LogC. 23.98fps. I have also tried to import a proxy I created of the footage with the same timebase, but ProRes Proxy (1080p) codec. Same error. The footage plays and edits fine in Premiere and DaVinci Resolve. I can import other Alexa footage of the same size and codec into After Effects just fine.
I found this article from years ago: https://forums.adobe.com/thread/578273 but it looks like nothing of use from there.
Someone please help because I'm at a total loss here, this doesn't make any sense to me. ???
Newest version of AE.
It is not the compiler that causes your program to crash on dereferencing a null pointer. The problem is that the pointer is pointing to memory that it is illegal to reference, and the operating system kills your program for invalid behavior.
Trying to trick the compiler by obfuscating that it is a null pointer won't work, because it isn't the compiler that detects it.
There is no legitimate reason to dereference a null pointer unless you on a rare system that maps page zero (or you intend your program to crash). It is generally accepted that zeroing a pointer is a good way to mark it as invalid and dereferencing an invalid pointer is a bug. Modern operating systems do not give you a page of memory at that address specifically to make debugging invalid pointers easier.
I would not even call your program crashing from this to be undefined behavior. Dereferencing a pointer with random data in it would give you undefined behavior. Dereferencing a pointer that contains an address not assigned to your program is quite well defined in demand paged memory protected operating systems, and the behavior defined by the operating system is for your program to crash. From the language's perspective, it is still undefined behavior, because what happens is not defined in the scope of the language. Since this behavior is undefined by the language, the compiler can do nothing about it and should do nothing about it.
The exception to this is systems that have no memory protection and systems that intentionally map page zero. Some older systems do this, but most of the modern systems that do are microcontrollers, some of which might even have memory mapped I/O or some other special purpose memory in page zero.
Since null pointer dereferences are typically bugs, it is unlikely a compiler would bother to optimize away null pointer dereferences or put guard code around a possible one, as this would not improve code performance. If they did even bother to detect this, they would do it to emit a warning to assist you in debugging, similar to the "code not reachable" warning. The only reason for the compiler to generate different code around one would be if it knew what you were trying to do.
You seem to have a misunderstanding of what Undefined Behavior means.
Undefined Behavior is not something that is "caused" by your code. It is not something that happens. It is something that is.
If you have some piece of code somewhere that dereferences a null pointer, that is Undefined Behavior. UB gives the compiler a lot of leeway.
The way this is usually phrased is that the compiler is allowed to do anything. It is allowed to compile code that dereferences a null pointer into code that formats your hard disk. It is allowed to compile it into code that crashes. It is allowed to compile it into code that does random things. It is even allowed to compile it into code that doesn't crash.
And until a couple of years ago, that's mostly what compilers did. However, that isn't even the most dangerous part.
There is one thing the compiler is also allowed to do: because you are not allowed to write code that exhibits UB, the compiler is allowed to assume that there will be no UB, when optimizing your code. And because of the complex optimizations that modern compilers do, this can have very weird consequences.
Let's say you have an if (userId == 0) statement, where you have UB in the else part. Since you are not allowed to write code that exhibits UB, the compiler is allowed to assume that the else branch will never be taken. This means that the compiler is allowed to assume that userId will always be 0, i.e. it is allowed to assume that the user is always root! And based on this assumption, it is allowed to optimize away other checks as well, opening you up to huge security holes.
This can lead to very extreme, or even worse, very subtle changes to the behavior of program parts far away from the place of the UB.
You are mixing several concerns into one question here which amplifies the confusion in this matter. You should take care to distinguish:
- Your C++ program - Undefined behavior is undefined. Anything could happen. That is all that the standard says in this matter, and as your question is tagged C++, there is nothing more to say from a C++ perspective.
- The executed machine code - The compiler will most likely still produce some machine code for your attempted dereference operation. You won't be able to tell what machine code you will get beforehand, as the compiler can do whatever it wants due to the UB at play. But I understand your question in the way that you are observing the generated machine code after the translation: This machine code of course has well defined semantics in the context of the executing hardware platform and, potentially, influenced by the operating system it is run on. You can consult the documentation for both to find out what the exact behavior will be, and this will be the reliable outcome of the execution, regardless of what the C++ standard says. As you did not tag your target platform and OS in the question, I will refrain from guessing what will happen on your machine.
So in short: You won't know what happens by looking at the C++ code. You are, in principle, able to know what will happen when looking at the generated binary, given that you understand the behavior of your target platform well enough.
To answer the literal headline of your questions: there do exist hardware/OS configurations in the real world (in particular in embedded systems) where dereferencing the null address will not be caught by either the hardware or the OS, so you cannot rely on this always resulting in program termination by the environment.
In practice, should you care? Not unless you need to reason about the behavior of a pre-generated binary which you are unable to change. From a C++ perspective it is easy enough to perform a null check before dereferencing and (reliably) terminating the program manually if that is the desired outcome. Reasoning about program behavior after the actual dereferencing occurred is not sensible from a C++ perspective.
The short answer: dereferencing nullptr is UB and does not always result into a segmentation fault.
That is because it depends on:
- what the compiler does,
- what the program does.
I've seen code like this in production code (simplified in this example, the lines were obviously not close to each other in the actual case):
struct SomeType;
void f(SomeType const& ref)
{
if (&ref == nullptr)
// bailout
// ...
}
void g()
{
SomeType* p = nullptr;
f(*p);
}
Crazy, right? But you can see why that "works", if you assume that references are implemented as pointers by the compiler...
But, as mentioned in many of the comments, you should not write code like that. Although this is sometimes more easily said than done (we all write buggy code at some point...).
At the risk of going slightly off-topic, let me explain how the above issue was identified in the said production code, because it eventually caused a segmentation fault.
The check on nullptr was not in the code of the function, it was instead in the logic of dynamic_cast, which results in nullptr when applied to nullptr. Unfortunately, a new version of GCC came with an optimisation in dynamic_cast that removed the check on nullptr when the pointer was determined to not be nullable, as in the case of taking the address of a reference...
void f(SomeType const& ref)
{
if (dynamic_cast<SomeOtherType const*>(&ref) == nullptr)
// bailout
// ...
}
In this instance, the issue was relatively quickly identified and the code fixed... but this is just one example among many!
I thought that any dereferencing for a null pointer would result in an exception.
No. Dereferencing a null pointer is undefinded behavior in C++.
C++ is not Java. C++ does have exceptions, but they are only for exceptional casses, not used all over the place (as in Java). You are supposed to know that dereferencing a null pointer is not allowed, and a compiler assumes that it never happens in correct code. If it still happens your code is invalid.
Read about undefined behavior. It is essential to know about it when you want to do anything serious in C++.
What are the rules for a valid dereferencing of a null pointer?
The rule is: You shall not do it. When you do it your code is ill-formed no diagnostics required. This is a different way to say: Your code has undefined behavior. The compiler is not reuqired to issue an error or warning and when you ask a compiler to compile your wrong code the result can be anything.
What are the rules for a valid dereferencing of a null pointer [in C++]?
C++ standard is actually somewhat non-specific about whether indirecting through a null pointer is valid by itself or not. It is not disallowed explicitly. The standard used to use "dereferencing the null pointer" as an example of undefined behaviour, but this example has since been removed.
There is an active core language issue CWG-232 titled "Is indirection through a null pointer undefined behavior?" where this is discussed. It has a proposed change of wording to explicitly allow indirection through a null pointer, and even to allow "empty" references in the language. The issue was created 20 years ago, has last been updated 15 years ago, when the proposed wording was found insufficient.
Here are a few examples:
X* ptr = nullptr;
*ptr;
Above, the result of the indirection is discarded. This is a case where standard is not explicit about its validity one way or another. The proposed wording would have allowed this explicitly. This is also a fairly pointless operation.
X& x = *ptr;
X* ptr2 = &x; // ptr2 == nullptr?
Above, the result of indirection through null is bound to an lvalue. This is explicitly undefined behaviour now, but the proposed wording would have allowed this.
ptr->member_function();
Above, the result of indirection goes through lvalue-to-rvalue conversion. This has undefined behaviour regardless of what the function does, and would remain undefined in the proposed resolution of CWG-232. Same applies to all of your examples.
One consequence of this is that return this == nullptr; can be optimised to return false; because this can never be null in a well defined program.