【发布时间】:2020-01-21 18:58:10
【问题描述】:
当我编译这个程序时:
#include <list>
int main() {
std::list<int> l = {1, 2};
l.remove(l.front());
}
通过使用 ASAN 和调试的 clang:
clang++-8 -fno-omit-frame-pointer -g -fsanitize=address -D_GLIBCXX_DEBUG -std=c++11 list-remove.cpp
我收到heap-use-after-free:
==31868==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000020 at pc 0x0000004fa1ae bp 0x7fff52cc5630 sp 0x7fff52cc5628
READ of size 4 at 0x603000000020 thread T0
#0 0x4fa1ad in std::__debug::list<int, std::allocator<int> >::remove(int const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/debug/list:649:18
#1 0x4f990f in main /tmp/list-remove.cpp:5:7
#2 0x7ff27d974b96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310
#3 0x41b879 in _start (/tmp/list-remove+0x41b879)
似乎当remove 发现x 匹配第一个元素时,它会从列表中删除该元素并将其删除。当它去检查第二个元素时,它会使用已经被删除的x来比较这个元素。
这是根据 C++ 标准的正确实现吗?似乎最好先将元素移动到末尾然后删除它们。这将避免heap-use-after-free 错误,但也许不需要这样的实现。
cppreference 没有提到 value 不能是容器中元素的别名。
这是我正在使用的 c++ 版本:
$ /usr/bin/c++ --version
c++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
【问题讨论】:
-
@FrançoisAndrieux 你是用
-D_GLIBCXX_DEBUG构建的吗? -
嗯。我们不需要
std::remove在这种情况下工作。我不知道为什么我们需要list::remove,但是缺少措辞。 -
按原样查看标准,这是一个错误。也就是说,我假设通常的自引用规则应该像
std::remove一样适用,而是标准存在缺陷并且代码是正确的。 -
在线草案也没有提到
std::list的限制:eel.is/c++draft/list#ops-15std::remove()也没有提到:eel.is/c++draft/alg.remove -
@NathanOliver 我们只需要移动分配,所以它不可能工作。这是今年四月在图书馆反射器中提出的。反应是……没有同情心。
标签: c++ stl language-lawyer