add sto APIs

This commit is contained in:
tequ
2025-09-29 13:38:05 +09:00
parent e3c4644151
commit 4ff8d688e2
3 changed files with 519 additions and 416 deletions

View File

@@ -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();
}