为什么列表初始化会导致 Seg 错误?

时间:2021-02-10 13:57:04

标签: c++

我正在调试应用程序的未定义行为。今天,我有机会尝试使用 -fsanitize=undefined -fsanitize=address 标志,并且像往常一样继续编译我的应用程序。原来,它在运行时发现了一些东西,并打印出了这个日志,

AddressSanitizer:DEADLYSIGNAL
=================================================================
==502288==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x0000004ce04b bp 0x611000000b98 sp 0x7ffc441a5bf8 T0)
==502288==The signal is caused by a READ memory access.
==502288==Hint: this fault was caused by a dereference of a high value address (see register values below).  Dissassemble the provided pc to learn which register was used.
    #0 0x4ce04b in llhttp__on_message_begin /home/llhttp/src/native/api.c:144:3
    #1 0x4ce4f4 in llhttp__internal__run /home/llhttp/build/c/llhttp.c:14590:13
    #2 0x4ce3e1 in llhttp__internal_execute /home/llhttp/build/c/llhttp.c:14624:10
    #3 0x4c8661 in HTTPTransport::initialize() /home/example.cpp:70:29
    #4 0x4c890f in main /home/example.cpp:76:12
    #5 0x7f09e9f590b2 in __libc_start_main /build/glibc-ZN95T4/glibc-2.31/csu/../csu/libc-start.c:308:16
    #6 0x41d53d in _start (/home/example+0x41d53d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/llhttp/src/native/api.c:144:3 in llhttp__on_message_begin
==502288==ABORTING

我最终找出了导致这种情况的原因。来了,

class Connection : public HTTPTransport
{
public:
    llhttp_t httpParser;
    llhttp_settings_t httpParserSettings;
    sessions session;

    bool auth;
     
    // This line causing it but if I remove it everything works.
    Connection() : auth(){}
};

但是我发现如果我去掉unique_ptr,直接去类的初始化,那行没有效果,所以segfault。

有趣的是,如果我使用 unique_ptr 并删除该行,然后一切正常,这不会发生。

我也试过在课堂上使用类内成员初始化方法;但它不起作用并抛出相同的段错误。

我正在 Clang 编译器上编译它并以 C++17 为目标。

总而言之,这里是我尝试过的东西,但没有以任何方式影响段错误,

  • 类内成员初始化
  • 直接初始化类而不是 unique_ptr
  • 移除该类与 HTTPTransport 基类的继承

我知道 std::make_unique 不会调用类列表初始值设定项,但事实是我在直接初始化类时遇到了相同的段错误,我是这样初始化的,

Connection currentConnection; 

最少的可重现代码,

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include "llhttp.h"

class HTTPTransport
{
public:
    struct sessions
    {
        std::string url;
    };

private:
    static int handleOnUrl(llhttp_t *ll, const char *buf, size_t len);

public:
    void initialize();    
};

class Connection : public HTTPTransport
{
public:
    llhttp_t httpParser;
    llhttp_settings_t httpParserSettings;
    sessions session;

    bool auth;

    Connection() : auth(){}
};
    
int HTTPTransport::handleOnUrl(llhttp_t *ll, const char *buf, size_t len)
{
    sessions *session = static_cast<sessions *>(ll->data);
    session->url = std::string(buf, len);
    return 0;
}

void HTTPTransport::initialize()
{
    auto currentConnection = std::make_unique<Connection>();

    llhttp_init(&currentConnection->httpParser, HTTP_REQUEST, &currentConnection->httpParserSettings);

    /* Set callbacks */

    currentConnection->httpParserSettings.on_url = handleOnUrl;
    currentConnection->httpParser.data = &currentConnection->session;

    std::string res = "CONNECT [2607:f8b0:4008:80c::2004]:443 HTTP/1.1\r\nHost: [2607:f8b0:4008:80c::2004]:443\r\n\r\n";

    enum llhttp_errno err = llhttp_execute(&currentConnection->httpParser, res.c_str(), res.length());
}

int main() {
    HTTPTransport Server;

    Server.initialize();

    return 0;
}

您将需要 llhttp 解析器库来构建它。您可以在 Github 找到它。

值得补充的是,即使我没有使用 unique_ptr 行为仍然相同

2 个答案:

答案 0 :(得分:3)

在您的默认构造函数中,您没有初始化 httpParserSettings,这意味着它在构造 Connection 对象时会有垃圾数据,最终 llhttp_init 将尝试使用这些垃圾数据。要解决此问题,请对默认构造函数中的所有成员进行值初始化:

class Connection : public HTTPTransport
{
public:
    llhttp_t httpParser;
    llhttp_settings_t httpParserSettings;
    sessions session;

    bool auth;

    Connection()
        : httpParser{},
          httpParserSettings{},
          session{},
          auth{}
    {}
};

或提供成员初始值设定项:

class Connection : public HTTPTransport
{
public:
    llhttp_t httpParser{};
    llhttp_settings_t httpParserSettings{};
    sessions session{};

    bool auth{};
    // no constructor needed in this case
};

它与没有默认构造函数的 Connection 一起使用的原因是,如果没有用户提供的默认构造函数,则 std::make_unique<Connection>() 值会初始化分配的对象。

答案 1 :(得分:0)

// This line causing it but if I remove it everything works.

检查此页面:https://en.cppreference.com/w/cpp/language/rule_of_three

由于您需要 Connection 对象可移动(通过 std::unique_ptr)但不可复制,我建议您这样做:

{
public:
    llhttp_t httpParser;
    llhttp_settings_t httpParserSettings;
    sessions session;

    bool auth;

    Connection() : auth(){}
    ~Connection() = default;

    // Non-copyable
    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;

    // Movable
    Connection(Connection&&) = default;
    Connection& operator=(Connection&&) = default;
};

编辑:不过,您似乎只定义了默认构造函数,所以我不确定这是问题...