Skip to content

Commit 91bef77

Browse files
committed
fix: prevent parse_bytecode_pattern error on large OP_PUSHDATA_4 lengths
1 parent cb23814 commit 91bef77

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-- Could not auto-generate a down migration.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
-- This implementation resolves an error when parsing large OP_PUSHDATA_4 pushes
2+
CREATE OR REPLACE FUNCTION parse_bytecode_pattern(bytecode bytea) RETURNS bytea
3+
LANGUAGE plpgsql IMMUTABLE
4+
AS $$
5+
DECLARE
6+
pattern bytea := '\x'::bytea;
7+
selected_byte integer;
8+
scratch bytea;
9+
i integer := 0;
10+
bytecode_length integer := octet_length(bytecode);
11+
BEGIN
12+
WHILE i < bytecode_length LOOP
13+
selected_byte := get_byte(bytecode, i);
14+
pattern := pattern || substring(bytecode from (i + 1) for 1);
15+
IF selected_byte > 78 OR selected_byte = 0 THEN
16+
-- OP_0 (0) and all opcodes after OP_PUSHDATA_4 (78) are single-byte instructions
17+
i := i + 1;
18+
ELSIF selected_byte > 0 AND selected_byte <= 75 THEN
19+
-- OP_PUSHBYTES_1 (1) through OP_PUSHBYTES_75 (75) directly indicate the length of pushed data
20+
i := i + 1 + selected_byte;
21+
ELSIF selected_byte = 76 THEN
22+
IF bytecode_length - i < 2 THEN
23+
-- malformed, return immediately
24+
RETURN pattern;
25+
END IF;
26+
-- OP_PUSHDATA_1 reads one length-byte
27+
i := i + 2 + get_byte(bytecode, (i + 1));
28+
ELSIF selected_byte = 77 THEN
29+
IF bytecode_length - i < 3 THEN
30+
-- malformed, return immediately
31+
RETURN pattern;
32+
END IF;
33+
-- OP_PUSHDATA_2 reads two length-bytes
34+
scratch := substring(bytecode from (i + 2) for 2);
35+
-- parse scratch as unsigned, two byte, little-endian number:
36+
i := i + 3 + ((get_byte(scratch, 1) << 8) | get_byte(scratch, 0));
37+
ELSIF selected_byte = 78 THEN
38+
IF bytecode_length - i < 5 THEN
39+
-- malformed, return immediately
40+
RETURN pattern;
41+
END IF;
42+
-- OP_PUSHDATA_4 reads four length-bytes
43+
scratch := substring(bytecode from (i + 2) for 4);
44+
IF get_byte(scratch, 3) << 24 < 0 THEN
45+
-- push length exceeds maximum signed int (>2GB), return immediately
46+
RETURN pattern;
47+
END IF;
48+
-- parse scratch as unsigned, four byte, little-endian number:
49+
i := i + 5 + ((get_byte(scratch, 3) << 24) | (get_byte(scratch, 2) << 16) | (get_byte(scratch, 1) << 8) | get_byte(scratch, 0));
50+
END IF;
51+
END LOOP;
52+
RETURN pattern;
53+
END;
54+
$$;

src/e2e/e2e.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const migration = (path: string) =>
6262
const dbUpMigrationPaths = [
6363
migration('default/1616195337538_init/up.sql'),
6464
migration('default/1673124945608_tokens/up.sql'),
65+
migration('default/1676794104752_parse_bytecode_pattern/up.sql'),
6566
];
6667

6768
const chaingraphInternalApiPort = '3201';

0 commit comments

Comments
 (0)