mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Keep a spread tuple alive until all its fields are packed into the rest
A spread tuple's first field carries the statements that evaluate the tuple into a hidden local, and its later fields read that local. When lowerArraySpreadItems stored the first field as one item's expression, the local's frame ended with that store, so a tuple returned by a call was released before its later fields were read (a use-after-free under SCRIPTC_SAN=1). The field's statements now join the packing sequence at the item's position, whose frame lasts until every item is stored.
This commit is contained in:
@@ -174,7 +174,15 @@ export function lowerArraySpreadItems(
|
||||
];
|
||||
for (const node of nodes) {
|
||||
if (!(node instanceof AstNode)) {
|
||||
const value = undefinedForNullish(lowerer, node, arrType);
|
||||
// A spread tuple's first field declares the tuple's storage, which
|
||||
// the later fields read: its statements join this sequence, whose
|
||||
// frame lasts until every item is stored.
|
||||
let item = node;
|
||||
if (item.kind === "seqExpr") {
|
||||
body.push(...item.stmts);
|
||||
item = item.result;
|
||||
}
|
||||
const value = undefinedForNullish(lowerer, item, arrType);
|
||||
const stored =
|
||||
lowerer.runtimeOptionalWidening(value.type, elem) !== null ||
|
||||
optionalElementValue(lowerer, value, elem)
|
||||
|
||||
@@ -58,6 +58,14 @@ const mixedChanging: [boolean, boolean | undefined] = [true, undefined];
|
||||
console.log(flags(...mixedChanging, ...mixedMore, flip(mixedChanging, true), ...mixedChanging));
|
||||
const mixedWords: [string, string | undefined] = ["a", undefined];
|
||||
console.log(label("z", ...["b", undefined, "c"], ...mixedWords), label("y", ...mixedWords, ...new Set(["s", undefined])));
|
||||
// A tuple returned by a call keeps its string fields alive until every
|
||||
// field is packed beside the other spreads.
|
||||
function makeTuple(n: number): [string, string | undefined, string] {
|
||||
return ["p" + n.toString(16).repeat(4), n % 2 ? undefined : "q" + n, "r".repeat((n % 7) + 3) + n];
|
||||
}
|
||||
for (let i = 0; i < 3; i++) {
|
||||
console.log(label("z", ...["m", undefined], ...makeTuple(i), ...mixedWords), label("y", ...makeTuple(i + 10), ...new Set(["s" + i])));
|
||||
}
|
||||
|
||||
function pick(start: (number | undefined)[] | string[], other: (number | undefined)[] | string[]): string {
|
||||
let a = start;
|
||||
|
||||
Reference in New Issue
Block a user