Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ CMakeFiles
/*.c
/*.h
/*.inc
.vscode

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 6a93c45:

I'll allow this since we already have piles of tool-specific stuff in here but this really ought to go in your .git/info/exclude rather than the project-wide gitignore.

5 changes: 5 additions & 0 deletions C/deserialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ int_fast32_t simplicity_decodeMallocDag(dag_node** dag, simplicity_callback_deco
if (dagLen <= 0) return dagLen;
static_assert(DAG_LEN_MAX <= (uint32_t)INT32_MAX, "DAG_LEN_MAX exceeds supported parsing range.");
if (DAG_LEN_MAX < (uint32_t)dagLen) return SIMPLICITY_ERR_DATA_OUT_OF_RANGE;
/* Every node's encoding is at least 2 bits long ('decodeNode' reads at least 2 bits before any call to 'decodeJet'),
* so a 'stream' too short to hold 'dagLen' nodes is bound to fail with 'SIMPLICITY_ERR_BITSTREAM_EOF'.
* Fail before allocating, so that the size of the allocation is proportional to the length of the 'stream'.
*/
if (stream->len < ((size_t)dagLen * 2 + stream->offset + CHAR_BIT - 1) / CHAR_BIT) return SIMPLICITY_ERR_BITSTREAM_EOF;
static_assert(DAG_LEN_MAX <= SIZE_MAX / sizeof(dag_node), "dag array too large.");
static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero.");
static_assert(DAG_LEN_MAX - 1 <= UINT32_MAX, "dag array index does not fit in uint32_t.");
Expand Down
31 changes: 31 additions & 0 deletions C/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,36 @@ static void test_decodeUptoMaxInt(void) {
}
}

static void test_decodeMallocDag_length(void) {
printf("Test decodeMallocDag length prefix\n");
static const struct {
const char* name;
unsigned char buf[5];
size_t len;
int_fast32_t expected;
} cases[] =
/* A length prefix of 8,000,000 in a 5 byte stream must be rejected before allocating the dag. */
{ { "8000000 nodes in 5 bytes", { 0xf0, 0x6e, 0x84, 0x80, 0x00 }, 5, SIMPLICITY_ERR_BITSTREAM_EOF }
/* A length prefix of 4 needs at least 6 + 2*4 bits, i.e. 2 bytes. */
, { "4 nodes in 1 byte", { 0xc0 }, 1, SIMPLICITY_ERR_BITSTREAM_EOF }
/* With 2 bytes the stream is long enough to start decoding, which then finds the first node referencing a non-existent child. */
, { "4 nodes in 2 bytes", { 0xc0, 0x00 }, 2, SIMPLICITY_ERR_DATA_OUT_OF_RANGE }
};

for (size_t i = 0; i < sizeof(cases)/sizeof(cases[0]); ++i) {
dag_node* dag;
bitstream stream = initializeBitstream(cases[i].buf, cases[i].len);
int_fast32_t result = simplicity_decodeMallocDag(&dag, simplicity_elements_decodeJet, NULL, &stream);
if (cases[i].expected == result && NULL == dag) {
successes++;
} else {
failures++;
printf("Unexpected result for %s. Expected %d and received %d\n", cases[i].name, (int)cases[i].expected, (int)result);
}
simplicity_free(dag);
}
}

static void test_hashBlock(void) {
printf("Test hashBlock\n");
dag_node* dag;
Expand Down Expand Up @@ -745,6 +775,7 @@ int main(int argc, char **argv) {
timing_flag = 0;
}
test_decodeUptoMaxInt();
test_decodeMallocDag_length();
test_hashBlock();
test_occursCheck();

Expand Down
Loading