首页 > 解决方案 > 为什么列表初始化会导致 Seg 错误?

问题描述

我正在调试我的应用程序的未定义行为。今天,我有机会尝试使用-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 为目标。

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

我知道它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,行为仍然相同

标签: c++

解决方案


在您的默认构造函数中,您没有进行初始化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>()如果没有用户提供的默认构造函数, value 会初始化分配的对象。


推荐阅读