strlcpy/strlcat 終究還是沒能加入 glibc
四 05 五月 2022 by ols3這算是程式語言考古吧! ;-)
儘管許多人曾倡議 glibc 應加入 strlcpy/strlcat,以避免日益嚴重的 buffer overflow 攻擊,但是這個二十幾年來的期望,終究沒有實現;至今,glibc 仍把這兩個函式排除在外!
(到底 glibc 是有多厭惡 strlcpy/strlcat? ;-) )
請參閱 glibc 的 FAQ: Why no strlcpy / strlcat?
glibc 所持的理由是:這兩個函式沒有辦法完全避免目標區出現緩衝區溢位,而且,字串可能會被無聲無息地截斷,不但增加複雜度,而且效率低落,因此,glibc 最終還是沒有接受 strlcpy/strlcat。
glibc 認為不必修改程式碼,使用 gcc -D_FORTIFY_SOURCE 一樣可以抓出問題;若不考慮效能,snprintf 函式也能做到,那加入 strlcpy/strncpy 作啥?
奇怪的是,在 BSD 族群中(OpenBSD/FreeBSD...),strlcpy/strlcat 卻是基本配備,尤其 OpenBSD 更把 strlcpy/strlcat 當成是他們加強程式碼安全的新技術,兩個陣營的思維可謂截然不同。
由於 glibc 沒有 strlcpy/strlcat,在 Linux 中若想使用這兩個函式,只能另外安裝 libbsd 或 libbsd-dev:
sudo apt install libbsd-dev
test-strlcpy.c:
#include <bsd/bsd.h>
#include <stdio.h>
int main()
{
char buff[5];
char src[10] = "abcdefgh";
strlcpy(buff, src, sizeof(buff));
printf("%s\n", buff);
return 0;
}
編譯執行:
gcc test-strlcpy.c -lbsd
./a.out
執行結果:
abcd
我們來看看 libbsd-dev 中的 strlcpy.c 如何實作:
#include <sys/types.h>
#include <string.h>
/*
* Copy string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}
其實和 OpenBSD 的 strlcpy.c 是一樣的:
/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */
#include <sys/types.h>
#include <string.h>
/*
* Copy string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}
DEF_WEAK(strlcpy);
您支持哪一派?
其實我支持 BSD,看看 OpenBSD 的安全成就: Only two remote holes in the default install, in a heck of a long time!,就知道了,或許 OpenBSD 是對的! ;-)