在一个程序中,有一些固定大小的二进制缓冲区用于存储数据,并使用 memcpy 将缓冲区从一个复制到另一个。由于源缓冲区可能比目标缓冲区大,因此如何检测是否存在缓冲区溢出?
在一个程序中,有一些固定大小的二进制缓冲区用于存储数据,并使用 memcpy 将缓冲区从一个复制到另一个。由于源缓冲区可能比目标缓冲区大,因此如何检测是否存在缓冲区溢出?
如何检测是否存在缓冲区溢出?
我的看法是你有三到四个选择(多少不等)。
memcpy
提供一个“安全”的函数。这是我在我的代码审查中要求的,我经常进行审核。我还要求验证所有参数,并断言所有参数。errno_t safe_memcpy(void* dest, size_t dsize, void* src, size_t ssize, size_t cnt)
{
ASSERT(dest != NULL);
ASSERT(src != NULL);
ASSERT(dsize != 0);
ASSERT(ssize != 0);
ASSERT(cnt != 0);
// What was the point of this call?
if(cnt == 0)
retrn 0;
if(dest == NULL || src == NULL)
return EINVALID;
if(dsize == 0 || ssize == 0)
return EINVALID;
ASSERT(dsize <= RSIZE_MAX);
ASSERT(ssize <= RSIZE_MAX);
ASSERT(cnt <= RSIZE_MAX);
if(dsize > RSIZE_MAX || ssize > RSIZE_MAX || cnt > RSIZE_MAX)
return EINVALID;
size_t cc = min(min(dsize, ssize), cnt);
memmove(dest, src, cc);
if(cc != cnt)
return ETRUNCATE;
return 0;
}
safe_memcpy
返回非0值,那么就意味着有错误,比如说坏的参数或者潜在的缓冲区溢出。
gets_s
和 sprintf_s
。它们提供一致的行为(例如始终确保字符串以 NULL
结尾)和一致的返回值(例如成功时返回 0 或一个 errno_t
)。errno_t err = memcpy_s(dest, dsize, src, cnt);
...
很遗憾,gcc和glibc不符合C标准。其中一位glibc维护者Ulrich Drepper称边界检查接口为"可怕低效的BSD垃圾",并且它们从未被添加。
memcpy
函数。但您确实拥有更安全的字符串函数,例如strlcpy
、strlcat
等。
memcpy
,strcpy
和gets
。编译器在可以推断目标缓冲区大小时使用更安全的变体。如果复制超过目标缓冲区大小,则程序调用abort()
。如果编译器无法推断目标缓冲区大小,则不使用“更安全”的变体。-U_FORTIFY_SOURCE
或-D_FORTIFY_SOURCE=0
编译程序。您需要知道源缓冲区中有多少数据,以及目标缓冲区中有多少可用空间。
如果目标缓冲区没有足够的空间来存放要从源缓冲区复制的所有数据,请不要调用memcpy()
。(您必须决定如果源比目标大时截断数据是否可行。)
如果您不知道有多少空间,则应重新编写代码以便了解有多少可用空间;否则,这样做是不安全的。
请注意,如果存在源和目标缓冲区重叠的可能性,则应使用memmove()
而不是memcpy()
。
在C++中,首先考虑不使用memcpy()
;因为它是一种C风格的操作,而不是C++风格。
std::vector<>
中,然后只需使用vector2 = vector1
即可。 - MSaltersvector
有一个名为insert
的成员函数。 - Steve Jessopinsert
执行的额外复制操作,那么你可以将向量中的一个元素的指针作为缓冲区传递给套接字的recv
函数。但是你必须确保向量的大小足够大。对向量进行resize
操作会清除字节,因此与写入未初始化数组相比仍然存在一些小的开销。 - Steve Jessop您应该始终知道并检查源和目标缓冲区的大小!
void *memcpy(void *dest, const void *src, size_t n);
n
不应大于src
或dest
的大小。
size_t getCopySize(size_t sourceSize, size_t destSize)
{
return (destSize <= sourceSize ? destSize : sourceSize);
}
memcpy(destination, source, getCopySize(sizeof(source),sizeof(destination)));
sizeof
只会给出指针的大小。 - juanchopanzadst
是一个指针,那么它将返回指针所指向的内存块的大小。如果dst
是其他类型的变量(例如数组),那么它将返回该变量的大小。 - James Kanze