Skip to content

Commit d09bc9e

Browse files
committed
c: use proper type coercion in consume
On 32bit platforms `size_t` is essentially `uint32_t` (or at times even meager `uint16_t`). Loading `uint64_t` field value into `size_t` on these platforms would truncate the high bits and leave only the low 32 (16) bits in place. This leads to various interesting errors in downstream modules. See: - nodejs/llhttp#110 - nodejs/undici#803
1 parent 888a0ce commit d09bc9e

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

src/implementation/c/node/consume.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,31 @@ export class Consume extends Node<frontend.node.Consume> {
88
const ctx = this.compilation;
99

1010
const index = ctx.stateField(this.ref.field);
11+
const ty = ctx.getFieldType(this.ref.field);
12+
13+
let fieldTy: string;
14+
if (ty === 'i64') {
15+
fieldTy = 'uint64_t';
16+
} else if (ty === 'i32') {
17+
fieldTy = 'uint32_t';
18+
} else if (ty === 'i16') {
19+
fieldTy = 'uint16_t';
20+
} else if (ty === 'i8') {
21+
fieldTy = 'uint8_t';
22+
} else {
23+
throw new Error(
24+
`Unsupported type ${ty} of field ${this.ref.field} for consume node`);
25+
}
1126

1227
out.push('size_t avail;');
13-
out.push('size_t need;');
28+
out.push(`${fieldTy} need;`);
29+
1430
out.push('');
1531
out.push(`avail = ${ctx.endPosArg()} - ${ctx.posArg()};`);
1632
out.push(`need = ${index};`);
33+
34+
// Note: `avail` or `need` are going to coerced to the largest
35+
// datatype needed to hold either of the values.
1736
out.push('if (avail >= need) {');
1837
out.push(` p += need;`);
1938
out.push(` ${index} = 0;`);

test/consume-test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,24 @@ describe('llparse/consume', () => {
4646
const binary = await build(p, start, 'consume-i64');
4747
await binary.check('3aaa2bb1a01b', 'off=4\noff=7\noff=9\noff=10\noff=12\n');
4848
});
49+
50+
it('should consume bytes with untruncated i64 field', async () => {
51+
p.property('i64', 'to_consume');
52+
53+
const start = p.node('start');
54+
const consume = p.consume('to_consume');
55+
56+
start
57+
.select(
58+
NUM_SELECT,
59+
p.invoke(p.code.mulAdd('to_consume', { base: 10 }), start)
60+
)
61+
.skipTo(consume);
62+
63+
consume
64+
.otherwise(printOff(p, start));
65+
66+
const binary = await build(p, start, 'consume-untruncated-i64');
67+
await binary.check('4294967297.xxxxxxxx', '\n');
68+
});
4969
});

0 commit comments

Comments
 (0)