...
If len
is equal to sizeof(buf)
, the null terminator is written one 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);
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.
...
This compliant solution ensures there is no overflow by only reading in only sizeof(buf)-1
characters. It also properly checks to see if an error has occurred.:
Code Block | ||||
---|---|---|---|---|
| ||||
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 */
}
|
...
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. |
...
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
...
References
Wiki Markup |
---|
\[[ilja 06|AA. References#ilja 06]\]
\[[MITRE 07|AA. References#MITRE 07]\] [CWE ID 170|http://cwe.mitre.org/data/definitions/170.html], "Improper Null Termination"
\[[Open Group 97a|AA. References#Open Group 97]\]
\[[Open Group 04|AA. References#Open Group 04]\] |
POS03-C. Do not use volatile as a synchronization primitive 50. POSIX (POS) POS31-C. Do not unlock or destroy another thread's mutex