The readlink()
function reads where a link points to. It makes no effort to null-terminate its second argument, buffer
. Instead, it just returns the number of characters it has written.
Noncompliant Code Example
If len
is equal to sizeof(buf)
, the null terminator is written one byte past the end of buf
.
char buf[1024]; ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf)); buf[len] = '\0';
An incorrect solution to this problem is to try to make buf
large enough that it can always hold the result.
long symlink_max; size_t bufsize; char *buf; ssize_t len; errno = 0; symlink_max = pathconf("/usr/bin/", _PC_SYMLINK_MAX); if (symlink_max == -1) { if (errno != 0) { /* handle error condition */ } bufsize = 10000; } else { bufsize = symlink_max+1; } buf = (char *)malloc(bufsize); if (buf == NULL) { /* handle error condition */ } len = readlink("/usr/bin/perl", buf, bufsize); buf[len] = '\0';
This modification incorrectly assumes that the symbolic link cannot be longer than the value of SYMLINK_MAX
returned by pathconf()
. However, the value returned by
pathconf()
is out of date by the time readlink()
is called, and so the off-by-one buffer overflow risk is still present because in between the two calls, the location of /usr/bin/perl
can change to a file system with a larger SYMLINK_MAX
value. Also, if SYMLINK_MAX
is indeterminate (that is, if pathconf()
returned -1 without setting errno
), the code uses an arbitrary large buffer size (10,000) that it hopes will be sufficient, but there is a small chance that readlink()
can return exactly this size.
An additional issue is that readlink()
can return -1
if it fails, causing an off-by-one underflow.
Compliant Solution
This compliant solution ensures there is no overflow by only reading in sizeof(buf)-1
characters. It also properly checks to see if an error has occurred.
enum { BUFFERSIZE = 1024 }; char buf[BUFFERSIZE]; ssize_t len = readlink("/usr/bin/perl", buf, sizeof(buf)-1); if (len != -1) { buf[len] = '\0'; } else { /* handle error condition */ }
Risk Assessment
Failing to properly null-terminate the result of readlink()
can result in abnormal program termination and buffer-overflow vulnerabilities.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
POS30-C |
high |
probable |
medium |
P12 |
L1 |
Automated Detection
Compass/ROSE can detect violations of this rule.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[ilja 06]
[MITRE 07] CWE ID 170, "Improper Null Termination"
[Open Group 97a]
[Open Group 04]
50. POSIX (POS) CON31-C. Do not unlock or destroy another thread's mutex