代码之家  ›  专栏  ›  技术社区  ›  Jagger Harris

为什么将memcpy与包含指针的自定义结构一起使用会导致堆缓冲区溢出?

  •  0
  • Jagger Harris  · 技术社区  · 2 年前

    我正在制作一个程序,它需要C89中的多种类型的动态数组。我正在尝试创建一个支持自定义结构数据的动态数组的自己的实现。

    然而,在使用我的动态数组实现时,我得到了堆缓冲区溢出,我不知道为什么。我似乎没有选角问题。我已经正确地分配并调整了动态数组的大小。我用了合适的尺码。我应该能够毫无问题地将结构添加到数组中。

    这是包含动态数组和点结构的主C文件。堆缓冲区溢出发生在 dynamic_array_push 。注意:出于测试目的,我创建了 point_a 变量,因此在这种情况下没有制作动态数组的实际目的。

    struct point {
      int x;
      int y;
      const char* id;
    };
    
    int main() {
      struct dynamic_array points = dynamic_array_create(sizeof(struct point));
      struct point point_a = {1, 1, "point_a"};
      size_t i;
    
      dynamic_array_push(&points, (void*)&point_a); /* Heap buffer overflow here */
    
      return 0;
    }
    

    这里是动态数组头文件,它包含创建和推送到动态数组的最小功能(不包括释放内存和弹出)。我已经找出了堆缓冲区溢出的原因,它发生在 memcpy .

    #define DYNAMIC_STARTING_SIZE 1
    #define DYNAMIC_GROWTH_AMOUNT 1.5
    
    struct dynamic_array {
      void* data;
      size_t amount;
      size_t capacity;
      size_t element_size;
    };
    
    struct dynamic_array dynamic_array_create(size_t element_size) {
      struct dynamic_array dynamic_array;
    
      dynamic_array.data = malloc(sizeof(DYNAMIC_STARTING_SIZE * element_size));
      dynamic_array.amount = 0;
      dynamic_array.capacity = DYNAMIC_STARTING_SIZE;
      dynamic_array.element_size = element_size;
    
      return dynamic_array;
    }
    
    void* dynamic_array_get(struct dynamic_array* dynamic_array, size_t index) {
      if (index >= dynamic_array->amount) {
        return NULL;
      }
    
      return ((unsigned char*)dynamic_array->data) + (index * dynamic_array->element_size);
    }
    
    void dynamic_array_push(struct dynamic_array* dynamic_array,
                            const void* element) {
      unsigned char* destination =
          (unsigned char*)dynamic_array->data +
          dynamic_array->amount * dynamic_array->element_size;
    
      if (dynamic_array->amount >= dynamic_array->capacity) {
        dynamic_array->capacity =
            (size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
        dynamic_array->data =
            realloc(dynamic_array->data,
                    dynamic_array->capacity * dynamic_array->element_size);
      }
    
      memcpy((void*)destination, element, dynamic_array->element_size); /* Heap buffer overflow here */
    
      dynamic_array->amount++;
    }
    
    

    我打印出了内存地址位置的值,它们的大小应该是 点a 并在我的机器上获得了这些值:

    Starting memory address: 0x502000000010
    Destination memory address: 0x502000000010
    Destination ending memory address: 0x502000000020 <- This is actually the address after the end
    

    但地址清理程序显示的写入大小为16字节(如 const char* point_a 是8字节和2 int s在我的机器上也是8个字节)的地址 0x502000000018 .

    然后,我决定删除 const char* data 字段,修复了该问题。然后我测试并发现,任何指针,无论它在结构中的位置如何,都会导致堆溢出。

    这个 destination 应该是正确的,因为我正在获取数据数组的位置,并添加数量乘以元素大小。这个 element 和 dynamic_array->element_size 位于正确的位置。当您第一次创建数组时,我也会适当地为数组数据分配内存,并在需要时重新分配(这在本文中并不重要,因为数组一旦创建就可以容纳一个值)。

    我是否应该做其他事情来支持用于动态数组的结构中的指针,或者这里有其他问题?

    与我的具体问题相似的问题,但我未能从中得到答案:

    关于memcpy和动态数组的其他类似问题:

    1 回复  |  直到 2 年前
        1
  •  1
  •   dbush    2 年前

    您的主要问题是在哪里创建初始数组:

    dynamic_array.data = malloc(sizeof(DYNAMIC_STARTING_SIZE * element_size));
    

    您正在询问表达式的数据类型的大小 DYNAMIC_STARTING_SIZE * element_size ,即。 size_t ,而不是表达式的值。A. 大小_t 很可能是8个字节,因此大于此大小的元素将无法分配足够的空间。这将导致您的写入超过分配内存的末尾。

    相反,您希望:

    dynamic_array.data = malloc(DYNAMIC_STARTING_SIZE * element_size);
    

    这里还有一个问题:

      unsigned char* destination =
          (unsigned char*)dynamic_array->data +
          dynamic_array->amount * dynamic_array->element_size;
    
      if (dynamic_array->amount >= dynamic_array->capacity) {
        dynamic_array->capacity =
            (size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
        dynamic_array->data =
            realloc(dynamic_array->data,
                    dynamic_array->capacity * dynamic_array->element_size);
      }
    

    如果重新分配移动了内存,那么 destination 将指向一个无效的位置。因此,请先检查尺寸:

      if (dynamic_array->amount >= dynamic_array->capacity) {
        dynamic_array->capacity =
            (size_t)ceil(dynamic_array->capacity * DYNAMIC_GROWTH_AMOUNT);
        dynamic_array->data =
            realloc(dynamic_array->data,
                    dynamic_array->capacity * dynamic_array->element_size);
      }
    
      unsigned char* destination =
          (unsigned char*)dynamic_array->data +
          dynamic_array->amount * dynamic_array->element_size;