Skip to content

UInt64 values in string payloads result in null messages #174

Open
@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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions