代码之家  ›  专栏  ›  技术社区  ›  Silverspur

带标记的联合中字符串伪造的分段错误

  •  0
  • Silverspur  · 技术社区  · 6 年前

    我有以下简化的代码,可以对变量(可以是整数或字符串)进行建模:

    //tagged union
    union VariableValueType {
        std::int64_t  i;
        std::string   s;
    
        VariableValueType(void): i(0) {}
        ~VariableValueType(void) {}
    };
    enum class VariableType {Undefined, Integer, String};
    
    class Variable
    {
        VariableValueType _value;
        VariableType _type;
    
        //constructor from a string
        Variable(const VariableType type, const std::string& value)
         : _type(VariableType::Undefined)
        {
            set(type,value);
        }
    
        // setter
        void set(const VariableType type, const std::string& value)
        {
            switch(type)
            {
                case VariableType::Integer :
                    _value.i = std::stoi(value);
                    _type = VariableType::Integer;
                    break;
                case VariableType::String :
                    _value.s = value; //<-- crashes here!!!
                    _type = VariableType::String;
                    break;
            }
        }
    };
    

    我打电话的时候 set type=String ,我得到一个分割错误的字符串假装线。

    #0  0x00007ffff7b70c73 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    #1  0x00007ffff7b70f89 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
    #2  0x00005555556c3fdd in Variable::set (this=0x5555559d7ae0, type=VariableType::String, value="1") at /home/.../Variable.cpp:118
    

    我不知道这是怎么回事。。。

    有趣的事实:如果我加上 std::string foo = value; 作为第一行 设置


    :

    _value.s ,我收到以下消息:

    error reading variable: Cannot create a lazy string with address 0x0, and a non-zero length.
    

    2 回复  |  直到 6 年前
        1
  •  2
  •   Matthieu Brucher    6 年前

    不要在联合中使用字符串。不要对联合中不是POD的对象使用任何东西(构造函数/析构函数也是如此)。

    使用 std::variant 相反(或 boost::variant

        2
  •  2
  •   Silverspur    6 年前

    正如在评论和另一个答案中所解释的,问题来自于这样一个事实:不是POD的union元素不能被简单地初始化:应该使用一个placement new来代替。当使用这种联合时,还必须注意删除不再使用的非POD元素。

    在这里,应该是这样的:

    // before assigning something else than a string
    if(_type == VariableType::String) _value.s.~std::string();
    
    // to assign a string if current type is *not* a string
    new(&_value.s) std::string(value);
    // to assign a string if current type is a string
    _value.s = value;
    

    还提到,使用专用库可以更容易地处理带有非POD元素的此类联合:

    • 直接在C++ 17中可用 std::variant
    • 可通过Boost boost::variant
    • 可通过mapbox/variant获得(增强替代:打火机库)