mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
A units copy reads the receiver's UTF-16 units through the pointer its guard took, which stays valid only while nothing indexes another non-ASCII string: that evicts the receiver's index entry and frees the units. The classifier admitted every typed-array and Buffer method, so a loop that sorted a typed array with a comparator function value, or copied an untyped source into one (`ta.set(source)`, whose element reads can run getters), got a units copy, and a comparator or getter that indexed other strings made the loop read freed memory. The units copy now admits only the typed-array and Buffer methods that call no program code and read no checked-dynamic value or string. Corpus 7307 sorts with such a comparator and copies from such a source inside scans of a warmed non-ASCII text; the sanitized build of the previous commit reports a heap-use-after-free on it.
74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
// A units copy must not run program code that indexes other strings: a
|
|
// comparator sort or a copy from an untyped source can evict the scanned
|
|
// text's UTF-16 units while the loop reads them.
|
|
|
|
function bigText(seed: number): string {
|
|
let s = "";
|
|
for (let i = 0; i < 9000; i++) s += String.fromCharCode(0xe9 + ((i * seed) % 7)) + "ab ";
|
|
return s;
|
|
}
|
|
|
|
const others = [bigText(3), bigText(5), bigText(11), bigText(13), bigText(17)];
|
|
|
|
let touched = 0;
|
|
function touchOthers(): void {
|
|
for (let k = 0; k < others.length; k++) {
|
|
const o = others[k];
|
|
for (let j = 0; j < 80; j++) touched += o.charCodeAt((j * 997 + k) % o.length);
|
|
}
|
|
}
|
|
|
|
function spaces(s: string): number {
|
|
let n = 0;
|
|
for (let i = 0; i < s.length; i++) if (s.charCodeAt(i) === 32) n++;
|
|
return n;
|
|
}
|
|
|
|
function sortedWhileScanning(
|
|
s: string,
|
|
values: Float64Array,
|
|
compare: (a: number, b: number) => number,
|
|
): number {
|
|
let sum = 0;
|
|
for (let i = 0; i < s.length; i++) {
|
|
sum += s.charCodeAt(i);
|
|
if (i % 4096 === 100) values.sort(compare);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
function copiedWhileScanning(s: string, values: Float64Array, source: any): number {
|
|
let sum = 0;
|
|
for (let i = 0; i < s.length; i++) {
|
|
sum += s.charCodeAt(i);
|
|
if (i % 4096 === 100) values.set(source);
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
const text = bigText(7);
|
|
// Repeated scans make the runtime hold the text's units.
|
|
let warm = 0;
|
|
for (let r = 0; r < 4; r++) warm += spaces(text);
|
|
console.log("warm", warm);
|
|
|
|
const values = new Float64Array([3, 1, 2]);
|
|
const byValue = (a: number, b: number): number => {
|
|
touchOthers();
|
|
return a - b;
|
|
};
|
|
console.log("sorted", sortedWhileScanning(text, values, byValue), values.join(","));
|
|
for (let r = 0; r < 2; r++) warm += spaces(text);
|
|
|
|
const source: any = {
|
|
length: 3,
|
|
get 0() {
|
|
touchOthers();
|
|
return 9;
|
|
},
|
|
1: 8,
|
|
2: 7,
|
|
};
|
|
console.log("copied", copiedWhileScanning(text, values, source), values.join(","));
|
|
console.log("touched", touched > 0, spaces(text));
|