Memory corruption and use-after-free

Many system components are written in the unsafe C/C++ languages that are prone to memory corruption vulnerabilities. Over more than 20 years, attackers have been abusing these vulnerabilities to compromise and subvert the system. Of course there have been much of defense side research efforts to stop these issues, but it is not quite close to be perfect --- many of them take too radical approaches so it involves either the changes of the underlying running system or is not quite suitable for real-world applications.

Today, use-after-free is generally known as one of the most difficult vulnerability type. Many modern C/C++ applications are developed under object-oriented or event-driven designs, which in turn separates out the memory resource use and free patterns. Due to this separation, developers cannot easily understand whether a certain memory pointer may point to a valid memory object or not, and a use-after-free vulnerability is introduced if they misunderstand. From the perspective of static analysis, use-after-free is a still challenging problem with a similar reason --- separated use and free routines require a precise points-to analysis with a complete inter-procedural analysis.

DangNull: Nullifying dangling pointers

Use-after-free example

class Div: Element;
class Body: Element;
class Document {
  Element* child;
};

// (a) memory allocations
Document *doc = new Document();
Body *body = new Body();
Div *div = new Div();

// (b) using memory: propagating pointers
doc->child = body;
body->child = div;

// (c) memory free: doc->child is now dangled
delete body;

// (d) use-after-free: dereference the dangled pointer
if (doc->child)
    doc->child->getAlign();

Above code snippet shows a contrived example having use-after-free issues. A pointer doc->child points to the invalid memory region after body is deleted. Thus, doc->child is a dangling pointer, and use-after-free occurs once the pointer doc->child is dereferenced.

Use-after-free could have been prevented if all the pointers pointing to the object to be freed are properly nullified (e.g., nullifying doc->child once body is deleted). However, such nullification is not straightforward for developers because doc->child is essentially a back-pointer to body and a list of back-pointers are known when the object is freed.

Nullifying dangling pointers

Motivated by this hardness for developers, we developed DangNull that automatically nullifies all potential dangling pointers and thus prevents use-after-free. By instrumenting additional code and maintaining extra metadata, it can trace object relationships via pointers and nullifies the pointers once it is dangled. For example, the above vulnerable code will be transformed into the code like below.

doc->child = body;
+ trace(&doc->child, body);

body->child = div;
+ trace(&body->child, div);

// delete operator is intercepted,
// and all back-pointers including doc->child are nulified.
delete body;

// doc->child is NULL, so use-after-free is prevented.
if (doc->child)
    doc->child->getAlign();

While intercepting all allocations and deallocations, it also instruments an extra runtime function call, trace(), to keep track of object relationships via pointers. One interesting property of DangNull is in reusing null-pointer checks in the program. Because the automatic nullification of dangling pointers are actually the runtime semantics that developers intended and had to implement, DangNull fills up the missing semantic hole and keeps the program alive even after the use-after-free attempts. We have implemented this idea using LLVM compiler infrastructures, and instrumented the Chromium browser (please refer the paper [1] for details).

Demo

The following video clip shows the demo on Chromium hardened with DangNull. We tested using a use-after-free exploit, CVE-2013-2909, and it shows how DangNull stops the exploit --- it stops the exploit attempts by triggering a segmentation fault due to null-dereference (we do a little bit more extra jobs to safely contain this exception, which we call safe-null dereference). When the nullification value is NULL, the Chromium browser can correctly render the exploit page as if the vulnerability is patched.


[1] Preventing Use-after-free with Dangling Pointers Nullification. Byoungyoung Lee, Chengyu Song, Yeongjin Jang, Tielei Wang, Taesoo Kim, Long Lu, Wenke Lee. Published in NDSS 2015. [PDF]