CVE-2021-33910 (i.e. “the systemd bug”)

Published

One of the readers of MAB Labs’ 0xB105F00D newsletter (you can sign up at https://mab-labs.com/#b105f00d) pointed out the recent systemd vulnerability that was discovered by Qualys. While the technical write-up may be found on Qualys’ website at https://blog.qualys.com/vulnerabilities-threat-research/2021/07/20/cve-2021-33910-denial-of-service-stack-exhaustion-in-systemd-pid-1, this blog post focuses on two aspects of this vulnerability. First, we would like to speculate as to why a developer would make this change. Second, let’s work through the systemd source code and subsequent documentation to get a better understanding of the vulnerability.

Let’s speculate as to why a developer would make the change from strdup to strdupa, which is the source of the vulnerability as mentioned in the writeup. Again, this is pure speculation as we place ourselves in the shoes of the developer that made this change and think about why it would have been made in the first place. 

As mentioned, the simple change from the strdup to strdupa resulted in using the stack to store a copy of the path instead of the heap. As most of us are aware, the typical pattern of (ultimately) allocating a variable on the heap using malloc is the following:

#include <string.h>
.
.
int *p;
p = (int *) malloc(sizeof(int));
if (p == NULL) {
    // Error message
    // return code
}
.
.
.
free(p);

When the original strdup function was used in the unit_name_path_escape function, the call to free would ultimately need to be made, whether directly by the developer or indirectly by other means (which we’ll see later).

Now, we may want to remove the call to the free to improve the performance of our implementation. In order to do that we would need to change the implementation from a heap-based solution to a stack-based solution. If we read the documentation for strdup (https://linux.die.net/man/3/strdupa), we notice that there is an entry to use a stack based solution, saving us the call to free: 

If we look at the documentation for alloca (https://linux.die.net/man/3/alloca), we can see that it uses to the stack to allocate memory (which is automatically freed):

Using strdupa looks to be a great replacement and it will save us a free, thus improving the performance of our implementation. 

Now that we have speculated as to why a developer would make the change, let’s walk through the source code and documentation to better understand the vulnerability. First, we can clone the systemd repository, located at https://github.com/systemd/systemd on the command line for analysis by executing the following command:

$> git clone https://github.com/systemd/systemd

Based on the writeup from Qualys, we know that the function unit_name_path_escape was the location of the call to strdupa. We can find the source file where this function resides, by executing the following command:

$ systemd> grep -r "unit_name_path_escape" *

and we discover that it’s in the source file “src/basic/unit-name.c”.

If we execute the following command, we can see the git history of this source file:

$ systemd> gitk src/basic/unit-name.c

We can see that originally, the call to strdupa was made and the value of the pointer that was returned was checked to make sure that it is not NULL. However, if we refer to the documentation of alloca (which is the underlying function that allocates memory for strdupa), we can see that this check is irrelevant:

The last sentence in the above snippet is important. It says that if we’re trying to allocate too much memory (remember, we’re allocating memory on the stack and so a stack overflow means that we’re allocating more memory than is available to us on the stack), then all bets are off and it’s pointless to perform the check in the function above.

In summary, we speculated as to why someone would want to make the change from using the heap to stack for memory allocation. We analyzed the systemd source code and documentation to get a better understanding of this vulnerability. While this was a simple change, it reminds us of the importance of reading the documentation of the functions we are using to ensure that there are no adverse side effects. It also reminds us that a stack overflow is still a valid and potentially catastrophic vulnerability that we need to be aware of in our implementations.