Skip to content

UInt64 values in string payloads result in null messages #174

@curtkrone

Description

@curtkrone

socket.io-client-cpp parses incoming string payloads using bool packet::parse(const string& payload_ptr), which uses this code to parse the payload string:

Document doc;
doc.Parse<0>(payload_ptr.data()+json_pos);
_message = from_json(doc, vector<shared_ptr<const string> >());
return false;

Parse eventually calls this method in reader.h to parse the number:

template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseNumber(InputStream& is, Handler& handler) {

When parsing an unsigned integer value requiring 64 bits (e.g. 17657333360744292000), ParseNumber correctly parses the int64_t, and gives it to the handler using:

cont = handler.Uint64(i64);

This ends up calling this code to set the value:

explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) {
    data_.n.u64 = u64;
    if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
        flags_ |= kInt64Flag;
    if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000)))
        flags_ |= kUintFlag;
    if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000)))
        flags_ |= kIntFlag;
}

Note that kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag.

When Parse returns to packet::parse, we create a message using:

message::ptr from_json(Value const& value, vector<shared_ptr<const string> > const& buffers)
{
    if(value.IsInt64())
    {
        return int_message::create(value.GetInt64());
    }
    else if(value.IsDouble())
    {
        return double_message::create(value.GetDouble());
    }

    // ... UNRELATED ELSE-IFS REMOVED FOR BREVITY ...

    else if(value.IsNull())
    {
        return null_message::create();
    }
    return message::ptr();
}

Note that there's no explicit check for IsUint64(), although that is defined in document.h. Both the Int64 and Double checks fail, so we end up calling return message::ptr(); instead, essentially throwing away the UInt64 that we parsed.

I think the easiest way to address this (without adding to the message types defined in sio_message.h) is to add a check for UInt64 values to from_json and return them as doubles or integers:

message::ptr from_json(Value const& value, vector<shared_ptr<const string> > const& buffers)
{
    if(value.IsInt64())
    {
        return int_message::create(value.GetInt64());
    }
    else if (value.IsUint64())
    {
      return double_message::create(value.GetDouble());
    }  
    else if(value.IsDouble())
    {
        return double_message::create(value.GetDouble());
    }

    // ... UNRELATED ELSE-IFS REMOVED FOR BREVITY ...

    else if(value.IsNull())
    {
        return null_message::create();
    }
    return message::ptr();
}

But I'm unsure which type would be better, or if this might have undesirable side-effects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions