简体   繁体   English

如何防止 memcpy 缓冲区溢出?

[英]How to prevent memcpy buffer overflow?

There are some binary buffers with fixed sizes in a program that are used to store data, and memcpy is used to copy the buffer from one to another one.程序中有一些固定大小的二进制缓冲区用于存储数据,memcpy 用于将缓冲区从一个缓冲区复制到另一个缓冲区。 Since the source buffer may be larger than the destination buffer, how can I detect if there is buffer overflow?由于源缓冲区可能大于目标缓冲区,如何检测是否存在缓冲区溢出?

You have to know how much data is in the source buffer and how much space is available in the target buffer.您必须知道源缓冲区中有多少数据以及目标缓冲区中有多少可用空间。

Do not call memcpy() if there is not enough space in the target buffer for all the data you want to copy from the source buffer.如果目标缓冲区中没有足够的空间用于您要从源缓冲区复制的所有数据,请不要调用memcpy() (You have to decide whether it is OK to truncate the data if the source is bigger than the target.) (如果源大于目标,您必须决定是否可以截断数据。)

If you don't know, rewrite the code so that you do know how much space there is;如果您不知道,请重写代码,以便您知道有多少空间; otherwise, it is not safe.否则,它是不安全的。

Note that if there is a chance of the source and target buffers overlapping, you should use memmove() rather then memcpy() .请注意,如果源缓冲区和目标缓冲区有可能重叠,则应使用memmove()而不是memcpy()

In C++, look askance at using memcpy() in the first place;在 C++ 中,首先斜视使用memcpy() that is a C-style operation rather than C++.这是一个 C 风格的操作,而不是 C++。

How can I detect if there is buffer overflow?如何检测是否存在缓冲区溢出?

I think you have three or four choices (give or take).我认为你有三四个选择(给予或接受)。


The first choice is to provide a "safe" function for memcpy .首选是为memcpy提供“安全”功能。 This is what I require in code under my purview, and I regularly audit for it.这是我在我职权范围内对代码的要求,我会定期对其进行审核。 I also require all parameters are validated, and all parameters are asserted.我还要求验证所有参数,并断言所有参数。

The assertions create self debugging code.断言创建自调试代码。 I want developers to write code;我希望开发人员编写代码; and I don't want them to waste time debugging.我不希望他们浪费时间调试。 So I require them to write code that debugs itself.所以我要求他们编写自我调试的代码。 ASSERTs also documents things rather well, so they can skimp on the documentation.断言也很好地记录了事情,所以他们可以跳过文档。 In release builds, the ASSERTs are removed by preporcessor macros.在发布版本中,ASSERT 被预处理器宏删除。

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;
}

If your safe_memcpy returns non-0, then there was an error like a bad parameter or potential buffer overflow.如果您的safe_memcpy返回非 0,则存在错误参数或潜在缓冲区溢出等错误。


The second choice is to use "safer" functions provided by the C Standard.第二种选择是使用 C 标准提供的“更安全”的函数。 C has "safer" functions via ISO/IEC TR 24731-1, Bounds Checking Interfaces . C 通过ISO/IEC TR 24731-1, Bounds Checking Interfaces具有“更安全”的功能。 On conforming platforms, you can simply call gets_s and sprintf_s .在符合标准的平台上,您可以简单地调用gets_ssprintf_s They offer consistent behavior (like always ensuring a string is NULL terminated) and consistent return values (like 0 on success or an errno_t ).它们提供一致的行为(例如始终确保字符串以NULL终止)和一致的返回值(例如成功时为 0 或errno_t )。

errno_t  err = memcpy_s(dest, dsize, src, cnt);
...

Unfortunately, gcc and glibc does not conform to the C Standard.不幸的是,gcc 和 glibc 不符合 C 标准。 Ulrich Drepper (one of the glibc maintainers) called bounds checking interfaces "horribly inefficient BSD crap" , and they were never added. Ulrich Drepper(glibc 维护者之一)将边界检查接口称为“极其低效的 BSD 垃圾” ,但从未添加过它们。


The third choice is to use the platform's "safer" interfaces, if present.第三种选择是使用平台的“更安全”接口(如果存在)。 On Windows, that happens to be the same as those in ISO/IEC TR 24731-1, Bounds Checking Interfaces .在 Windows 上,这恰好与ISO/IEC TR 24731-1, Bounds Checking Interfaces 中的相同 You also have the String Safe library.您还拥有 String Safe 库。

On Apple and BSD, you have don't have a "safer" function for memcpy .在 Apple 和 BSD 上,您没有memcpy的“更安全”功能。 But you do have safer string functions like strlcpy , strlcat and friends.但是你确实有更安全的字符串函数,比如strlcpystrlcat和朋友。


On Linux, your fourth choice is to use FORTIFY_SOURCE.在 Linux 上,您的第四个选择是使用 FORTIFY_SOURCE。 FORTIFY_SOURCE uses "safer" variants of high risk functions like memcpy , strcpy and gets . FORTIFY_SOURCE 使用高风险函数的“更安全”变体,如memcpystrcpygets The compiler uses the safer variants when it can deduce the destination buffer size.当编译器可以推导出目标缓冲区大小时,它会使用更安全的变体。 If the copy would exceed the destination buffer size, then the program calls abort() .如果副本将超过目标缓冲区大小,则程序调用abort() If the compiler cannot deduce the destination buffer size, then the "safer" variants are not used.如果编译器无法推断目标缓冲区大小,则不会使用“更安全”的变体。

To disable FORTIFY_SOURCE for testing, you should compile the program with -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0 .要禁用 FORTIFY_SOURCE 进行测试,您应该使用-U_FORTIFY_SOURCE-D_FORTIFY_SOURCE=0编译程序。

You should always know and check the src and dest buffers size !您应该始终知道并检查 src 和 dest 缓冲区大小!

void *memcpy(void *dest, const void *src, size_t n);

n should never be greater than src or dest size. n永远不应大于srcdest大小。

If for example you have:例如,如果您有:

destination 4 bytes size目标 4 字节大小

source 5 bytes size源 5 字节大小

You can make sure to copy, at most, 4 bytes to destination buffer:您可以确保最多将 4 个字节复制到目标缓冲区:

size_t getCopySize(size_t sourceSize, size_t destSize)
{
    return (destSize <= sourceSize ? destSize : sourceSize);
}
memcpy(destination, source, getCopySize(sizeof(source),sizeof(destination)));

Basing on your application you could also make sure that the remaining data will be copied at a later time, or you can skip it if some data can be ignored.根据您的应用程序,您还可以确保稍后复制剩余的数据,或者如果可以忽略某些数据,则可以跳过它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM