【问题标题】:ERROR: AddressSanitizer: heap-buffer-overflow on address X at pc Y bp Z sp W错误:AddressSanitizer:地址 X 在 pc Y bp Z sp W 上的堆缓冲区溢出
【发布时间】:2017-01-04 22:12:45
【问题描述】:

我对动态内存管理和使用 Fsanitise 标志来查找内存管理问题非常陌生。我不能使用向量来存储数据 - 我需要使用原始数组以及“新建”和“删除”来管理堆对象。

当我尝试运行EuclideanVectorTester编译程序时出现以下错误,但不确定是什么问题,请高手赐教?

weill % make
g++ -std=c++14 -Wall -Werror -O2 -fsanitize=address -c EuclideanVectorTester.cpp
g++ -std=c++14 -Wall -Werror -O2 -fsanitize=address -c EuclideanVector.cpp
g++ -fsanitize=address EuclideanVectorTester.o EuclideanVector.o -o EuclideanVectorTester
weill % ./EuclideanVectorTester
1
=================================================================
==15341==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb59007e0 at pc 0x8048ca7 bp 0xbfb47388 sp 0xbfb4737c
WRITE of size 8 at 0xb59007e0 thread T0
    #0 0x8048ca6 in main (/tmp_amd/kamen/export/kamen/3/z3386180/cs6771/evec/EuclideanVectorTester+0x8048ca6)
    #1 0xb6ecae45 in __libc_start_main (/lib/i386-linux-gnu/i686/cmov/libc.so.6+0x16e45)

0xb59007e0 is located 0 bytes to the right of 16-byte region [0xb59007d0,0xb59007e0)
allocated by thread T0 here:
    #0 0xb722a4c4 in operator new[](unsigned int) (/usr/lib/libasan.so.1+0x524c4)
    #1 0x8048b9a in main (/tmp_amd/kamen/export/kamen/3/z3386180/cs6771/evec/EuclideanVectorTester+0x8048b9a)
    #2 0xb6ecae45 in __libc_start_main (/lib/i386-linux-gnu/i686/cmov/libc.so.6+0x16e45)
    #3 0x8048d8c (/tmp_amd/kamen/export/kamen/3/z3386180/cs6771/evec/EuclideanVectorTester+0x8048d8c)

SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 main
Shadow bytes around the buggy address:
  0x36b200a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b200b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b200c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b200d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b200e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x36b200f0: fa fa fa fa fa fa fa fa fa fa 00 00[fa]fa 04 fa
  0x36b20100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b20110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b20120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b20130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36b20140: 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
  Contiguous container OOB:fc
  ASan internal:           fe
==15341==ABORTING
weill %

EuclideanVector.h 文件是这样的:

#ifndef _EuclideanVector_h
#define _EuclideanVector_h

#include <iostream>
#include <algorithm>


namespace even {
    class EuclideanVector {

        public:

            /*
             * A constructor that takes the number of dimensions (as an unsigned int) but no magnitudes, 
             * sets the magnitude in each dimension as 0.0. This is the default constructor, with the default value being 1.
             */
            template <typename NUM>
            EuclideanVector(const NUM dimensions = 1): 
                  EuclideanVector(dimensions, 0.0) {}; // delegating constructor; default constructor that takes in dimensions if there is user input, otherwise dimensions = 1 if it is an empty constructor

            // target constructor for delegating constructor
            template <typename NUM1, typename NUM2> // any numeric types of user input for dimensions and magnitude will be static_cast to unsigned int and double respectively
            EuclideanVector(const NUM1 dimensions, const NUM2 magnitude){

                        // static cast to unsigned int and assign dimensions_ to that
                        dimensions_ = new unsigned int (static_cast<unsigned int>(dimensions));

                        // assign pointer "magnitude_" to dynamically-allocated memory of new unnamed array<double> object of size "dimensions_"
                        magnitude_  =  new double [*dimensions_];

                        // fill the array<double> object "magnitude_" a number of "dimensions_" times, with the <double> value of "magnitude_" for each dimension
                        std::fill_n(magnitude_, dimensions_, static_cast<double>(magnitude));

                  }

            /*
             * Destructor: ~EuclideanVector
             * The destructor deallocates any memory acquired by the constructors. 
             */
            ~EuclideanVector();


            /*
             * Member function: getMagnitude()
             * Returns a double containing the number of dimensions in a particular array.
             */
            const double& getMagnitude () const;


            /*
             * Member function: getNumDimensions()
             * Returns an unsigned int containing the number of dimensions in a particular vector.
             */
            unsigned int getNumDimensions() const;


        private:
        /* Everything from here to the end of the class is private, so
         * not accessible or intended for use for the client */

            unsigned int *dimensions_;
            double *magnitude_;
            //double normal_;
        };
}

#endif

EuclideanVector.cpp 文件是这样的:

#include "EuclideanVector.h"
#include <algorithm>
#include <cmath> // for sqrt
#include <sstream>
#include <iterator>

namespace even {

    unsigned int EuclideanVector::getNumDimensions () const
    {
        return *dimensions_;
    }

    const double& EuclideanVector::getMagnitude () const
    {
        return *magnitude_;
    }

    // destructor
    EuclideanVector::~EuclideanVector() {
        delete dimensions_;
        delete [] magnitude_;
    }
}

EuclideanVectorTester.cpp 文件是这样的:

#include <iostream>
#include <vector>
#include <list>

#include "EuclideanVector.h"

int main() {

    std::cout << "1" << std::endl;

    evec::EuclideanVector a(2); 


    std::cout << "2" << std::endl;
    std::cout << "3" << std::end;

}

【问题讨论】:

  • 我不能使用向量来存储数据——我喜欢听到你不能使用 vector 的原因——你正在使用模板、命名空间,甚至STL算法,但不能使用std::vector?? .如果你不能使用vector,那么创建你自己的简单vector类,测试它,然后使用它。
  • 那是教授的“伟大”演讲(讽刺,因为它充满了你知道什么)。太糟糕了,他/她没有就复制构造函数、赋值运算符、析构函数、异常安全等发表演讲。你被引导相信初学者可以自己实现这个——我还没有看到一个初学者实现这个在没有经验丰富的 C++ 程序员的大量帮助的情况下,他们自己就能正确地进行操作。
  • 显然你的教授不希望你在他的课程之后之前read Knuth。不会粉饰它;他像一个有钱谈论代码的人一样站在正面;不要它。祝你好运。
  • 伙计们!!我意识到我之前的代码与为维度_和幅度_声明的指针有什么问题。我忘了尊重 std:fill_n 中的维度_。这就是出现分段错误的原因。干杯! =D
  • 考虑使用 '-O0 -g3' 进行编译,以便在输出中获得更好的调试注释。

标签: c++ constructor heap-memory dynamic-memory-allocation address-sanitizer


【解决方案1】:

我只想说,您的教授所说的有很多不准确之处(至少可以这么说)。

话虽如此,问题是您已将 dimensions 声明为 unsigned int*,但您在此处使用它时就好像它是普通的 unsigned int

std::fill_n(magnitude_, dimensions_, static_cast<double>(magnitude));

立即解决方法是这样做:

std::fill_n(magnitude_, *dimensions_, static_cast<double>(magnitude));

然而,这引出了一个问题,为什么一个简单的unsigned int 需要是一个指针,然后使用new 分配。没有理由这样做,因为使用 new 的效率低于您现在正在做的事情。

如果您已将 dimensions_ 声明为:

unsigned int dimensions_;

不是指针,而是分配给dimensons_的代码变成这样:

// no call to new is done
dimensions_ = static_cast<unsigned int>(dimensions);

// no dereference of a pointer needs to be done on the two lines below 
magnitude_ = new double[dimensions_];
std::fill_n(magnitude_, dimensions_, static_cast<double>(magnitude));

没有对分配器进行额外调用,因此代码立即变得更有效率。

此外,析构函数现在看起来像这样:

EuclideanVector::~EuclideanVector() 
{
    delete [] magnitude_;
}

但即使说明了所有这些,给出的答案也只能在您使用相同的main 程序进行测试时解决问题。如果您将main 更改为:

even::EuclideanVector a(2);
even::EuclideanVector b = a;

您现在会遇到不正确的复制语义。再说一遍,上面的修复只需要让你的 main 函数工作。把main改成上面例子一样简单的东西,你会遇到更多的问题。

【讨论】:

  • 一个+1 表示你有勇气接受这个。
  • 澄清一下,std::fill_n 中为什么不需要解引用magnitude_?是因为 std::fill_n 的第一个参数是应该是指针的输出参数吗?而第二个参数是要修改的元素个数,所以需要解引用才能得到dimensions_的值而不是指针?
  • @iteong -- 请查看文档here。是的,你需要取消对指针的引用,否则你给它一个等于指针值的计数值(如果你的应用是 64 位,可能是数百万甚至数十亿)。
猜你喜欢
  • 1970-01-01
  • 2019-01-05
  • 1970-01-01
  • 2022-11-14
  • 2023-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多