首页 > 解决方案 > Get n-th character of string : AddressSanitizer: stack-use-after-scope

问题描述

I am trying to implement my own string_view. Here is an example of class:

class Example {
public:
    Example(const std::string& s) : data_(s.data()) {}

    char operator[](size_t pos) const {
        return data_[pos];
    }
private:
    const char* data_;
};

Test:

Example e("hello");
char t = e[1];
REQUIRE(t == 'e');

An error:

==38867==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffee74544a2 at pc 0x0001087ad931 bp 0x7ffee7454400 sp 0x7ffee74543f8
READ of size 1 at 0x7ffee74544a2 thread T0
    #0 0x1087ad930 in Example::operator[](unsigned long) const code.h:12

Address 0x7ffee74544a2 is located in stack of thread T0 at offset 66 in frame
    #0 0x1087aca5f in ____C_A_T_C_H____T_E_S_T____0() test.cpp:7

  This frame has 11 object(s):
    [32, 40) 'e' (line 9)
    [64, 88) 'ref.tmp' (line 9) <== Memory access at offset 66 is inside this variable

Why do I have this error and how can I fix it?

标签: c++

解决方案


class Example {
public:
    Example(const std::string& s) // s is temporary
         : data_(s.data()) {      // data refers to internal data of s
    }                             // at the ctor exit, s is destroyed

    char operator[](size_t pos) const {
        return data_[pos];        // data_ is invalid
    }
private:
    const char* data_;
};

您的构造函数通过引用获取一个字符串,但您没有向它传递一个字符串。您正在传递一个文字,它是一个 const char 数组,它转换为一个 char const*,可用于构造一个临时 std::string。这个临时绑定到您的构造函数参数,并在您的构造函数退出时销毁。

同时,您将指针存储到此临时对象的数据中,因此当您读取数据指针时,它指的是已销毁对象的内部。

首先,您需要确保您没有存储指向临时对象的指针。为此,您需要将构造函数限制为仅使用传入的实际左值字符串。

您可以通过使构造函数显式并用另一个接受右值引用以禁止它们的构造函数重载它来防止其中的一些转换:

class Example {
public:
    explicit Example(const std::string& s) : data_(s.data()) {}                             
    Example(std::string&&) = delete;                             
    ...
};

您可能还需要一个直接采用 char const* 的构造函数。

现在:

int main() {
    // error: call to deleted constructor of 'Example'
    Example e1("asdf");
    // error: call to deleted constructor of 'Example'
    Example e2(std::string("asdf"));

    // ok
    std::string str{"asdf"};
    Example e3(str);
}

当然,您应该处理复制、移动、分配和其他特殊功能。

现场观看 https://godbolt.org/z/EqjT9o


推荐阅读