osquery-1/osquery/utils/schemer/json/schemer_json_impl.h
Stefano Bonicatti ea17c51bb8 Fix undefined-behavior in copyValueFromJValue
Issue highlighted by asan activated in PR osquery/osquery#5628

Imprecisions between float -> double -> json -> double -> float
lead to out of range values been saved into a float variable.
Since json has only the notion of doubles as floating point numbers,
it's better to require to use them.

Also forced the json parser to parse floating point numbers
with full precision, otherwise the test testing for precision would fail.

PR: osquery/osquery#5665
2019-08-16 16:47:18 +02:00

238 lines
7.5 KiB
C++

/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed in accordance with the terms specified in
* the LICENSE file found in the root directory of this source tree.
*/
#pragma once
#include <osquery/utils/schemer/json/schemer_json_error.h>
#include <osquery/utils/expected/expected.h>
#include <osquery/utils/json/json.h>
#include <osquery/utils/schemer/schemer.h>
#include <algorithm>
#include <string>
namespace osquery {
namespace schemer {
namespace impl {
template <typename WriterType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, bool>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType value) {
writer.Bool(value);
}
template <
typename WriterType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, std::int8_t>::value ||
std::is_same<ValueType, std::int16_t>::value ||
std::is_same<ValueType, std::int32_t>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType value) {
writer.Int(value);
}
template <typename WriterType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, std::int64_t>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType const& value) {
writer.Int64(value);
}
template <
typename WriterType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, std::uint8_t>::value ||
std::is_same<ValueType, std::uint16_t>::value ||
std::is_same<ValueType, std::uint32_t>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType value) {
writer.Uint(value);
}
template <typename WriterType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, std::uint64_t>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType const& value) {
writer.Uint64(value);
}
template <typename WriterType,
typename ValueType,
typename std::enable_if<std::is_floating_point<ValueType>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType const& value) {
writer.Double(value);
}
template <typename WriterType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, std::string>::value,
int>::type = 0>
void writeValue(WriterType& writer, ValueType const& value) {
writer.String(value);
}
template <typename WriterType>
class JsonWriter;
template <typename WriterType,
typename ValueType,
typename std::enable_if<has_schema<ValueType>::value, int>::type = 0>
void writeValue(WriterType& writer, ValueType const& value) {
auto next_writer = impl::JsonWriter<WriterType>(writer);
ValueType::discloseSchema(next_writer, value);
}
template <typename WriterType>
class JsonWriter final {
public:
explicit JsonWriter(WriterType& writer) : writer_(writer) {
writer_.StartObject();
}
~JsonWriter() {
writer_.EndObject();
}
template <typename KeyType, typename ValueType>
void record(const KeyType& key, ValueType const& value) {
writer_.Key(key);
writeValue(writer_, value);
}
private:
WriterType& writer_;
};
class JsonReader final {
public:
explicit JsonReader(rapidjson::Value const& jObject) : jObject_(jObject) {
status.ignoreResult();
if (!jObject_.IsObject()) {
status =
createError(JsonError::TypeMismatch)
<< "Wrong type of value: " << jValueToStringForErrorMessage(jObject_)
<< ", expected object";
}
}
template <typename KeyType, typename ValueType>
void record(const KeyType& key, ValueType& value) {
if (status.isError()) {
return;
}
auto const it = jObject_.FindMember(key);
if (it == jObject_.MemberEnd()) {
status = createError(JsonError::MissedKey)
<< "Missed mandatory key " << key;
} else {
copyValueFromJValue(key, value, it->value);
}
}
static inline std::string jValueToStringForErrorMessage(
rapidjson::Value const& jObject) {
auto buf = rapidjson::StringBuffer{};
rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
jObject.Accept(writer);
// make sure string representation of value is not too long
std::size_t const kMaxLength = 22u;
if (buf.GetSize() < kMaxLength) {
return std::string{buf.GetString(), buf.GetSize()};
} else {
return std::string{buf.GetString(), kMaxLength - 3} + "...";
}
}
template <typename KeyType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, std::string>::value,
int>::type = 0>
void copyValueFromJValue(const KeyType& key,
ValueType& value,
rapidjson::Value const& jValue) {
if (jValue.IsString()) {
value.assign(jValue.GetString(), jValue.GetStringLength());
} else {
status = createError(JsonError::TypeMismatch)
<< "Wrong type of value in pair {\"" << key
<< "\":" << jValueToStringForErrorMessage(jValue)
<< "}, expected string";
}
}
template <typename KeyType,
typename ValueType,
typename std::enable_if<std::is_same<ValueType, double>::value,
int>::type = 0>
void copyValueFromJValue(const KeyType& key,
ValueType& value,
rapidjson::Value const& jValue) {
if (jValue.IsNumber()) {
value = jValue.GetDouble();
} else {
status = createError(JsonError::TypeMismatch)
<< "Wrong type of value in pair {\"" << key
<< "\":" << jValueToStringForErrorMessage(jValue)
<< "}, expected floating point number";
}
}
template <typename KeyType,
typename ValueType,
typename std::enable_if<std::is_integral<ValueType>::value,
int>::type = 0>
void copyValueFromJValue(const KeyType& key,
ValueType& value,
rapidjson::Value const& jValue) {
if (jValue.template Is<ValueType>()) {
value = jValue.template Get<ValueType>();
} else {
status = createError(JsonError::TypeMismatch)
<< "Wrong type of value in pair {\"" << key
<< "\":" << jValueToStringForErrorMessage(jValue)
<< "}, expected "
<< boost::core::demangle(typeid(ValueType).name());
}
}
template <
typename KeyType,
typename ValueType,
typename std::enable_if<has_schema<ValueType>::value, int>::type = 0>
void copyValueFromJValue(const KeyType& key,
ValueType& value,
rapidjson::Value const& jValue) {
auto next_reader = impl::JsonReader{jValue};
if (next_reader.status.isError()) {
status = std::move(next_reader.status);
} else {
ValueType::discloseSchema(next_reader, value);
if (next_reader.status.isError()) {
status = std::move(next_reader.status);
}
}
}
public:
ExpectedSuccess<JsonError> status = Success{};
private:
rapidjson::Value const& jObject_;
};
} // namespace impl
} // namespace schemer
} // namespace osquery