mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-04 10:45:50 +00:00
add sto APIs
This commit is contained in:
@@ -43,11 +43,22 @@ public:
|
||||
// util_keylet
|
||||
|
||||
/// sto APIs
|
||||
// sto_validate
|
||||
// sto_subfield
|
||||
// sto_subarray
|
||||
// sto_emplace
|
||||
// sto_erase
|
||||
Expected<bool, HookReturnCode>
|
||||
sto_validate(Bytes const& data) const;
|
||||
|
||||
Expected<std::pair<uint32_t, uint32_t>, HookReturnCode>
|
||||
sto_subfield(Bytes const& data, uint32_t field_id) const;
|
||||
|
||||
Expected<std::pair<uint32_t, uint32_t>, HookReturnCode>
|
||||
sto_subarray(Bytes const& data, uint32_t index_id) const;
|
||||
|
||||
Expected<Bytes, HookReturnCode>
|
||||
sto_emplace(
|
||||
Bytes const& source_object,
|
||||
std::optional<Bytes> const& field_object,
|
||||
uint32_t field_id) const;
|
||||
|
||||
// sto_erase: same as sto_emplace with field_object = nullopt
|
||||
|
||||
/// etxn APIs
|
||||
Expected<std::shared_ptr<Transaction>, HookReturnCode>
|
||||
@@ -193,7 +204,8 @@ public:
|
||||
ledger_keylet(Keylet const& klLo, Keylet const& klHi) const;
|
||||
|
||||
/// state APIs
|
||||
// state
|
||||
|
||||
// state: same as state_foreign with ns = 0 and account = hook_account()
|
||||
|
||||
Expected<Bytes, HookReturnCode>
|
||||
state_foreign(
|
||||
@@ -201,7 +213,8 @@ public:
|
||||
uint256 const& ns,
|
||||
AccountID const& account) const;
|
||||
|
||||
// state_set
|
||||
// state_set: same as state_foreign_set with ns = 0 and account =
|
||||
// hook_account()
|
||||
|
||||
Expected<uint64_t, HookReturnCode>
|
||||
state_foreign_set(
|
||||
@@ -301,6 +314,30 @@ private:
|
||||
uint256 const& key,
|
||||
Bytes const& data,
|
||||
bool modified) const;
|
||||
|
||||
// these are only used by get_stobject_length below
|
||||
enum parse_error {
|
||||
pe_unexpected_end = -1,
|
||||
pe_unknown_type_early = -2, // detected early
|
||||
pe_unknown_type_late = -3, // end of function
|
||||
pe_excessive_nesting = -4,
|
||||
pe_excessive_size = -5
|
||||
};
|
||||
|
||||
inline Expected<
|
||||
int32_t,
|
||||
parse_error>
|
||||
get_stobject_length(
|
||||
unsigned char* start, // in - begin iterator
|
||||
unsigned char* maxptr, // in - end iterator
|
||||
int& type, // out - populated by serialized type code
|
||||
int& field, // out - populated by serialized field code
|
||||
int& payload_start, // out - the start of actual payload data for
|
||||
// this type
|
||||
int& payload_length, // out - the length of actual payload data for
|
||||
// this type
|
||||
int recursion_depth = 0) // used internally
|
||||
const;
|
||||
};
|
||||
|
||||
} // namespace hook
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <ripple/protocol/STObject.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/tokens.h>
|
||||
#include "ripple/basics/Expected.h"
|
||||
#include "test/jtx/utility.h"
|
||||
#include <cfenv>
|
||||
|
||||
namespace hook {
|
||||
@@ -335,10 +337,246 @@ HookAPI::util_sha512h(Slice const& data) const
|
||||
// util_keylet
|
||||
|
||||
/// sto APIs
|
||||
// sto_validate
|
||||
// sto_subfield
|
||||
// sto_subarray
|
||||
// sto_emplace
|
||||
Expected<bool, HookReturnCode>
|
||||
HookAPI::sto_validate(Bytes const& data) const
|
||||
{
|
||||
if (data.size() < 2)
|
||||
return Unexpected(TOO_SMALL);
|
||||
|
||||
unsigned char* start = const_cast<unsigned char*>(data.data());
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + data.size();
|
||||
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
auto const length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (!length)
|
||||
return 0;
|
||||
upto += length.value();
|
||||
}
|
||||
|
||||
return upto == end ? 1 : 0;
|
||||
}
|
||||
|
||||
Expected<std::pair<uint32_t, uint32_t>, HookReturnCode>
|
||||
HookAPI::sto_subfield(Bytes const& data, uint32_t field_id) const
|
||||
{
|
||||
if (data.size() < 2)
|
||||
return Unexpected(TOO_SMALL);
|
||||
|
||||
unsigned char* start = const_cast<unsigned char*>(data.data());
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + data.size();
|
||||
|
||||
DBG_PRINTF(
|
||||
"sto_subfield called, looking for field %u type %u\n",
|
||||
field_id & 0xFFFF,
|
||||
(field_id >> 16));
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF((j == 0 ? " >%02X< " : " %02X "), *(start + j));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
// if ((*upto & 0xF0) == 0xE0)
|
||||
// upto++;
|
||||
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
auto const length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (!length)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
if ((type << 16) + field == field_id)
|
||||
{
|
||||
DBG_PRINTF(
|
||||
"sto_subfield returned for field %u type %u\n",
|
||||
field_id & 0xFFFF,
|
||||
(field_id >> 16));
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF((j == 0 ? " [%02X] " : " %02X "), *(upto + j));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
if (type == 0xF) // we return arrays fully formed
|
||||
return std::make_pair(upto - start, length.value());
|
||||
|
||||
// return pointers to all other objects as payloads
|
||||
return std::make_pair(upto - start + payload_start, payload_length);
|
||||
}
|
||||
upto += length.value();
|
||||
}
|
||||
|
||||
if (upto != end)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
|
||||
return Unexpected(DOESNT_EXIST);
|
||||
}
|
||||
|
||||
Expected<std::pair<uint32_t, uint32_t>, HookReturnCode>
|
||||
HookAPI::sto_subarray(Bytes const& data, uint32_t index_id) const
|
||||
{
|
||||
if (data.size() < 2)
|
||||
return Unexpected(TOO_SMALL);
|
||||
|
||||
unsigned char* start = const_cast<unsigned char*>(data.data());
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + data.size();
|
||||
|
||||
// unwrap the array if it is wrapped,
|
||||
// by removing a byte from the start and end
|
||||
if ((*upto & 0xF0U) == 0xF0U)
|
||||
{
|
||||
upto++;
|
||||
end--;
|
||||
}
|
||||
|
||||
if (upto >= end)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
|
||||
/*
|
||||
DBG_PRINTF("sto_subarray called, looking for index %u\n", index_id);
|
||||
for (int j = -5; j < 5; ++j)
|
||||
printf(( j == 0 ? " >%02X< " : " %02X "), *(start + j));
|
||||
DBG_PRINTF("\n");
|
||||
*/
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
auto const length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (!length)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
|
||||
if (i == index_id)
|
||||
{
|
||||
DBG_PRINTF("sto_subarray returned for index %u\n", index_id);
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF(
|
||||
(j == 0 ? " [%02X] " : " %02X "),
|
||||
*(upto + j + length.value()));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
return std::make_pair(upto - start, length.value());
|
||||
}
|
||||
upto += length.value();
|
||||
}
|
||||
|
||||
if (upto != end)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
|
||||
return Unexpected(DOESNT_EXIST);
|
||||
}
|
||||
|
||||
Expected<Bytes, HookReturnCode>
|
||||
HookAPI::sto_emplace(
|
||||
Bytes const& source_object,
|
||||
std::optional<Bytes> const& field_object,
|
||||
uint32_t field_id) const
|
||||
{
|
||||
// RH TODO: put these constants somewhere (votable?)
|
||||
if (source_object.size() > 1024 * 16)
|
||||
return Unexpected(TOO_BIG);
|
||||
|
||||
if (source_object.size() < 2)
|
||||
return Unexpected(TOO_SMALL);
|
||||
|
||||
if (!field_object.has_value())
|
||||
{
|
||||
// this is a delete operation
|
||||
}
|
||||
else
|
||||
{
|
||||
if (field_object->size() > 4096)
|
||||
return Unexpected(TOO_BIG);
|
||||
|
||||
if (field_object->size() < 2)
|
||||
return Unexpected(TOO_SMALL);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> out(
|
||||
(size_t)(
|
||||
source_object.size() + (field_object ? field_object->size() : 0)),
|
||||
(uint8_t)0);
|
||||
uint8_t* write_ptr = out.data();
|
||||
|
||||
// we must inject the field at the canonical location....
|
||||
// so find that location
|
||||
unsigned char* start = const_cast<unsigned char*>(source_object.data());
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + source_object.size();
|
||||
unsigned char* inject_start = end;
|
||||
unsigned char* inject_end = end;
|
||||
|
||||
DBG_PRINTF(
|
||||
"sto_emplace called, looking for field %u type %u\n",
|
||||
field_id & 0xFFFF,
|
||||
(field_id >> 16));
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF((j == 0 ? " >%02X< " : " %02X "), *(start + j));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
auto const length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (!length)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
if ((type << 16) + field == field_id)
|
||||
{
|
||||
inject_start = upto;
|
||||
inject_end = upto + length.value();
|
||||
break;
|
||||
}
|
||||
else if ((type << 16) + field > field_id)
|
||||
{
|
||||
inject_start = upto;
|
||||
inject_end = upto;
|
||||
break;
|
||||
}
|
||||
upto += length.value();
|
||||
}
|
||||
|
||||
// if the scan loop ends past the end of the source object
|
||||
// then the source object is invalid/corrupt, so we must
|
||||
// return an error
|
||||
if (upto > end)
|
||||
return Unexpected(PARSE_ERROR);
|
||||
|
||||
// upto is injection point
|
||||
int64_t bytes_written = 0;
|
||||
|
||||
// part 1
|
||||
if (inject_start - start > 0)
|
||||
{
|
||||
size_t len = inject_start - start;
|
||||
memcpy(write_ptr, start, len);
|
||||
bytes_written += len;
|
||||
}
|
||||
|
||||
if (field_object && field_object->size() > 0)
|
||||
{
|
||||
// write the field (or don't if it's a delete operation)
|
||||
memcpy(
|
||||
write_ptr + bytes_written,
|
||||
field_object->data(),
|
||||
field_object->size());
|
||||
bytes_written += field_object->size();
|
||||
}
|
||||
|
||||
// part 2
|
||||
if (end - inject_end > 0)
|
||||
{
|
||||
size_t len = end - inject_end;
|
||||
memcpy(write_ptr + bytes_written, inject_end, len);
|
||||
bytes_written += len;
|
||||
}
|
||||
|
||||
out.resize(bytes_written);
|
||||
return out;
|
||||
}
|
||||
|
||||
// sto_erase
|
||||
|
||||
/// etxn APIs
|
||||
@@ -2507,4 +2745,199 @@ HookAPI::set_state_cache(
|
||||
return 1;
|
||||
}
|
||||
|
||||
// RH NOTE this is a light-weight stobject parsing function for drilling into a
|
||||
// provided serialzied object however it could probably be replaced by an
|
||||
// existing class or routine or set of routines in XRPLD Returns object length
|
||||
// including header bytes (and footer bytes in the event of array or object)
|
||||
// negative indicates error
|
||||
inline Expected<
|
||||
int32_t,
|
||||
HookAPI::parse_error>
|
||||
HookAPI::get_stobject_length(
|
||||
unsigned char* start, // in - begin iterator
|
||||
unsigned char* maxptr, // in - end iterator
|
||||
int& type, // out - populated by serialized type code
|
||||
int& field, // out - populated by serialized field code
|
||||
int& payload_start, // out - the start of actual payload data for this type
|
||||
int& payload_length, // out - the length of actual payload data for this
|
||||
// type
|
||||
int recursion_depth) // used internally
|
||||
const
|
||||
{
|
||||
if (recursion_depth > 10)
|
||||
return Unexpected(pe_excessive_nesting);
|
||||
|
||||
unsigned char* end = maxptr;
|
||||
unsigned char* upto = start;
|
||||
int high = *upto >> 4;
|
||||
int low = *upto & 0xF;
|
||||
|
||||
upto++;
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
if (high > 0 && low > 0)
|
||||
{
|
||||
// common type common field
|
||||
type = high;
|
||||
field = low;
|
||||
}
|
||||
else if (high > 0)
|
||||
{
|
||||
// common type, uncommon field
|
||||
type = high;
|
||||
field = *upto++;
|
||||
}
|
||||
else if (low > 0)
|
||||
{
|
||||
// common field, uncommon type
|
||||
field = low;
|
||||
type = *upto++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// uncommon type and field
|
||||
type = *upto++;
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
field = *upto++;
|
||||
}
|
||||
|
||||
DBG_PRINTF(
|
||||
"%d get_st_object found field %d type %d\n",
|
||||
recursion_depth,
|
||||
field,
|
||||
type);
|
||||
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
|
||||
// RH TODO: link this to rippled's internal STObject constants
|
||||
// E.g.:
|
||||
/*
|
||||
int field_code = (safe_cast<int>(type) << 16) | field;
|
||||
auto const& fieldObj = ripple::SField::getField;
|
||||
*/
|
||||
|
||||
if (type < 1 || type > 19 || (type >= 9 && type <= 13))
|
||||
return Unexpected(pe_unknown_type_early);
|
||||
|
||||
bool is_vl = (type == 8 /*ACCID*/ || type == 7 || type == 18 || type == 19);
|
||||
|
||||
int length = -1;
|
||||
if (is_vl)
|
||||
{
|
||||
length = *upto++;
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
|
||||
if (length < 193)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (length > 192 && length < 241)
|
||||
{
|
||||
length -= 193;
|
||||
length *= 256;
|
||||
length += *upto++ + 193;
|
||||
if (upto > end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
}
|
||||
else
|
||||
{
|
||||
int b2 = *upto++;
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
length -= 241;
|
||||
length *= 65536;
|
||||
length += 12481 + (b2 * 256) + *upto++;
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
}
|
||||
}
|
||||
else if ((type >= 1 && type <= 5) || type == 16 || type == 17)
|
||||
{
|
||||
length =
|
||||
(type == 1
|
||||
? 2
|
||||
: (type == 2
|
||||
? 4
|
||||
: (type == 3
|
||||
? 8
|
||||
: (type == 4
|
||||
? 16
|
||||
: (type == 5
|
||||
? 32
|
||||
: (type == 16
|
||||
? 1
|
||||
: (type == 17 ? 20
|
||||
: -1)))))));
|
||||
}
|
||||
else if (type == 6) /* AMOUNT */
|
||||
{
|
||||
length = (*upto >> 6 == 1) ? 8 : 48;
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
}
|
||||
|
||||
if (length > -1)
|
||||
{
|
||||
payload_start = upto - start;
|
||||
payload_length = length;
|
||||
DBG_PRINTF(
|
||||
"%d get_stobject_length field: %d Type: %d VL: %s Len: %d "
|
||||
"Payload_Start: %d Payload_Len: %d\n",
|
||||
recursion_depth,
|
||||
field,
|
||||
type,
|
||||
(is_vl ? "yes" : "no"),
|
||||
length,
|
||||
payload_start,
|
||||
payload_length);
|
||||
return length + (upto - start);
|
||||
}
|
||||
|
||||
if (type == 15 || type == 14) /* Object / Array */
|
||||
{
|
||||
payload_start = upto - start;
|
||||
|
||||
for (int i = 0; i < 1024; ++i)
|
||||
{
|
||||
int subfield = -1, subtype = -1, payload_start_ = -1,
|
||||
payload_length_ = -1;
|
||||
auto const sublength = get_stobject_length(
|
||||
upto,
|
||||
end,
|
||||
subtype,
|
||||
subfield,
|
||||
payload_start_,
|
||||
payload_length_,
|
||||
recursion_depth + 1);
|
||||
DBG_PRINTF(
|
||||
"%d get_stobject_length i %d %d-%d, upto %d sublength %d\n",
|
||||
recursion_depth,
|
||||
i,
|
||||
subtype,
|
||||
subfield,
|
||||
upto - start,
|
||||
sublength);
|
||||
if (!sublength)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
upto += sublength.value();
|
||||
if (upto >= end)
|
||||
return Unexpected(pe_unexpected_end);
|
||||
|
||||
if ((*upto == 0xE1U && type == 0xEU) ||
|
||||
(*upto == 0xF1U && type == 0xFU))
|
||||
{
|
||||
payload_length = upto - start - payload_start;
|
||||
upto++;
|
||||
return (upto - start);
|
||||
}
|
||||
}
|
||||
return Unexpected(pe_excessive_size);
|
||||
}
|
||||
|
||||
return Unexpected(pe_unknown_type_late);
|
||||
};
|
||||
|
||||
} // namespace hook
|
||||
|
||||
@@ -2993,207 +2993,6 @@ DEFINE_HOOK_FUNCTION(
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
|
||||
// these are only used by get_stobject_length below
|
||||
enum parse_error : int32_t {
|
||||
pe_unexpected_end = -1,
|
||||
pe_unknown_type_early = -2, // detected early
|
||||
pe_unknown_type_late = -3, // end of function
|
||||
pe_excessive_nesting = -4,
|
||||
pe_excessive_size = -5
|
||||
};
|
||||
|
||||
// RH NOTE this is a light-weight stobject parsing function for drilling into a
|
||||
// provided serialzied object however it could probably be replaced by an
|
||||
// existing class or routine or set of routines in XRPLD Returns object length
|
||||
// including header bytes (and footer bytes in the event of array or object)
|
||||
// negative indicates error
|
||||
inline int32_t
|
||||
get_stobject_length(
|
||||
unsigned char* start, // in - begin iterator
|
||||
unsigned char* maxptr, // in - end iterator
|
||||
int& type, // out - populated by serialized type code
|
||||
int& field, // out - populated by serialized field code
|
||||
int& payload_start, // out - the start of actual payload data for this type
|
||||
int& payload_length, // out - the length of actual payload data for this
|
||||
// type
|
||||
int recursion_depth = 0) // used internally
|
||||
{
|
||||
if (recursion_depth > 10)
|
||||
return pe_excessive_nesting;
|
||||
|
||||
unsigned char* end = maxptr;
|
||||
unsigned char* upto = start;
|
||||
int high = *upto >> 4;
|
||||
int low = *upto & 0xF;
|
||||
|
||||
upto++;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
if (high > 0 && low > 0)
|
||||
{
|
||||
// common type common field
|
||||
type = high;
|
||||
field = low;
|
||||
}
|
||||
else if (high > 0)
|
||||
{
|
||||
// common type, uncommon field
|
||||
type = high;
|
||||
field = *upto++;
|
||||
}
|
||||
else if (low > 0)
|
||||
{
|
||||
// common field, uncommon type
|
||||
field = low;
|
||||
type = *upto++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// uncommon type and field
|
||||
type = *upto++;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
field = *upto++;
|
||||
}
|
||||
|
||||
DBG_PRINTF(
|
||||
"%d get_st_object found field %d type %d\n",
|
||||
recursion_depth,
|
||||
field,
|
||||
type);
|
||||
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
|
||||
// RH TODO: link this to rippled's internal STObject constants
|
||||
// E.g.:
|
||||
/*
|
||||
int field_code = (safe_cast<int>(type) << 16) | field;
|
||||
auto const& fieldObj = ripple::SField::getField;
|
||||
*/
|
||||
|
||||
if (type < 1 || type > 19 || (type >= 9 && type <= 13))
|
||||
return pe_unknown_type_early;
|
||||
|
||||
bool is_vl = (type == 8 /*ACCID*/ || type == 7 || type == 18 || type == 19);
|
||||
|
||||
int length = -1;
|
||||
if (is_vl)
|
||||
{
|
||||
length = *upto++;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
|
||||
if (length < 193)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (length > 192 && length < 241)
|
||||
{
|
||||
length -= 193;
|
||||
length *= 256;
|
||||
length += *upto++ + 193;
|
||||
if (upto > end)
|
||||
return pe_unexpected_end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int b2 = *upto++;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
length -= 241;
|
||||
length *= 65536;
|
||||
length += 12481 + (b2 * 256) + *upto++;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
}
|
||||
}
|
||||
else if ((type >= 1 && type <= 5) || type == 16 || type == 17)
|
||||
{
|
||||
length =
|
||||
(type == 1
|
||||
? 2
|
||||
: (type == 2
|
||||
? 4
|
||||
: (type == 3
|
||||
? 8
|
||||
: (type == 4
|
||||
? 16
|
||||
: (type == 5
|
||||
? 32
|
||||
: (type == 16
|
||||
? 1
|
||||
: (type == 17 ? 20
|
||||
: -1)))))));
|
||||
}
|
||||
else if (type == 6) /* AMOUNT */
|
||||
{
|
||||
length = (*upto >> 6 == 1) ? 8 : 48;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
}
|
||||
|
||||
if (length > -1)
|
||||
{
|
||||
payload_start = upto - start;
|
||||
payload_length = length;
|
||||
DBG_PRINTF(
|
||||
"%d get_stobject_length field: %d Type: %d VL: %s Len: %d "
|
||||
"Payload_Start: %d Payload_Len: %d\n",
|
||||
recursion_depth,
|
||||
field,
|
||||
type,
|
||||
(is_vl ? "yes" : "no"),
|
||||
length,
|
||||
payload_start,
|
||||
payload_length);
|
||||
return length + (upto - start);
|
||||
}
|
||||
|
||||
if (type == 15 || type == 14) /* Object / Array */
|
||||
{
|
||||
payload_start = upto - start;
|
||||
|
||||
for (int i = 0; i < 1024; ++i)
|
||||
{
|
||||
int subfield = -1, subtype = -1, payload_start_ = -1,
|
||||
payload_length_ = -1;
|
||||
int32_t sublength = get_stobject_length(
|
||||
upto,
|
||||
end,
|
||||
subtype,
|
||||
subfield,
|
||||
payload_start_,
|
||||
payload_length_,
|
||||
recursion_depth + 1);
|
||||
DBG_PRINTF(
|
||||
"%d get_stobject_length i %d %d-%d, upto %d sublength %d\n",
|
||||
recursion_depth,
|
||||
i,
|
||||
subtype,
|
||||
subfield,
|
||||
upto - start,
|
||||
sublength);
|
||||
if (sublength < 0)
|
||||
return pe_unexpected_end;
|
||||
upto += sublength;
|
||||
if (upto >= end)
|
||||
return pe_unexpected_end;
|
||||
|
||||
if ((*upto == 0xE1U && type == 0xEU) ||
|
||||
(*upto == 0xF1U && type == 0xFU))
|
||||
{
|
||||
payload_length = upto - start - payload_start;
|
||||
upto++;
|
||||
return (upto - start);
|
||||
}
|
||||
}
|
||||
return pe_excessive_size;
|
||||
}
|
||||
|
||||
return pe_unknown_type_late;
|
||||
}
|
||||
|
||||
// Given an serialized object in memory locate and return the offset and length
|
||||
// of the payload of a subfield of that object. Arrays are returned fully
|
||||
// formed. If successful returns offset and length joined as int64_t. Use
|
||||
@@ -3211,58 +3010,13 @@ DEFINE_HOOK_FUNCTION(
|
||||
if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
if (read_len < 2)
|
||||
return TOO_SMALL;
|
||||
|
||||
unsigned char* start = (unsigned char*)(memory + read_ptr);
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + read_len;
|
||||
|
||||
DBG_PRINTF(
|
||||
"sto_subfield called, looking for field %u type %u\n",
|
||||
field_id & 0xFFFF,
|
||||
(field_id >> 16));
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF((j == 0 ? " >%02X< " : " %02X "), *(start + j));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
// if ((*upto & 0xF0) == 0xE0)
|
||||
// upto++;
|
||||
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
int32_t length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (length < 0)
|
||||
return PARSE_ERROR;
|
||||
if ((type << 16) + field == field_id)
|
||||
{
|
||||
DBG_PRINTF(
|
||||
"sto_subfield returned for field %u type %u\n",
|
||||
field_id & 0xFFFF,
|
||||
(field_id >> 16));
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF((j == 0 ? " [%02X] " : " %02X "), *(upto + j));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
if (type == 0xF) // we return arrays fully formed
|
||||
return (((int64_t)(upto - start))
|
||||
<< 32) /* start of the object */
|
||||
+ (uint32_t)(length);
|
||||
|
||||
// return pointers to all other objects as payloads
|
||||
return (((int64_t)(upto - start + payload_start))
|
||||
<< 32U) /* start of the object */
|
||||
+ (uint32_t)(payload_length);
|
||||
}
|
||||
upto += length;
|
||||
}
|
||||
|
||||
if (upto != end)
|
||||
return PARSE_ERROR;
|
||||
|
||||
return DOESNT_EXIST;
|
||||
hook::HookAPI api(hookCtx);
|
||||
Bytes data{memory + read_ptr, memory + read_ptr + read_len};
|
||||
auto const result = api.sto_subfield(data, field_id);
|
||||
if (!result)
|
||||
return result.error();
|
||||
auto const& pair = result.value();
|
||||
return (uint64_t(pair.first) << 32U) + (uint32_t)pair.second;
|
||||
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
@@ -3281,56 +3035,13 @@ DEFINE_HOOK_FUNCTION(
|
||||
if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
if (read_len < 2)
|
||||
return TOO_SMALL;
|
||||
|
||||
unsigned char* start = (unsigned char*)(memory + read_ptr);
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + read_len;
|
||||
|
||||
// unwrap the array if it is wrapped,
|
||||
// by removing a byte from the start and end
|
||||
if ((*upto & 0xF0U) == 0xF0U)
|
||||
{
|
||||
upto++;
|
||||
end--;
|
||||
}
|
||||
|
||||
if (upto >= end)
|
||||
return PARSE_ERROR;
|
||||
|
||||
/*
|
||||
DBG_PRINTF("sto_subarray called, looking for index %u\n", index_id);
|
||||
for (int j = -5; j < 5; ++j)
|
||||
printf(( j == 0 ? " >%02X< " : " %02X "), *(start + j));
|
||||
DBG_PRINTF("\n");
|
||||
*/
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
int32_t length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (length < 0)
|
||||
return PARSE_ERROR;
|
||||
|
||||
if (i == index_id)
|
||||
{
|
||||
DBG_PRINTF("sto_subarray returned for index %u\n", index_id);
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF(
|
||||
(j == 0 ? " [%02X] " : " %02X "), *(upto + j + length));
|
||||
DBG_PRINTF("\n");
|
||||
|
||||
return (((int64_t)(upto - start)) << 32U) /* start of the object */
|
||||
+ (int64_t)(length);
|
||||
}
|
||||
upto += length;
|
||||
}
|
||||
|
||||
if (upto != end)
|
||||
return PARSE_ERROR;
|
||||
|
||||
return DOESNT_EXIST;
|
||||
hook::HookAPI api(hookCtx);
|
||||
Bytes data{memory + read_ptr, memory + read_ptr + read_len};
|
||||
auto const result = api.sto_subarray(data, index_id);
|
||||
if (!result)
|
||||
return result.error();
|
||||
auto const& pair = result.value();
|
||||
return (uint64_t(pair.first) << 32U) + (uint32_t)pair.second;
|
||||
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
@@ -3526,92 +3237,26 @@ DEFINE_HOOK_FUNCTION(
|
||||
return MEM_OVERLAP;
|
||||
}
|
||||
|
||||
// we must inject the field at the canonical location....
|
||||
// so find that location
|
||||
unsigned char* start = (unsigned char*)(memory + sread_ptr);
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + sread_len;
|
||||
unsigned char* inject_start = end;
|
||||
unsigned char* inject_end = end;
|
||||
hook::HookAPI api(hookCtx);
|
||||
Bytes source{memory + sread_ptr, memory + sread_ptr + sread_len};
|
||||
std::optional<Bytes> field;
|
||||
if (fread_len > 0 && fread_ptr > 0)
|
||||
field = Bytes{memory + fread_ptr, memory + fread_ptr + fread_len};
|
||||
auto const result = api.sto_emplace(source, field, field_id);
|
||||
if (!result)
|
||||
return result.error();
|
||||
auto const& bytes = result.value();
|
||||
|
||||
DBG_PRINTF(
|
||||
"sto_emplace called, looking for field %u type %u\n",
|
||||
field_id & 0xFFFF,
|
||||
(field_id >> 16));
|
||||
for (int j = -5; j < 5; ++j)
|
||||
DBG_PRINTF((j == 0 ? " >%02X< " : " %02X "), *(start + j));
|
||||
DBG_PRINTF("\n");
|
||||
if (bytes.size() > write_len)
|
||||
return INTERNAL_ERROR;
|
||||
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
int32_t length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (length < 0)
|
||||
return PARSE_ERROR;
|
||||
if ((type << 16) + field == field_id)
|
||||
{
|
||||
inject_start = upto;
|
||||
inject_end = upto + length;
|
||||
break;
|
||||
}
|
||||
else if ((type << 16) + field > field_id)
|
||||
{
|
||||
inject_start = upto;
|
||||
inject_end = upto;
|
||||
break;
|
||||
}
|
||||
upto += length;
|
||||
}
|
||||
|
||||
// if the scan loop ends past the end of the source object
|
||||
// then the source object is invalid/corrupt, so we must
|
||||
// return an error
|
||||
if (upto > end)
|
||||
return PARSE_ERROR;
|
||||
|
||||
// upto is injection point
|
||||
int64_t bytes_written = 0;
|
||||
|
||||
// part 1
|
||||
if (inject_start - start > 0)
|
||||
{
|
||||
WRITE_WASM_MEMORY(
|
||||
bytes_written,
|
||||
write_ptr,
|
||||
write_len,
|
||||
start,
|
||||
(inject_start - start),
|
||||
memory,
|
||||
memory_length);
|
||||
}
|
||||
|
||||
if (fread_len > 0)
|
||||
{
|
||||
// write the field (or don't if it's a delete operation)
|
||||
WRITE_WASM_MEMORY(
|
||||
bytes_written,
|
||||
(write_ptr + bytes_written),
|
||||
(write_len - bytes_written),
|
||||
memory + fread_ptr,
|
||||
fread_len,
|
||||
memory,
|
||||
memory_length);
|
||||
}
|
||||
|
||||
// part 2
|
||||
if (end - inject_end > 0)
|
||||
{
|
||||
WRITE_WASM_MEMORY(
|
||||
bytes_written,
|
||||
(write_ptr + bytes_written),
|
||||
(write_len - bytes_written),
|
||||
inject_end,
|
||||
(end - inject_end),
|
||||
memory,
|
||||
memory_length);
|
||||
}
|
||||
return bytes_written;
|
||||
WRITE_WASM_MEMORY_AND_RETURN(
|
||||
write_ptr,
|
||||
write_len,
|
||||
bytes.data(),
|
||||
bytes.size(),
|
||||
memory,
|
||||
memory_length);
|
||||
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
@@ -3660,24 +3305,12 @@ DEFINE_HOOK_FUNCTION(
|
||||
if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length))
|
||||
return OUT_OF_BOUNDS;
|
||||
|
||||
if (read_len < 2)
|
||||
return TOO_SMALL;
|
||||
|
||||
unsigned char* start = (unsigned char*)(memory + read_ptr);
|
||||
unsigned char* upto = start;
|
||||
unsigned char* end = start + read_len;
|
||||
|
||||
for (int i = 0; i < 1024 && upto < end; ++i)
|
||||
{
|
||||
int type = -1, field = -1, payload_start = -1, payload_length = -1;
|
||||
int32_t length = get_stobject_length(
|
||||
upto, end, type, field, payload_start, payload_length, 0);
|
||||
if (length < 0)
|
||||
return 0;
|
||||
upto += length;
|
||||
}
|
||||
|
||||
return upto == end ? 1 : 0;
|
||||
Bytes data{read_ptr + memory, read_ptr + read_len + memory};
|
||||
hook::HookAPI api(hookCtx);
|
||||
auto const result = api.sto_validate(data);
|
||||
if (!result)
|
||||
return result.error();
|
||||
return result.value() ? 1 : 0;
|
||||
|
||||
HOOK_TEARDOWN();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user