C++ ASan:heap use after free after vector.emplace(push)\u返回递归函数 #包括 结构节点; std::向量堆; 结构节点{ int x,c; 显式节点(intx):x(x),c(0){ 无效更新(){ 如果(x>0){ 如果(c==0){ c=heap.size(); 堆。向后放置(x/2); } 堆[c]。更新(); } } }; int main(){ 堆。向后放置(100); heap.back().update(); }
考虑上面的代码。当使用C++ ASan:heap use after free after vector.emplace(push)\u返回递归函数 #包括 结构节点; std::向量堆; 结构节点{ int x,c; 显式节点(intx):x(x),c(0){ 无效更新(){ 如果(x>0){ 如果(c==0){ c=heap.size(); 堆。向后放置(x/2); } 堆[c]。更新(); } } }; int main(){ 堆。向后放置(100); heap.back().update(); },c++,vector,address-sanitizer,C++,Vector,Address Sanitizer,考虑上面的代码。当使用g++-fsanize=address(gcc版本10.2.0(Ubuntu 10.2.0-5ubuntu1~20.04))编译并运行时,我得到了AddressSanitizer:heap-use-after-free。在谷歌搜索之后,当我试图访问之前已释放的指针时,似乎会发生此错误。检查ASan的输出显示vector.emplace_back释放了该对象。我相信这是向量自身大小调整的结果,这得到了heap.reserve(1000)使代码运行良好这一事实的支持 然后,我试
g++-fsanize=address
(gcc版本10.2.0(Ubuntu 10.2.0-5ubuntu1~20.04)
)编译并运行时,我得到了AddressSanitizer:heap-use-after-free
。在谷歌搜索之后,当我试图访问之前已释放的指针时,似乎会发生此错误。检查ASan的输出显示vector.emplace_back释放了该对象。我相信这是向量自身大小调整的结果,这得到了heap.reserve(1000)
使代码运行良好这一事实的支持
然后,我试着用推回代替定位推回,产生了类似的结果。但是,用循环替换递归效果很好
intmain(){
堆。向后放置(100);
int-prevc=0;
while(true){
intx=heap[prevc].x,c=0;
如果(x>0){
如果(c==0){
c=heap.size();
堆。向后放置(x/2);
}
prevc=c;
}否则{
打破
}
}
}
进一步检查后,似乎在第一次调用update()
后引发了异常。此外,访问堆[0]
和堆[1]
也可以,但访问堆[c]
失败
现在我很困惑。我找不到任何未定义的行为,也看不到向量内部大小调整导致我无法访问元素的任何原因。此外,我不确定为什么仅使用递归会失败。我做错了什么?正如user4581301在评论中指出的,问题实际上不是访问向量中的元素,而是访问
c
,这会失败,因为此
已被堆大小调整删除
在我的特殊情况下,我想写一个没有指针的二叉树,所以递归是必要的。具有讽刺意味的是,这背后的理由是为了避免指针陷阱,但结果适得其反。为了将来参考,我找到了四种可能的解决方案:
heap.reserve()
使用数组和指向最后一个元素索引的指针模拟向量(基本上是选项1,但可能更快)
添加类似于Node self=*this的内容编码>并通过self
访问所有属性。注意:执行Node*self=this代码>不起作用,因为self
将与此
一起失效
只要使用指针;它们的存在是有原因的
正如user4581301在评论中指出的,问题实际上不是访问向量中的元素,而是访问c
,这会失败,因为此
已被堆大小调整删除
在我的特殊情况下,我想写一个没有指针的二叉树,所以递归是必要的。具有讽刺意味的是,这背后的理由是为了避免指针陷阱,但结果适得其反。为了将来参考,我找到了四种可能的解决方案:
heap.reserve()
使用数组和指向最后一个元素索引的指针模拟向量(基本上是选项1,但可能更快)
添加类似于Node self=*this的内容编码>并通过self
访问所有属性。注意:执行Node*self=this代码>不起作用,因为self
将与此
一起失效
只要使用指针;它们的存在是有原因的
将析构函数添加到节点
,该节点在调用时打印(可能与c
一起)。你会惊讶的。难道不是vector
调整了一个?方便的读数:@StephenNewell在析构函数中打印x
时,只打印100个。我相信异常是在第一次调用update()
之后引发的。我会将此内容与更多信息一起编辑到问题中。当您在heap
中的节点上调用update
时,请记住this
指的是heap
中的节点,当heap.emplace\u back(x/2)时代码>调整堆的大小,这有点像。这意味着在堆[c].update()中使用c
将是一次相当冒险的经历。在节点
中添加一个析构函数,在调用它时打印(可能与c
一起)。你会惊讶的。难道不是vector
调整了一个?方便的读数:@StephenNewell在析构函数中打印x
时,只打印100个。我相信异常是在第一次调用update()
之后引发的。我会将此内容与更多信息一起编辑到问题中。当您在heap
中的节点上调用update
时,请记住this
指的是heap
中的节点,当heap.emplace\u back(x/2)时代码>调整堆的大小,这有点像。这意味着在堆[c].update()中使用c
代码>将是一次相当大的冒险。请注意第3点。我不确定这是否100%合法。请记住,该程序仍在包含无效this
的方法中。快速查看该标准没有发现任何明确说明该标准是否有效的内容,但您已保证不会使用该标准,并且在实践中应该是安全的。关于第3点的说明。我不确定这是否100%合法。请记住,该程序仍在具有无效