关于readlink不返回空终止值的原因、考量及安全风险的技术问询
Great question—this is one of those POSIX API design quirks that trips up a lot of developers, but there's solid reasoning behind it, and misusing it absolutely can lead to nasty security holes. Let's dive in.
Why doesn't readlink null-terminate the buffer?
The decision to skip automatic null-termination is rooted in core UNIX design principles and practicality:
- Historical minimalism & single-responsibility: Early UNIX tools followed the "do one thing well" rule.
readlink's sole job is to fetch the raw content of a symbolic link—adding string termination logic would be an extra responsibility that belongs in user-space code, not a low-level system call. This keeps the API lean and aligned with UNIX's philosophy of separating concerns. - Support for arbitrary link content: Symbolic links can technically contain null bytes in their target paths (even if it's unusual). If
readlinkadded a null terminator automatically, any existing nulls in the link content would trick user code into thinking the string ends early, truncating the actual target. By leaving termination to the caller, the API correctly handles all possible byte sequences in links. - Efficiency & caller control:
readlinkreturns the exact number of bytes it read. Letting the caller manage null termination avoids wasting buffer space (no need to reserve an extra byte if you don't need a string) and gives developers full control—for example, if you're processing the link content as a raw byte stream instead of a C string, you don't want an extra null messing things up. It also lets you explicitly handle truncation cases (when the return value equals your buffer size, you know you need a bigger buffer).
Can improper use of readlink lead to security risks?
Absolutely—this is a common source of vulnerabilities, especially in code that assumes all data is null-terminated. Here are the most critical risks:
- Buffer overflows and invalid memory access: If you pass a non-null-terminated
readlinkbuffer to functions likestrlen,strcpy, orprintf("%s"), these functions will keep reading memory until they hit a random null byte. This can:- Crash your program (accessing invalid memory)
- Expose sensitive data from the stack/heap (like passwords, API keys, or memory addresses)
- Let attackers execute arbitrary code via buffer overflow (if the overwritten memory includes control structures like return addresses)
Example of dangerous code:
char buf[256]; readlink("/user/link", buf, sizeof(buf)); // ❌ buf isn't null-terminated—strlen will read past the buffer! size_t path_len = strlen(buf); - Truncated path attacks: If your buffer is too small and you don't check the return value, you'll get a truncated version of the link target. Attackers can exploit this by creating extra-long symbolic links that trick your program into accessing a different file than intended. For example, a program that reads a link to a config file might end up opening a malicious file in a parent directory if the path is truncated.
- Information disclosure: When non-null-terminated buffers are logged or displayed to users, they often include garbage data from adjacent memory. This can leak internal program state (like memory layout details) that attackers can use to plan more sophisticated exploits.
How to use readlink safely
To avoid these issues, follow these best practices:
- Always check the return value of
readlink(it returns-1on error, or the number of bytes read on success). - Reserve space for a null terminator when allocating your buffer, then explicitly add it after a successful call:
#define BUF_SIZE 1024 char buf[BUF_SIZE]; ssize_t bytes_read = readlink("/safe/link", buf, BUF_SIZE - 1); // Leave 1 byte for null if (bytes_read != -1) { buf[bytes_read] = '\0'; // Explicitly terminate } else { // Handle error (e.g., perror("readlink failed")) } - Use length-limited string functions (like
strnlen,strncpy) instead of their unsafe counterparts when working with the buffer. - If you suspect the link target might be longer than your buffer, reallocate a larger buffer and retry the call until you get the full content.
内容的提问来源于stack exchange,提问作者Yam Mesicka
相关产品推荐
相关产品推荐

