记录一次在开发过程中,由于疏忽,导致的内存溢出的调试过程。涉及到:

  1. 结构体中定义了占位符
  2. x86 系统中该函数的形参为多个时,形参在堆栈空间上的内存分配。
  3. malloc 动态分配内存的重要性
  4. C语言中内存没边界检查,对开发者代码编写的要求较高

0. 个人简介 && 授权须知

image-20230911133730620

📋 个人简介

  • 💖 作者简介:大家好,我是喜欢记录零碎知识点的菜鸟打工人。😎
  • 📝 个人主页:欢迎访问我的博客主页🔥…
    • https://blog.csdn.net/qq_39217004?spm=1010.2135.3001.5343
  • 🎉 支持我:点赞👍+收藏⭐️+留言📝
  • 📣 系列专栏:嵌入式Linux开发 🍁 🍁
  • 💬格言:写文档啊不是写文章,重要的还是直白!🔥

转载文章,禁止声明原创;不允许直接二次转载,转载请根据原文链接联系作者

若无需改版,在文首清楚标注作者及来源/原文链接,并删除【原创声明】,即可直接转载。
但对于未注明转载来源/原文链接的文章,我将保留追述的权利。

https://blog.csdn.net/qq_39217004?spm=1010.2135.3001.5343

作者:积跬步、至千里

image-20230911133724204

1. 问题描述

  1. 调用自己定义的 Tensor 接口,创建3个四维张量。其中 create_tensor 是 malloc 的,使用完毕之后需要手动释放。
Tensor *cache_tensor = create_tensor(4, cache_shape, TYPE_FLOAT16, "cache_tensor", LAYOUT_ROW_MAJOR, false);
Tensor *input_tensor = create_tensor(4, input_shape, TYPE_FLOAT16, "input_tensor", LAYOUT_ROW_MAJOR, false);
Tensor *input_vector_tensor = create_tensor(4, input_vector_shape, TYPE_INT32, "input_vector_tensor", LAYOUT_ROW_MAJOR, false);
  1. 调用自己的函数接口,更新张量的值
update_Tensor_device(cache_tensor, input_tensor, input_vector_tensor);
  1. 执行完毕后,释放张量
        free_tensor(cache_tensor);
        free_tensor(input_tensor);
        free_tensor(input_vector_tensor);
  1. 报错,core dumped
    在这里插入图片描述

2. 查找 Segmentation fault (core dumped) 的原因

gdb 运行可执行文件,找到报错的地方,通过 backtrace 查找
在这里插入图片描述
在这里插入图片描述
可以看到,在释放创建的张量时,报错了。
以上三个张量都是在 CPU 端通过 create_tensor 创建的,释放时报错,我们打印出张量数据的地址观察。
在这里插入图片描述
张量的地址为什么会被改变了呢?
在调用 input_tensor 变量的函数处,调用前和调用后分别打印 变量的地址,发现,该变量是在 my_test 函数中被改变了值。
在这里插入图片描述

3. 分析函数中内存溢出的位置

input_tensor 变量的地址被改变了,而 my_test 函数中,该参数恰好是 const 类型的,说明代码肯定是有问题,产生了内存被篡改的地方。

void my_test(Tensor *cache,const Tensor *input, Tensor *input_vec)

在 x86 中,函数的形参在栈空间上的分配为: :参数从右到左压栈

栈顶 → input_vec → input → cache ← 栈底

既然 inputconst 类型,那么是否 cache 或者 input_vec 变量产生了溢出?
详细查看代码中,对cache 或者 input_vec 变量赋值的地方。

  1. 在函数执行的位置打断点

在这里插入图片描述

  1. 监控变量

watch input_tensor->data
在这里插入图片描述
继续执行,果然变量的地址被改变了
在这里插入图片描述
报错信息为:

Thread 1 "update_cache_te" hit Watchpoint 2: input_tensor->data

Old value = (void *) 0x40f8f0
New value = <unreadable>
__memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:243
243     ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: No such file or directory.

经查找资料,程序停在了 __memmove_avx_unaligned_erms —— 这是一个底层内存拷贝函数(如 memcpymemmove 的实现)。

而我的代码中确实有用到 memcpy 接口

在这里插入图片描述
经过研究以上变量,发现我的结构体是这么定义的:
在这里插入图片描述
在这里插入图片描述

4. 结构体变量中的占位符惹的祸

代码中,结构体中定义了一个 int32_t input_vector_arry[0]; 作为占位符。
本身占位符是不分配内存空间的。

我定义了一个普通的 结构体变量,并将一个数组赋值到了结构体变量的占位符中,也恰好是我代码中 memcpy 接口的地方。
其实 cblk_matmul.runtimeArgs.input_vector_arry 这个地址是不可访问的。

由于赋值时意外给 cache 变量的额外的空间赋值,并且
cache input 和 input_vec 三个变量是函数形参,因此我错误的操作了cache 变量之外的地址空间,影响了 input 变量的地址。
在这里插入图片描述
造成了 input->data 被意外改变了地址,导致 cpu 无法访问

New value =  <unreadable>

因为这个地址就是数组中的值了,比如 0x0b,cpu 当然无法访问。

5.修改错误

定义结构体变量时,由于有个占位符,因此必须使用动态分配内存的方式,把需要使用的占位符的空间提前算出来。如下所示:
在这里插入图片描述
memcpy 时,拷贝的数据一定不要越界
在这里插入图片描述
修改以上代码,再次测试,解决了 Segmentation fault (core dumped) 问题。

6.总结

函数形参的地址:在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐