mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 10:05:48 +00:00
Limit STVar recursion during deserialization (RIPD-1603):
Constructing deeply nested objects could allow an attacker to cause a server to overflow its available stack. We now enforce a 10-deep nesting limit, and signal an error if we encounter objects that are nested deeper. Acknowledgements: Ripple thanks Guido Vranken for responsibly disclosing this issues. Bug Bounties and Responsible Disclosures: We welcome reviews of the rippled codebase and urge reviewers to responsibly disclose any issues that they may find. For more on Ripple's Bug Bounty program, please visit https://ripple.com/bug-bounty
This commit is contained in:
committed by
Nikolaos D. Bougalis
parent
9af994ceb4
commit
881cd4cfad
@@ -53,7 +53,7 @@ public:
|
|||||||
STArray (STArray&&);
|
STArray (STArray&&);
|
||||||
STArray (STArray const&) = default;
|
STArray (STArray const&) = default;
|
||||||
STArray (SField const& f, int n);
|
STArray (SField const& f, int n);
|
||||||
STArray (SerialIter& sit, SField const& f);
|
STArray (SerialIter& sit, SField const& f, int depth = 0);
|
||||||
explicit STArray (int n);
|
explicit STArray (int n);
|
||||||
explicit STArray (SField const& f);
|
explicit STArray (SField const& f);
|
||||||
STArray& operator= (STArray const&) = default;
|
STArray& operator= (STArray const&) = default;
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ public:
|
|||||||
STObject(STObject const&) = default;
|
STObject(STObject const&) = default;
|
||||||
STObject (const SOTemplate & type, SField const& name);
|
STObject (const SOTemplate & type, SField const& name);
|
||||||
STObject (const SOTemplate & type, SerialIter & sit, SField const& name);
|
STObject (const SOTemplate & type, SerialIter & sit, SField const& name);
|
||||||
STObject (SerialIter& sit, SField const& name);
|
STObject (SerialIter& sit, SField const& name, int depth = 0);
|
||||||
STObject (SerialIter&& sit, SField const& name)
|
STObject (SerialIter&& sit, SField const& name)
|
||||||
: STObject(sit, name)
|
: STObject(sit, name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ STArray::STArray (SField const& f, int n)
|
|||||||
v_.reserve(n);
|
v_.reserve(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
STArray::STArray (SerialIter& sit, SField const& f)
|
STArray::STArray (SerialIter& sit, SField const& f, int depth)
|
||||||
: STBase(f)
|
: STBase(f)
|
||||||
{
|
{
|
||||||
while (!sit.empty ())
|
while (!sit.empty ())
|
||||||
@@ -97,8 +97,7 @@ STArray::STArray (SerialIter& sit, SField const& f)
|
|||||||
Throw<std::runtime_error> ("Non-object in array");
|
Throw<std::runtime_error> ("Non-object in array");
|
||||||
}
|
}
|
||||||
|
|
||||||
v_.emplace_back(fn);
|
v_.emplace_back(sit, fn, depth+1);
|
||||||
v_.back().set (sit, 1);
|
|
||||||
|
|
||||||
if (v_.back().setTypeFromSField (fn) == STObject::typeSetFail)
|
if (v_.back().setTypeFromSField (fn) == STObject::typeSetFail)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,11 +67,13 @@ STObject::STObject (SOTemplate const& type,
|
|||||||
setType (type);
|
setType (type);
|
||||||
}
|
}
|
||||||
|
|
||||||
STObject::STObject (SerialIter& sit, SField const& name)
|
STObject::STObject (SerialIter& sit, SField const& name, int depth)
|
||||||
: STBase(name)
|
: STBase(name)
|
||||||
, mType(nullptr)
|
, mType(nullptr)
|
||||||
{
|
{
|
||||||
set(sit, 0);
|
if (depth > 10)
|
||||||
|
Throw<std::runtime_error> ("Maximum nesting depth of STObject exceeded");
|
||||||
|
set(sit, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
STObject&
|
STObject&
|
||||||
@@ -206,7 +208,7 @@ bool STObject::set (SerialIter& sit, int depth)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unflatten the field
|
// Unflatten the field
|
||||||
v_.emplace_back(sit, fn);
|
v_.emplace_back(sit, fn, depth+1);
|
||||||
|
|
||||||
// If the object type has a known SOTemplate then set it.
|
// If the object type has a known SOTemplate then set it.
|
||||||
STObject* const obj = dynamic_cast <STObject*> (&(v_.back().get()));
|
STObject* const obj = dynamic_cast <STObject*> (&(v_.back().get()));
|
||||||
|
|||||||
@@ -721,92 +721,102 @@ static boost::optional <STObject> parseObject (
|
|||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
STObject data (inName);
|
try
|
||||||
|
|
||||||
for (auto const& fieldName : json.getMemberNames ())
|
|
||||||
{
|
{
|
||||||
Json::Value const& value = json [fieldName];
|
|
||||||
|
|
||||||
auto const& field = SField::getField (fieldName);
|
STObject data (inName);
|
||||||
|
|
||||||
if (field == sfInvalid)
|
for (auto const& fieldName : json.getMemberNames ())
|
||||||
{
|
{
|
||||||
error = unknown_field (json_name, fieldName);
|
Json::Value const& value = json [fieldName];
|
||||||
|
|
||||||
|
auto const& field = SField::getField (fieldName);
|
||||||
|
|
||||||
|
if (field == sfInvalid)
|
||||||
|
{
|
||||||
|
error = unknown_field (json_name, fieldName);
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (field.fieldType)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Object-style containers (which recurse).
|
||||||
|
case STI_OBJECT:
|
||||||
|
case STI_TRANSACTION:
|
||||||
|
case STI_LEDGERENTRY:
|
||||||
|
case STI_VALIDATION:
|
||||||
|
if (! value.isObject ())
|
||||||
|
{
|
||||||
|
error = not_an_object (json_name, fieldName);
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto ret = parseObject (json_name + "." + fieldName,
|
||||||
|
value, field, depth + 1, error);
|
||||||
|
if (! ret)
|
||||||
|
return boost::none;
|
||||||
|
data.emplace_back (std::move (*ret));
|
||||||
|
}
|
||||||
|
catch (std::exception const&)
|
||||||
|
{
|
||||||
|
error = invalid_data (json_name, fieldName);
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Array-style containers (which recurse).
|
||||||
|
case STI_ARRAY:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto array = parseArray (json_name + "." + fieldName,
|
||||||
|
value, field, depth + 1, error);
|
||||||
|
if (array == boost::none)
|
||||||
|
return boost::none;
|
||||||
|
data.emplace_back (std::move (*array));
|
||||||
|
}
|
||||||
|
catch (std::exception const&)
|
||||||
|
{
|
||||||
|
error = invalid_data (json_name, fieldName);
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Everything else (types that don't recurse).
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
auto leaf =
|
||||||
|
parseLeaf (json_name, fieldName, &inName, value, error);
|
||||||
|
|
||||||
|
if (!leaf)
|
||||||
|
return boost::none;
|
||||||
|
|
||||||
|
data.emplace_back (std::move (*leaf));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some inner object types have templates. Attempt to apply that.
|
||||||
|
if (data.setTypeFromSField (inName) == STObject::typeSetFail)
|
||||||
|
{
|
||||||
|
error = template_mismatch (inName);
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (field.fieldType)
|
return std::move (data);
|
||||||
{
|
|
||||||
|
|
||||||
// Object-style containers (which recurse).
|
|
||||||
case STI_OBJECT:
|
|
||||||
case STI_TRANSACTION:
|
|
||||||
case STI_LEDGERENTRY:
|
|
||||||
case STI_VALIDATION:
|
|
||||||
if (! value.isObjectOrNull ())
|
|
||||||
{
|
|
||||||
error = not_an_object (json_name, fieldName);
|
|
||||||
return boost::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto ret = parseObject (json_name + "." + fieldName,
|
|
||||||
value, field, depth + 1, error);
|
|
||||||
if (! ret)
|
|
||||||
return boost::none;
|
|
||||||
data.emplace_back (std::move (*ret));
|
|
||||||
}
|
|
||||||
catch (std::exception const&)
|
|
||||||
{
|
|
||||||
error = invalid_data (json_name, fieldName);
|
|
||||||
return boost::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Array-style containers (which recurse).
|
|
||||||
case STI_ARRAY:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto array = parseArray (json_name + "." + fieldName,
|
|
||||||
value, field, depth + 1, error);
|
|
||||||
if (array == boost::none)
|
|
||||||
return boost::none;
|
|
||||||
data.emplace_back (std::move (*array));
|
|
||||||
}
|
|
||||||
catch (std::exception const&)
|
|
||||||
{
|
|
||||||
error = invalid_data (json_name, fieldName);
|
|
||||||
return boost::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Everything else (types that don't recurse).
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
auto leaf =
|
|
||||||
parseLeaf (json_name, fieldName, &inName, value, error);
|
|
||||||
|
|
||||||
if (!leaf)
|
|
||||||
return boost::none;
|
|
||||||
|
|
||||||
data.emplace_back (std::move (*leaf));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (std::exception const&)
|
||||||
// Some inner object types have templates. Attempt to apply that.
|
|
||||||
if (data.setTypeFromSField (inName) == STObject::typeSetFail)
|
|
||||||
{
|
{
|
||||||
error = template_mismatch (inName);
|
error = invalid_data (json_name);
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::move (data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boost::optional <detail::STVar> parseArray (
|
static boost::optional <detail::STVar> parseArray (
|
||||||
|
|||||||
@@ -108,8 +108,10 @@ STVar::STVar (nonPresentObject_t, SField const& name)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
STVar::STVar (SerialIter& sit, SField const& name)
|
STVar::STVar (SerialIter& sit, SField const& name, int depth)
|
||||||
{
|
{
|
||||||
|
if (depth > 10)
|
||||||
|
Throw<std::runtime_error> ("Maximum nesting depth of STVar exceeded");
|
||||||
switch (name.fieldType)
|
switch (name.fieldType)
|
||||||
{
|
{
|
||||||
case STI_NOTPRESENT: construct<STBase>(name); return;
|
case STI_NOTPRESENT: construct<STBase>(name); return;
|
||||||
@@ -125,8 +127,8 @@ STVar::STVar (SerialIter& sit, SField const& name)
|
|||||||
case STI_VL: construct<STBlob>(sit, name); return;
|
case STI_VL: construct<STBlob>(sit, name); return;
|
||||||
case STI_ACCOUNT: construct<STAccount>(sit, name); return;
|
case STI_ACCOUNT: construct<STAccount>(sit, name); return;
|
||||||
case STI_PATHSET: construct<STPathSet>(sit, name); return;
|
case STI_PATHSET: construct<STPathSet>(sit, name); return;
|
||||||
case STI_OBJECT: construct<STObject>(sit, name); return;
|
case STI_OBJECT: construct<STObject>(sit, name, depth); return;
|
||||||
case STI_ARRAY: construct<STArray>(sit, name); return;
|
case STI_ARRAY: construct<STArray>(sit, name, depth); return;
|
||||||
default:
|
default:
|
||||||
Throw<std::runtime_error> ("Unknown object type");
|
Throw<std::runtime_error> ("Unknown object type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public:
|
|||||||
|
|
||||||
STVar (defaultObject_t, SField const& name);
|
STVar (defaultObject_t, SField const& name);
|
||||||
STVar (nonPresentObject_t, SField const& name);
|
STVar (nonPresentObject_t, SField const& name);
|
||||||
STVar (SerialIter& sit, SField const& name);
|
STVar (SerialIter& sit, SField const& name, int depth = 0);
|
||||||
|
|
||||||
STBase& get() { return *p_; }
|
STBase& get() { return *p_; }
|
||||||
STBase& operator*() { return get(); }
|
STBase& operator*() { return get(); }
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user