
The defect that is rightly considered to be the “plague” of modern programming is surmountable. We offer you to get acquainted with the translation of the article by Bertrand Meyer, a French scientist, creator of the Eiffel programming language, visiting professor and head of the Laboratory of Software Engineering at Innopolis University. The original article was
published in the journal Communications of the ACM.
The code is important - we talked about this in the previous
article . Programming languages play an equally important role. Despite the fact that Eiffel is better known for the principles of Design by Contract (“contract design”), they are only part of systematic design, the main purpose of which is to help developers realize their maximum potential and eliminate sources of failures and errors from code.
')
Speaking about the sources of failures, it is worth mentioning null pointer dereferencing - a defect that is rightly considered to be the “plague” of modern programming. This term refers to a phenomenon that occurs when you make a call to
xf , meaning "to apply the component
f (field access or operation) to the object that
x refers to." If your task is to define meaningful data structures, you must allow the use of a null value, also known as Nil or Void, as one of the possible values of the reference variables (for example, to complete the associated data structures: the "next" field of the last element of the list must be zero to indicate that the next item is missing). Next, you should make sure that the call
xf never applies to the zero value of
x , because in this case there is no object to which
f is applied.
This problem is quite relevant for object-oriented languages, in which calls of the form
xf are the key mechanism. The risk of uncertain behavior arises with each use of this mechanism (how many billions of such cases have already occurred by the time you read this article?). Compilers for most programming languages catch other errors of a similar nature - in particular, errors related to the type of declaration, for example, when an incorrect value is assigned to a variable. However, compilers cannot prevent null pointer dereferencing.
Null pointer dereferencing is a major vulnerability that threatens the implementation of most modern programs. According to Tony Hoare, the null pointer dereference is "
a billion-dollar error ." And this is not an exaggeration. Alexander Kogtenkov, in his Ph.D.
thesis, investigated defects related to the dereference of a null pointer, based on the database of typical vulnerabilities and risks (CVE), which contains information about Internet attacks. The results of the study are presented in a graph showing the total number of attacks per year.

Behind the number of attacks are frightening real cases. Based on the CVE-2016-9113 vulnerability
description :
Dereferencing a NULL pointer in the convertbmp.c module imagetobmp function: OpenJPEG 2.1.2 image-> comps [0] .data does not assign a value after initialization (NULL). The result is denial of service.Yes, this is the case with the JPEG standard. Try not to think about it by uploading your photos to the network. In just one month (November 2016), vulnerabilities associated with null pointer dereference were recorded in the system database, affecting Gotha's products in the IT industry, starting from
Google and
Microsoft (“theoretically, anyone could crash the server by building just one” special "data packet") to
Red Hat and
Cisco .
NVIDIA commented on it this way: NVIDIA Quadro products, NVS and Ge-Force, as well as NVIDIA Windows GPU Display Driver R340 versions up to 342.00 and R375 versions up to 375.63 discovered a driver vulnerability (nvlddmkm.sys), where dereferencing of the NULL pointer caused by invalid input may result in denial of service or potential escalation of privileges.
People often complain that security and the Internet are incompatible things. But what if the problem is not only in design (the TCP / IP protocol stack functions perfectly), but in the programming languages that are used to write the means of implementing these protocols?
As for the programming language Eiffel, we decided it was time to solve this problem. Previously, we eliminated insecure type conversions using the type system, got rid of memory management errors using garbage collection, and data race defects using the SCOOP mechanism. It's time to solve the problem of null pointer dereferencing. Now there is no problem of unsafe calls in Eiffel - null pointer dereference is impossible here in principle. By accepting your program, the compiler ensures that each time you execute a call to xf, the variable x will refer to a specific object that actually exists.
How did we do it? In this article we will not describe in detail how to prevent the dereferencing of null pointers by limiting ourselves to a reference to a resource with
documentation . Note also that the mechanism is constantly being improved. In this article we will talk about the main ideas. The original
article on this topic was the main report at the European Conference on Object Oriented Programming (ECOOP) in 2005. Several years later, reviewing the original decision in one of the
articles , I wrote: “It took several weeks to develop, improve and describe void safety technology based on the mechanism of protection against dereferencing null pointers. Engineering work took four years. ”
It sounded optimistic. Seven years later, "engineering work" continued. And the point is not to protect null pointers from dereferencing - the mechanism was initially theoretically sufficiently substantiated. The purpose of the protracted refinement of the concept was to facilitate the work of programmers. Any mechanism devoid of bugs, for example, static typing, provides reliable protection and security, according to the following formula: “prohibit malicious schemes (otherwise defects cannot be avoided), while retaining useful ones (otherwise it would be too easy to get rid of defects! All you need to do is remove all the programs!) while not changing the way they work. ” The so-called "engineering works" include a detailed static analysis, due to which the compiler takes safe types that would be rejected by a more simplified solution.
In practice, the complexity of optimizing solutions for protecting against dereferencing null pointers is more related to the initialization of objects. Details of the mechanism can be constantly improved, but the idea itself is simple: the mechanism is based on type declaration and static analysis.
The system of protection against dereferencing null pointers distinguishes between "attached" and "detachable" (detachable) types. If you typify the variable
p1 with a specific type (for example, PERSON), it will never be zero - its value will always be a reference to an object of this type, i.e. variable
p1 is "attached". This is the default. If you want the
p2 variable to take the zero value, label it as “detachable” -
detachable PERSON . Simple compilation mechanisms support this distinction: you can assign
p1 to
p2 , but not vice versa. Thus, the "attached" expression is correct: during the execution of the program, the value of
p1 will always be non-zero. The compiler formally guarantees this.
When static analysis of such guarantees is much more, and it does not require any effort on the part of programmers, if the code is safe. For example, if the code snippet looks like this:
if p2 / = Void then p2.f end , we know that everything is in order (under certain conditions. In multi-threaded programming, for example, it is important that the parallel flow does not null the variable
p2 between the time it is tested and the application
f . This is provided by the rules ).
Of course, the actual definition of the mechanism does not guarantee that the compiler will recognize safe cases and will reject unsafe. We cannot simply entrust the security of a program to a software tool (even open source tools such as Eiffel compilers). In addition, there is more than just a compiler. The definition of void safety uses a series of simple and clear rules, known as certified attachment patterns (CAPs), that compilers must follow. The previous example illustrates one of these certified templates. The formal model, supported by mechanized evidence (using the Isabelle / HOL tool), provides strong
evidence of the validity of these rules, including sensitive issues related to initialization.
The void safety technology has existed for several years now, and those who have used it do not want to return to the old safety methods of null pointers. Creating secure code is quickly becoming commonplace.
Are you sure that your code is protected from null pointer dereferencing?