Main Content

Misuse of readlink()

Third argument of readlink does not leave space for null terminator in buffer

Description

This defect occurs when you pass a buffer size argument to readlink() that does not leave space for a null terminator in the buffer.

For instance:

ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf));
The third argument is exactly equal to the size of the second argument. For large enough symbolic links, this use of readlink() does not leave space to enter a null terminator.

Risk

The readlink() function copies the content of a symbolic link (first argument) to a buffer (second argument). However, the function does not append a null terminator to the copied content. After using readlink(), you must explicitly add a null terminator to the buffer.

If you fill the entire buffer when using readlink, you do not leave space for this null terminator.

Fix

When using the readlink() function, make sure that the third argument is one less than the buffer size.

Then, append a null terminator to the buffer. To determine where to add the null terminator, check the return value of readlink(). If the return value is -1, an error has occurred. Otherwise, the return value is the number of characters (bytes) copied.

Examples

expand all

#include <unistd.h>

#define SIZE1024 1024

extern void display_path(const char *);

void func() {
    char buf[SIZE1024];
    ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf));
    if (len > 0) {
        buf[len - 1] = '\0';
    }
    display_path(buf);
}

In this example, the third argument of readlink is exactly the size of the buffer (second argument). If the first argument is long enough, this use of readlink does not leave space for the null terminator.

Also, if no characters are copied, the return value of readlink is 0. The following statement leads to a buffer underflow when len is 0.

buf[len - 1] = '\0';

Correction — Make Sure Size Argument is One Less Than Buffer Size

One possible correction is to make sure that the third argument of readlink is one less than size of the second argument.

The following corrected code also accounts for readlink returning 0.

#include <stdlib.h>
#include <unistd.h>

#define fatal_error() abort()
#define SIZE1024 1024

extern void display_path(const char *);

void func() {
    char buf[SIZE1024];
    ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf) - 1); 
    if (len != -1) {
        buf[len] = '\0';
        display_path(buf);
    }
    else {
        /* Handle error */
        fatal_error();
    }
}

Result Information

Group: Security
Language: C | C++
Default: Off
Command-Line Syntax: READLINK_MISUSE
Impact: Medium

Version History

Introduced in R2017a

expand all