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. the function with its arguments is:
readlink(link, buf, len);
Non-Compliant Solution
readlink() never 0-terminates by itself, so you have to do it by yourself. People often seem to forget this, leading to infoleaks or sometimes memory corruption.
another thing people like to do is:
Noncompliant Code Example
If len
is equal to sizeof(buf)
, the null terminator is written 1 byte past the end of buf
:
Code Block | ||||
---|---|---|---|---|
| ||||
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:
Code Block | ||||
---|---|---|---|---|
| ||||
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 | ||||
Code Block | ||||
len = readlink(link, buf, sizeof(buf));
buf[len] = '\0';
|
There are two problems here, readlink() can return -1 if it fails and hence causing an 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, so the off-by-one buffer-overflow risk is still present because, 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 underflow, so always check the readlink return value. The other problem that can occur is that readlink returns how many byted got written to the buffer, in this case it can write up to sizeof(buf) bytes. if it does you basicly end up doing:
bufsizeof(buf) = '\0'; which is an off-by-one overflowunderflow.
Compliant Solution
This compliant solution ensures there is no overflow by reading in only sizeof(buf)-1
characters. It also properly checks to see if an error has occurred:
Code Block | ||||
---|---|---|---|---|
| ||||
enum { BUFFERSIZE = 1024 }; #include <unistd.h> char buf[1024BUFFERSIZE]; ssizetssize_t len; ... if ((len = readlink("/modulesusr/bin/pass1perl", buf, sizeof(buf)-1)); if (len != -1) { buf[len] = '\0'; } else { /* handle error condition */ } |
References
...
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
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Astrée |
| Supported: Can be checked with appropriate analysis stubs. | |||||||
Axivion Bauhaus Suite |
| CertC-POS30 | |||||||
CodeSonar |
| LANG.MEM.BO | Buffer Overrun | ||||||
Compass/ROSE | |||||||||
Coverity |
| READLINK | Implemented | ||||||
Helix QAC |
| C5033 | |||||||
Klocwork |
| ABV.GENERAL | |||||||
Parasoft C/C++test |
| CERT_C-POS30-a | Avoid overflow due to reading a not zero terminated string | ||||||
| CERT C: Rule POS30-C | Checks for misuse of readlink() (rule partially covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
---|---|---|
CWE 2.11 | CWE-170, Improper null termination | 2017-06-13: CERT: Rule subset of CWE |
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-170 and POS30-C
CWE-170 = Union( POS30-C, list) where list =
- Non-null terminated strings fed to functions other than POSIX readlink()
Bibliography
...
...