简体   繁体   English

可变内容导致 AddressSanitizer: heap-use-after-free on address

[英]Variadic content leads to AddressSanitizer: heap-use-after-free on address

Object of type AAAA can hold any other object depending on its category. AAAA类型的对象可以根据其类别容纳任何其他对象。 For example it holds object of type BBBB .例如,它持有BBBB类型的对象。 An object of type BBBB also can hold any object content depending on its category. BBBB类型的对象还可以根据其类别保存任何对象内容。

Address sanitizer fails for the following code:以下代码的地址清理程序失败:

#include <assert.h>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class AAAA;
AAAA generate_a();

enum class A_CATEGORY
{
    Not_Seclected,
    B_DATA_CAT,
    // other categories
};

enum class B_CATEGORY
{
    Not_Seclected,
    C_DATA_CAT,
    // other categories
};

class CCCC
{
public:
    vector<int> needed_dummy;
};

class BBBB
{
public:
    B_CATEGORY category = B_CATEGORY::Not_Seclected;
    void * p_variadic_content = nullptr;

    BBBB()
    {
        cout<<"BBBB: default constructor"<<endl;
    }

    BBBB(const BBBB &obj)
    {
        cout<<"BBBB: copy constructor {"<<endl;
        *this = obj;
        if(obj.p_variadic_content != nullptr)
        {
            switch(this->category)
            {
                case B_CATEGORY::C_DATA_CAT:
                    this->p_variadic_content = (void *) new CCCC();
                    *((CCCC*) this->p_variadic_content) = *((CCCC*) obj.p_variadic_content);
                    break;
                default:
                    cout<<"Unknown category"<<endl;
            }
        }
        cout<<"BBBB: copy constructor }"<<endl;
    }

    ~BBBB()
    {
        switch(this->category)
        {
            case B_CATEGORY::C_DATA_CAT:
                assert(p_variadic_content != nullptr);
                delete (CCCC *) p_variadic_content;
                p_variadic_content = nullptr;
                cout<<"~BBBB"<<endl;
                break;
            default:
                cout<<"Unknown category"<<endl;
        }
    }
};

class AAAA
{
public:
    A_CATEGORY category = A_CATEGORY::Not_Seclected;
    void * p_variadic_content = nullptr;

    AAAA()
    {
       cout<<"AAAA: default constructor"<<endl;
    }

    AAAA(const AAAA &obj)
    {
        cout<<"AAAA: copy constructor {"<<endl;
        *this = obj;
        if(obj.p_variadic_content != nullptr)
        {
            switch(this->category)
            {
                case A_CATEGORY::B_DATA_CAT:
                    this->p_variadic_content = (void *) new BBBB();
                    *((BBBB*) this->p_variadic_content) = *((BBBB*) obj.p_variadic_content);
                    break;
                default:
                    cout<<"Unknown category"<<endl;
            }
        }
        cout<<"AAAA: copy constructor }"<<endl;
    }

    ~AAAA()
    {
        switch(this->category)
        {
            case A_CATEGORY::B_DATA_CAT:
                assert(p_variadic_content != nullptr);
                delete (BBBB *) p_variadic_content;
                p_variadic_content = nullptr;
                cout<<"~AAAA"<<endl;
                break;
            default:
                cout << "Unknown category" << endl;
        }
    }
};

AAAA generate_a()
{
    AAAA a_object;
    a_object.category = A_CATEGORY::B_DATA_CAT;
    BBBB * b_object = new BBBB();
    CCCC * c_object = new CCCC();
    b_object->category = B_CATEGORY::C_DATA_CAT;
    b_object->p_variadic_content = (void *) c_object;
    a_object.p_variadic_content = (void *) b_object;
    return a_object;
}

int main()
{
    vector<AAAA> aaa;
    AAAA a_object = generate_a();
    aaa.push_back(std::move(a_object));

    return 0;
}

Compiled by编译者

g++ -std=c++11 -fsanitize=address  aaaa.cpp  && ./a.out

The execution fails with the following trace执行失败并显示以下跟踪

AAAA: default constructor
BBBB: default constructor
AAAA: copy constructor {
BBBB: default constructor
AAAA: copy constructor }
~BBBB
~AAAA
=================================================================
==17317==ERROR: AddressSanitizer: heap-use-after-free on address 0x60300000efe8 at pc 0x000000401d30 bp 0x7ffd036102a0 sp 0x7ffd03610290
READ of size 8 at 0x60300000efe8 thread T0
    #0 0x401d2f in std::vector<int, std::allocator<int> >::~vector() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401d2f)
    #1 0x401701 in CCCC::~CCCC() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401701)
    #2 0x4017da in BBBB::~BBBB() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x4017da)
    #3 0x401c4a in AAAA::~AAAA() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401c4a)
    #4 0x402b56 in void std::_Destroy<AAAA>(AAAA*) (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x402b56)
    #5 0x402846 in void std::_Destroy_aux<false>::__destroy<AAAA*>(AAAA*, AAAA*) (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x402846)
    #6 0x4023ff in void std::_Destroy<AAAA*>(AAAA*, AAAA*) (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x4023ff)
    #7 0x402098 in void std::_Destroy<AAAA*, AAAA>(AAAA*, AAAA*, std::allocator<AAAA>&) (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x402098)
    #8 0x401e17 in std::vector<AAAA, std::allocator<AAAA> >::~vector() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401e17)
    #9 0x4014d4 in main (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x4014d4)
    #10 0x7f2bbb9eb83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
    #11 0x401118 in _start (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401118)

0x60300000efe8 is located 8 bytes inside of 24-byte region [0x60300000efe0,0x60300000eff8)
freed by thread T0 here:
    #0 0x7f2bbc429b8a in operator delete(void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99b8a)
    #1 0x4017e2 in BBBB::~BBBB() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x4017e2)
    #2 0x401c4a in AAAA::~AAAA() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401c4a)
    #3 0x4014c8 in main (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x4014c8)
    #4 0x7f2bbb9eb83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

previously allocated by thread T0 here:
    #0 0x7f2bbc429592 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99592)
    #1 0x40126b in generate_a() (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x40126b)
    #2 0x401498 in main (/media/common/tmp/cpp/Untitled Folder/aaaa/a.out+0x401498)
    #3 0x7f2bbb9eb83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)

SUMMARY: AddressSanitizer: heap-use-after-free ??:0 std::vector<int, std::allocator<int> >::~vector()
Shadow bytes around the buggy address:
  0x0c067fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c067fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa fd[fd]fd fa
  0x0c067fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==17317==ABORTING

So,所以,

  1. What is wrong?怎么了?

  2. How should I fix it without changing the structure?我应该如何在不改变结构的情况下修复它? I am after a solution by correcting the memory management rather than getting around the problem.我通过更正内存管理而不是解决问题来寻求解决方案。 I am mainly after a fix in constructors/destructor.我主要是在修复构造函数/析构函数之后。 Also, I fill extend the categories.另外,我填写扩展类别。 So, I am not after removing them.所以,我不是在删除它们之后。 I will not accept removing void* or try replacing it by an explicit type.我不会接受删除void*或尝试用显式类型替换它。

The problem stems from this line:问题源于这一行:

*((BBBB*) this->p_variadic_content) = *((BBBB*) obj.p_variadic_content);

Because class BBBB does not define a copy assignment operator, the default one will be used which will result in both this and obj having identical BBBB::p_variadic_content pointers to the same memory block.因为类BBBB没有定义复制赋值运算符,所以将使用默认运算符,这将导致thisobj具有相同的BBBB::p_variadic_content指向同一内存块的指针。 Later, when the two objects are destroyed, you try to free up the same memory block twice which results in your heap-use-after-free problem.稍后,当两个对象被销毁时,您尝试两次释放同一个内存块,这会导致堆释放后使用问题。

The solution is to define the assignment operator ( BBBB &operator=(const BBBB &) ) for class BBBB , and make changes to the copy constructor to not make use of it the way you are.解决方案是为类BBBB定义赋值运算符( BBBB &operator=(const BBBB &) ),并对复制构造函数进行更改,以不按照您的方式使用它。 Since the assignment operator should perform the required deep copy, then the copy constructor (which calls the copy assignment) would not need to make this copy.由于赋值运算符应该执行所需的深度复制,因此复制构造函数(调用复制赋值)将不需要进行此复制。

Incidentally, you can reduce the need for those type casts by storing the newly allocated pointer in a local variable with the correct type.顺便说一句,您可以通过将新分配的指针存储在具有正确类型的局部变量中来减少对这些类型转换的需要。 For example,例如,

            case B_CATEGORY::C_DATA_CAT:
            {
                CCCC *pc = new CCCC;
                p_variadic_content = pc;
                *pc = *((CCCC*) obj.p_variadic_content);
                break;
            }

It is then easier to see that this can be condensed down into然后更容易看出这可以浓缩为

            case B_CATEGORY::C_DATA_CAT:
                p_variadic_content = new CCCC(*(CCCC*) obj.p_variadic_content);
                break;

which avoids the error you get in the original code (but without fixing the underlying cause of the error).这避免了您在原始代码中遇到的错误(但没有修复错误的根本原因)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 AddressSanitizer:特定输入的 heap-use-after-free 错误 - AddressSanitizer: heap-use-after-free error for particular input address sanitizer 有时会错过 heap-use-after-free - address sanitizer sometimes misses heap-use-after-free AddressSanitizer 识别 std::vector<t> ::push_back 作为 heap-use-after-free 错误的原因</t> - AddressSanitizer identifies std::vector<T>::push_back as reason for heap-use-after-free error BST释放后使用堆错误中的删除节点 - Delete Node in BST heap-use-after-free error 将变量声明为引用时的堆使用后释放 - heap-use-after-free when declaring a variable as a reference ASan:在递归 function 中的 vector.emplace(push)_back 之后的堆使用后释放 - ASan: heap-use-after-free after vector.emplace(push)_back in a recursive function 在 C++ 中使用带有 2D 向量的引用时,堆使用后释放 - heap-use-after-free when using references with 2D vector in C++ ASAN:将二叉树展平到链接列表时释放后使用堆 - ASAN: heap-use-after-free when flattening a binary tree to a linked list 使用 std::vector 时的简单 Boost UDP 接收器 gest heap-use-after-free - Simple Boost UDP receiver gest heap-use-after-free when using std::vector C++ [heap-use-after-free error] 引用和 push_back 到 vector - C++ [heap-use-after-free error] with referencing and push_back to vector
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM