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 const&) = default;
|
||||
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 (SField const& f);
|
||||
STArray& operator= (STArray const&) = default;
|
||||
|
||||
@@ -281,7 +281,7 @@ public:
|
||||
STObject(STObject const&) = default;
|
||||
STObject (const SOTemplate & type, 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(sit, name)
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ STArray::STArray (SField const& f, int n)
|
||||
v_.reserve(n);
|
||||
}
|
||||
|
||||
STArray::STArray (SerialIter& sit, SField const& f)
|
||||
STArray::STArray (SerialIter& sit, SField const& f, int depth)
|
||||
: STBase(f)
|
||||
{
|
||||
while (!sit.empty ())
|
||||
@@ -97,8 +97,7 @@ STArray::STArray (SerialIter& sit, SField const& f)
|
||||
Throw<std::runtime_error> ("Non-object in array");
|
||||
}
|
||||
|
||||
v_.emplace_back(fn);
|
||||
v_.back().set (sit, 1);
|
||||
v_.emplace_back(sit, fn, depth+1);
|
||||
|
||||
if (v_.back().setTypeFromSField (fn) == STObject::typeSetFail)
|
||||
{
|
||||
|
||||
@@ -67,11 +67,13 @@ STObject::STObject (SOTemplate const& type,
|
||||
setType (type);
|
||||
}
|
||||
|
||||
STObject::STObject (SerialIter& sit, SField const& name)
|
||||
STObject::STObject (SerialIter& sit, SField const& name, int depth)
|
||||
: STBase(name)
|
||||
, mType(nullptr)
|
||||
{
|
||||
set(sit, 0);
|
||||
if (depth > 10)
|
||||
Throw<std::runtime_error> ("Maximum nesting depth of STObject exceeded");
|
||||
set(sit, depth);
|
||||
}
|
||||
|
||||
STObject&
|
||||
@@ -206,7 +208,7 @@ bool STObject::set (SerialIter& sit, int depth)
|
||||
}
|
||||
|
||||
// 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.
|
||||
STObject* const obj = dynamic_cast <STObject*> (&(v_.back().get()));
|
||||
|
||||
@@ -721,92 +721,102 @@ static boost::optional <STObject> parseObject (
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
STObject data (inName);
|
||||
|
||||
for (auto const& fieldName : json.getMemberNames ())
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Some inner object types have templates. Attempt to apply that.
|
||||
if (data.setTypeFromSField (inName) == STObject::typeSetFail)
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = template_mismatch (inName);
|
||||
error = invalid_data (json_name);
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return std::move (data);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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_ACCOUNT: construct<STAccount>(sit, name); return;
|
||||
case STI_PATHSET: construct<STPathSet>(sit, name); return;
|
||||
case STI_OBJECT: construct<STObject>(sit, name); return;
|
||||
case STI_ARRAY: construct<STArray>(sit, name); return;
|
||||
case STI_OBJECT: construct<STObject>(sit, name, depth); return;
|
||||
case STI_ARRAY: construct<STArray>(sit, name, depth); return;
|
||||
default:
|
||||
Throw<std::runtime_error> ("Unknown object type");
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
|
||||
STVar (defaultObject_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& operator*() { return get(); }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user