简体   繁体   English

C ++ memset / memcpy / strcpy实现-检查缓冲区溢出

[英]C++ memset / memcpy / strcpy implementation - check for buffer overflows

I have made basic memset / memcpy / strcpy implementations in C++, which work fine. 我已经在C ++中完成了基本的memset / memcpy / strcpy实现,可以正常工作。 However, is there a way of detecting buffer overflows if I was to do something like this: 但是,如果我要执行以下操作,是否可以检测缓冲区溢出:

Example: 例:

int main()
{
    char *buf = (char *)calloc(10, sizeof(char));
    __strcpy(buf, "Hello World"); 
    // buffer size: 10, copy size: 12 (including '\0') - overflow
}

Implementations ( typedef unsigned int UINT ): 实现( typedef unsigned int UINT ):

void *__memset(void *_Dst, int _Val, UINT _Size)
{
    UINT *buf = (UINT *)_Dst;
    while (_Size--)
    {
        *buf++ = (UINT)_Val;
    }
    return _Dst;
}

void *__memcpy(void *_Dst, const void *_Src, UINT _Size)
{
    UINT *buf = (UINT *)_Dst;
    UINT *src = (UINT *)_Src;
    while (_Size--)
    {
        *buf++ = *src++;
    }
    return _Dst;
}

char *__strcpy(char *_Dst, const char *_Src)
{
    while ((*_Dst++ = *_Src++) != '\0');
    return _Dst;
}

Buffer overflows are not detectable in your program. 在程序中无法检测到缓冲区溢出。 The operating system is detecting them. 操作系统正在检测它们。 You only can check for potential pitfalls in the code (if/else,asserts,exceptions). 您只能在代码中检查潜在的陷阱(如果/否则,则断言,例外)。 Or you use profiling tools like valgrind. 或者您使用诸如valgrind之类的配置工具。

You can detect overflows, but only if you also implement your own memory-management routines. 您可以检测到溢出,但前提是您还必须实现自己的内存管理例程。 We used to do this when we wrote embedded software that ran on devices without a "real" operating system, before there were very many good debugging tools. 在编写非常好的调试工具之前,当我们编写在没有“真实”操作系统的设备上运行的嵌入式软件时,我们常常这样做。

The idea is to build your own wrapper around malloc() (and calloc() , in your case), that will allocate a few more bytes than the caller requests. 这个想法是围绕malloc() (和您的情况下的calloc()构建您自己的包装器,该包装器将分配比调用方请求更多的字节。 Then set up a few "guard bytes" before and after the memory that was requested and initialize the entire buffer with recognizable data. 然后在请求的内存前后设置一些“保护字节”,并使用可识别的数据初始化整个缓冲区。 Also build a wrapper around free() that checks for the guard bytes before releasing the memory and generates an error if they've changed. 还要在free()周围构建一个包装器,该包装器在释放内存之前检查保护字节,如果更改,则会生成错误。

#define GUARD_LEN = 4   // Arbitrary number of guard bytes.
#define GUARD_BYTE = 0xA5  // Arbitrary but recognizable: 10100101b
#define UNUSED_BYTE = 0x96 // Arbitrary but recognizable: 10010110b
#define FREED_BYTE = 0xC3  // Arbitrary but recognizable: 11000011b
#define MAX_ALLOCS = 1024  // Max # of malloc'ed buffers.
struct {
  void *addr;  // Address of malloc'ed buffer
  size_t len;  // Number of requested bytes
} Allocs[MAX_ALLOCS];

// Allocates and initializes memory.
void *chk_malloc(size_t length) {
  // Allocate memory for buffer + guard bytes.
  void *mem = malloc(length + 2*GUARD_LEN);
  if (mem == NULL) {
    return NULL;
  }

  // Initialize: [GUARD][UNUSED_BUFFER][GUARD]
  // Caller's usable memory starts after GUARD.
  void *buffer = mem + GUARD_LEN;
  memset(mem, GUARD_BYTE, GUARD_LEN);
  memset(buffer, UNUSED_BYTE, length);
  memset(buffer + length, GUARD_BYTE, GUARD_LEN);

  // Remember the address and length.
  // Simplified for demonstration; you may want this to be smarter.
  for (int i = 0; i < MAX_ALLOCS; ++i) {
    if (Allocs[i].addr == NULL) {
      Allocs[i].addr = buffer;
      Allocs[i].len = length;
      return buffer;
  }
  return NULL;  // Should also indicate MAX_ALLOCS is too small.
}

// Checks that buffer is filled with val.
bool chk_filled(void *buffer, char val, size_t len) {
  for (int i = 0; i < len; ++i) {
    if (buffer[i] != val) {
      return false;
    }
  }
  return true;
}

// Checks for over/underrun and releases memory.
void chk_free(void *buffer) {
  // Find the buffer in the array of alloc'ed buffers.
  for (int i = 0; i < MAX_ALLOCS; ++i) {
    if (Allocs[i].addr == buffer) {
      void *guard = buffer - GUARD_LEN;  // Initial guard bytes.
      if (!chk_filled(guard, GUARD_BYTE, GUARD_LEN)) {
        // Underrun
      }
      end_guard = buffer + Allocs[i].len;    // Terminal guard bytes.
      if (!chk_filled(end_guard, GUARD_BYTE, GUARD_LEN)) {
        // Overrun
      }

      // Mark the buffer as free and release it.
      memset(guard, FREED_BYTE, Allocs[i].len + 2*GUARD_LEN);
      Allocs[i].addr = -Allocs[i].addr;  // See text below.
      free(guard);
      return;
    }
  }
  // Error: attempt to free unalloc'ed memory.
}

In reality you'd probably want this to be smarter in several ways: 实际上,您可能希望通过以下几种方式使其更智能:

  • You might not want to have a limit of MAX_ALLOCS . 您可能不希望有MAX_ALLOCS的限制。
  • Check for allocated memory that isn't freed when the program exits. 检查程序退出时是否释放的已分配内存。
  • Print Allocs[] on exit. 在出口上打印Allocs[]
  • Print more information and/or exit immediately when an error is detected. 检测到错误时,打印更多信息和/或立即退出。

The safer way to detect buffer overflows is by providing your own implementation of calloc instead. 检测缓冲区溢出的更安全方法是提供您自己的calloc实现。 Provide a few bytes padding before and after the returned block, set them to a known value (NOT 0 or 255), and when calling free check that they're untouched. 在返回的块之前和之后提供一些字节填充,将它们设置为已知值(NOT 0或255),并在调用free检查它们是否未被触动。 Also, after calling free you should overwrite the whole block (including the padding on both sides) to check for double free calls. 同样,在free通话之后,您应该覆盖整个块(包括两侧的填充)以检查是否有双重free通话。

All your mem* functions are invalid. 您所有的mem *函数均无效。 They copy (or set) objects of type unsigned int while they have to copy (or set) objects of type unsigned char. 他们复制(或设置)unsigned int类型的对象,而他们必须复制(或设置)unsigned char类型的对象。 Take into account that __Size can be an odd number. 考虑到__Size可以是一个奇数。 These functions theirself are unable to check buffer overflow without changing their declarations. 这些函数本身无法在不更改其声明的情况下检查缓冲区溢出。

Also even the third function is invalid 甚至第三个功能也是无效的

char *__strcpy(char *_Dst, const char *_Src)
{
    while ((*_Dst++ = *_Src++) != '\0');
    return _Dst;
}

inside the function pointer _Dst was changed and points to the terminating zero. 函数指针_Dst内部的值已更改,并指向终止的零。 This address you return from the function while you have to return the address of the first character of the string pointed by _Dst. 从函数返回的地址,而必须返回_Dst指向的字符串的第一个字符的地址。

The same is valid for the first two functions. 前两个功能同样有效。

You may need the size of buffer you are owing, which can be used to iterate that many locations to copy checking buffer overflow if any as shown below, 您可能需要欠下的缓冲区size ,该size可用于迭代许多位置以复制缓冲区,检查缓冲区是否溢出,如下所示,

char *__strcpy(char *_Dst, const char *_Src, int size)
{
   while ((*_Dst++ = *_Src++) != '\0' && size--); //Iterate/copy to allocated Bytes
   return _Dst;
}

int main()
{
    int size;
    char *buf = (char *)calloc(size, sizeof(char)); // Here you know the size of buf
    __strcpy(buf, "Hello World", size);  // Send size as parameter
   // buffer size: 10, copy size: 12 (including '\0') - overflow
}

There can be a real C++ implementation, if you use arrays instead of pointers to char. 如果使用数组而不是指向char的指针,则可能存在真正的C ++实现。 You could define your functions as template and make the array size a template argument. 您可以将函数定义为模板,并使数组大小为模板参数。

template < std::size_t D >
char* strcpy( char ( &dest )[ D ], const char* source )
{
    assert( D > std::strlen( source ) );
    ...
}

As you really just need the destination size, I've left the source size out. 由于您确实只需要目标大小,因此我省略了源大小。

int main()
{
    char buf[ 10 ];
    // would assert, if assertions are enabled.
    strcpy( buf, "Hello World" ); 
}

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

相关问题 正确使用memset和memcpy在C ++中初始化字符数组 - Using memset and memcpy correctly to initialize a character array in C++ C++/Arduino:无符号字符上的 strcpy()、strncpy() 和 memcpy() 不起作用 - C++/Arduino: strcpy(), strncpy() and memcpy() on unsigned char not working C++ char 指针的 memcpy/strcpy 指向 class 成员 char 指针 - C++ memcpy/strcpy of char pointer to class member char pointer 将整数复制到缓冲区 memcpy C++ - Copying an integer to a Buffer memcpy C++ Address Sanitizer with Visual C++:忽略读取缓冲区溢出,同时仍捕获写入缓冲区溢出 - Address Sanitizer with Visual C++: ignore read buffer overflows while still catching write buffer overflows 裸机嵌入式 c++:将 flash 写入 ram 时未定义对 memcpy 和 memset 的引用 - bare metal embedded c++: undefined reference to memcpy and memset while writing flash to ram TCP memcpy缓冲区使用C ++返回垃圾数据 - TCP memcpy buffer returns rubbish data using C++ C ++安全框架,用于缓冲区溢出,格式字符串错误和整数溢出 - C++ Security Framework for Buffer Overflows, Format String Bugs and Integer Overflows 用于在C ++中初始化的memset - memset for initialization in C++ 向量 C++ 上的 Memset - Memset on vector C++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM