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

问题描述 投票:0回答:1

我正在编写一个程序,需要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++;
}

我打印出了内存地址位置的值,它们的大小应该是

point_a
的大小,并在我的机器上获取了这些值:

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

但是地址清理程序显示地址

const char* point_a
处的写入大小为 16 字节(因为
int
是 8 字节,两个
0x502000000018
在我的机器上也是 8 字节)。

然后,我决定删除

const char* data
字段,这解决了问题。然后我测试并发现任何指针,无论它在结构中的什么位置,都会导致堆溢出。

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

我还应该做些什么来支持用于动态数组的结构中的指针,或者这里还有其他问题吗?

与我的具体问题类似但未能得到答案的问题:

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

arrays c dynamic memcpy address-sanitizer
1个回答
0
投票

您的主要问题是创建初始数组的位置:

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

您要求的是表达式

DYNAMIC_STARTING_SIZE * element_size
的数据类型的大小,即
size_t
,而不是表达式的值。
size_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;
© www.soinside.com 2019 - 2024. All rights reserved.