Почему AddressSanitizer сообщает об ошибке использования стека после области действия? - PullRequest
0 голосов
/ 25 февраля 2020

У меня есть класс, который управляет контейнером. Моя функция вставки-значения возвращает объект, деструктор которого удаляет вставленное значение. Идея состоит в том, что я могу зарегистрировать значение в контейнере, сохранить полученный объект Registration, а затем просто вывести этот объект go из области видимости, чтобы удалить зарегистрированное значение. Я уменьшил код проблемного c до следующего:

#include <iostream>
#include <list>
#include <vector>
#include <memory>
#include <string>
#include <unordered_map>

class Directory {
public:
    struct Registration {
        Registration(Directory& dir, const std::string& key)
            : dir_{dir}, key_{key}
        {}

        ~Registration() { dir_.del(key_); }

        Directory&  dir_;
        const std::string&  key_;
    };

    using registration_t = std::unique_ptr<Registration>;

    registration_t put(std::string key, std::string val) {
        map_[key] = val;
        return std::make_unique<Registration>(*this, key);
    }

    void del(const std::string& key) {
        map_.erase(key);
    }

private:
    std::unordered_map<std::string,std::string> map_;
};

int main(int argc, char* argv[])
{
    Directory directory_;
    std::list<typename Directory::registration_t> registrations_;

    registrations_.push_back(directory_.put("asdf", "djdjdjdjd"));

    for ( const auto& i: registrations_ )
        std::cout << i->key_ << std::endl;
}

Когда я запускаю это с AddressSanitizer, он сообщает следующее:

=================================================================
==153498==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffcb33622e0 at pc 0x7fb29b24a2fd bp 0x7ffcb3362140 sp 0x7ffcb33618e8
READ of size 4 at 0x7ffcb33622e0 thread T0
    #0 0x7fb29b24a2fc  (/lib64/libasan.so.5+0x6a2fc)
    #1 0x7fb29b116082 in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) (/lib64/libstdc++.so.6+0x136082)
    #2 0x402826 in main /home/john/asdf/test.cpp:43
    #3 0x7fb29accf1a2 in __libc_start_main ../csu/libc-start.c:308
    #4 0x4023dd in _start (/home/john/asdf/a.out+0x4023dd)

Address 0x7ffcb33622e0 is located in stack of thread T0 at offset 240 in frame
    #0 0x4024a5 in main /home/john/asdf/test.cpp:37

  This frame has 9 object(s):
    [32, 33) '<unknown>'
    [48, 49) '<unknown>'
    [64, 72) '<unknown>'
    [96, 104) '__for_begin' (line 42)
    [128, 136) '__for_end' (line 42)
    [160, 184) 'registrations_' (line 39)
    [224, 256) '<unknown>' <== Memory access at offset 240 is inside this variable
    [288, 320) '<unknown>'
    [352, 408) 'directory_' (line 38)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope (/lib64/libasan.so.5+0x6a2fc) 
Shadow bytes around the buggy address:
  0x100016664400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100016664410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100016664420: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100016664430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
  0x100016664440: f1 f1 f8 f2 f8 f2 f8 f2 f2 f2 00 f2 f2 f2 00 f2
=>0x100016664450: f2 f2 00 00 00 f2 f2 f2 f2 f2 f8 f8[f8]f8 f2 f2
  0x100016664460: f2 f2 f8 f8 f8 f8 f2 f2 f2 f2 00 00 00 00 00 00
  0x100016664470: 00 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 00 00
  0x100016664480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100016664490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000166644a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Это не имеет значения если directory_ и registrations_ являются локальными по отношению к main() или глобальными. Я попробовал его как с registrations_ как vector и list, так и с Directory::registration_t как shared_ptr и unique_ptr.

Я уверен, что должен что-то упустил, но я уже давно смотрю на это и не могу понять, что это такое.

1 Ответ

1 голос
/ 25 февраля 2020

Функция Directory::put принимает значение key, а затем вы сохраняете ссылку на этот объект.

Это не очень хорошо работает, поскольку объект key будет разрушен и перестанет работать. существует, как только возвращается функция put. Любая попытка использовать эту висячую ссылку приведет к неопределенному поведению .

Вместо этого сохраните копию в классе Registration.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...