"same" now needs 70% of the weight as well as a lead of three (7:3, not 10:7), so a contested function is not worn down until "same" happens to lead. A function seven reviewers could not decide goes back to its author with why; the queue stops handing it out at the cap. Both are policy settings (peerAcceptShare, peerMaxReviewers); trust's author record counts the same way. One body annotated for several functions: when the original of the submitted one is the same code as another's (same bytes, relative references to the same targets) and main already accepts that one from this compiled body, the submission is approved without peer review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
166 lines
7.3 KiB
JavaScript
166 lines
7.3 KiB
JavaScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { fixture, FN_ADDR, CALLEE_ADDR, GLOBAL_ADDR } from './helpers.js';
|
|
import { PEImage } from '../src/shared/pe.js';
|
|
import { CoffObject } from '../src/shared/coff.js';
|
|
import { parseListing } from '../src/shared/listing.js';
|
|
import { parseAnnotations } from '../src/shared/annotations.js';
|
|
import { compareObject, STATUS, lcsRatio, sameOriginal } from '../src/shared/compare.js';
|
|
import { reconcile } from '../src/shared/reconcile.js';
|
|
|
|
const FILE = 'src/player.cpp';
|
|
|
|
function symbols(extra = []) {
|
|
return new Map(
|
|
[
|
|
{ id: 'fn_00001', kind: 'function', addr: FN_ADDR, size: 15 },
|
|
{ id: 'fn_00002', kind: 'function', addr: CALLEE_ADDR, size: 1 },
|
|
{ id: 'gl_00001', kind: 'global', addr: GLOBAL_ADDR, size: 4 },
|
|
...extra,
|
|
].map((s) => [s.id, s]),
|
|
);
|
|
}
|
|
|
|
function run({ objPatch, nameIndex = new Map(), syms = symbols() } = {}) {
|
|
const f = fixture({ objPatch });
|
|
const { annotations } = parseAnnotations(f.source, FILE);
|
|
const result = compareObject({
|
|
obj: new CoffObject(f.obj),
|
|
listing: parseListing(f.listing),
|
|
file: FILE,
|
|
annotations,
|
|
symbols: syms,
|
|
nameIndex,
|
|
pe: new PEImage(f.pe),
|
|
});
|
|
return { result, fn: result.functions[0] };
|
|
}
|
|
|
|
test('PE reader maps addresses and base relocations', () => {
|
|
const pe = new PEImage(fixture().pe);
|
|
assert.equal(pe.imageBase, 0x400000);
|
|
assert.equal(pe.read(FN_ADDR, 3).toString('hex'), '558bec');
|
|
assert.ok(pe.baseRelocations().has(FN_ADDR + 9));
|
|
assert.ok(pe.isCode(FN_ADDR));
|
|
assert.ok(!pe.isCode(GLOBAL_ADDR));
|
|
});
|
|
|
|
test('COFF reader extracts functions and relocations', () => {
|
|
const fns = new CoffObject(fixture().obj).functions();
|
|
assert.equal(fns.length, 1);
|
|
assert.equal(fns[0].name, '?Update@Player@@QAEXXZ');
|
|
assert.equal(fns[0].bytes.length, 15);
|
|
assert.deepEqual(fns[0].relocs.map((r) => [r.offset, r.symbol.name]), [[4, '?Callee@@YAXXZ'], [9, '?g_val@@3HA']]);
|
|
});
|
|
|
|
test('exact match when every referenced symbol is known', () => {
|
|
const { fn } = run({ nameIndex: new Map([['?Callee@@YAXXZ', 'fn_00002'], ['?g_val@@3HA', 'gl_00001']]) });
|
|
assert.equal(fn.status, STATUS.EXACT);
|
|
assert.equal(fn.score, 1);
|
|
assert.equal(fn.symbol, '?Update@Player@@QAEXXZ');
|
|
assert.equal(fn.demangled, 'Player::Update');
|
|
});
|
|
|
|
test('unknown references are resolved through the symbol database addresses', () => {
|
|
const { result, fn } = run();
|
|
assert.equal(fn.status, STATUS.UNRESOLVED);
|
|
const check = { units: [result], annotationErrors: [] };
|
|
const rec = reconcile(check, symbols());
|
|
assert.equal(result.functions[0].status, STATUS.EXACT);
|
|
assert.deepEqual(rec.learned.map((l) => [l.decorated, l.id]).sort(), [['?Callee@@YAXXZ', 'fn_00002'], ['?Update@Player@@QAEXXZ', 'fn_00001'], ['?g_val@@3HA', 'gl_00001']]);
|
|
assert.deepEqual(rec.conflicts, []);
|
|
});
|
|
|
|
test('a reference to data with no symbol stays unresolved and is reported', () => {
|
|
const syms = symbols();
|
|
syms.delete('gl_00001');
|
|
const { result } = run({ syms });
|
|
const rec = reconcile({ units: [result], annotationErrors: [] }, syms);
|
|
assert.equal(result.functions[0].status, STATUS.UNRESOLVED);
|
|
assert.deepEqual(rec.unknown, [{ decorated: '?g_val@@3HA', addr: GLOBAL_ADDR }]);
|
|
});
|
|
|
|
test('calling the wrong function is a mismatch', () => {
|
|
const syms = symbols([{ id: 'fn_00003', kind: 'function', addr: 0x401200, size: 1, decorated: '?Callee@@YAXXZ' }]);
|
|
const { fn } = run({ syms, nameIndex: new Map([['?Callee@@YAXXZ', 'fn_00003']]) });
|
|
assert.equal(fn.status, STATUS.MISMATCH);
|
|
assert.match(fn.relocations[0].reason, /fn_00003/);
|
|
});
|
|
|
|
test('reconcile flags a decorated name that is already bound elsewhere', () => {
|
|
const syms = symbols([{ id: 'fn_00003', kind: 'function', addr: 0x401200, size: 1, decorated: '?Callee@@YAXXZ' }]);
|
|
const { result } = run({ syms });
|
|
const rec = reconcile({ units: [result], annotationErrors: [] }, syms);
|
|
assert.equal(rec.conflicts.length, 1);
|
|
assert.equal(result.functions[0].status, STATUS.MISMATCH);
|
|
});
|
|
|
|
test('byte differences are reported with the instruction at that offset', () => {
|
|
const { fn } = run({ objPatch: (b) => (b[13] = 0x90) });
|
|
assert.equal(fn.status, STATUS.MISMATCH);
|
|
assert.equal(fn.diffRegions.length, 1);
|
|
assert.equal(fn.diffRegions[0].offset, 13);
|
|
assert.equal(fn.diffRegions[0].ours, '90');
|
|
assert.equal(fn.diffRegions[0].original, '5d');
|
|
assert.match(fn.diffRegions[0].ourInstruction, /pop ebp/);
|
|
assert.equal(fn.diffRegions[0].sourceLine, 8);
|
|
assert.ok(fn.score > 0.9 && fn.score < 1);
|
|
});
|
|
|
|
test('size mismatches fall back to a similarity score', () => {
|
|
const syms = symbols();
|
|
syms.get('fn_00001').size = 20;
|
|
const { fn } = run({ syms });
|
|
assert.equal(fn.status, STATUS.MISMATCH);
|
|
assert.ok(fn.score > 0.5 && fn.score < 1);
|
|
});
|
|
|
|
test('annotation with an ID that has no address in this version', () => {
|
|
const syms = symbols();
|
|
syms.get('fn_00001').addr = null;
|
|
const { fn } = run({ syms });
|
|
assert.equal(fn.status, STATUS.NO_ADDRESS);
|
|
});
|
|
|
|
test('lcsRatio', () => {
|
|
assert.equal(lcsRatio(Buffer.from('abcd'), Buffer.from('abcd')), 1);
|
|
assert.equal(lcsRatio(Buffer.from('abcd'), Buffer.from('abxd')), 0.75);
|
|
assert.equal(lcsRatio(Buffer.alloc(0), Buffer.alloc(0)), 1);
|
|
});
|
|
|
|
// Every unit's s_ScriptFunctions is its own table: a file-local static is known by object and name.
|
|
test('a file-local static resolves by its unit, never by the bare name', () => {
|
|
const f = fixture();
|
|
const { annotations } = parseAnnotations(f.source, FILE);
|
|
const obj = new CoffObject(f.obj);
|
|
const real = obj.dataSymbols.bind(obj);
|
|
obj.dataSymbols = () => [...real(), { name: '?g_val@@3HA', section: 2, storageClass: 3 }];
|
|
const syms = symbols([{ id: 'gl_09999', kind: 'global', addr: 0x405000, size: 4, decorated: '?g_val@@3HA' }]);
|
|
const compare = (nameIndex) =>
|
|
compareObject({ obj, listing: parseListing(f.listing), file: FILE, annotations, symbols: syms, nameIndex, pe: new PEImage(f.pe) }).functions[0];
|
|
// Another unit's static of the same name (at another address) does not make this one wrong.
|
|
const other = compare(new Map([['?Callee@@YAXXZ', 'fn_00002'], ['?g_val@@3HA', 'gl_09999']]));
|
|
assert.notEqual(other.status, STATUS.MISMATCH);
|
|
const own = compare(new Map([['?Callee@@YAXXZ', 'fn_00002'], ['?g_val@@3HA', 'gl_09999'], ['src/player.obj|?g_val@@3HA', 'gl_00001']]));
|
|
assert.equal(own.status, STATUS.EXACT);
|
|
});
|
|
|
|
test('sameOriginal: two copies of a function match when their calls reach the same target', () => {
|
|
// Two 12-byte copies at 0x1000 and 0x1100: mov ecx, 1; call 0x2000; ret; nop; nop.
|
|
const image = Buffer.alloc(0x200, 0xcc);
|
|
const copy = (at, target) => {
|
|
const off = at - 0x1000;
|
|
image.set([0xb9, 1, 0, 0, 0, 0xe8], off);
|
|
image.writeInt32LE(target - (at + 10), off + 6);
|
|
image.set([0xc3, 0x90], off + 10);
|
|
};
|
|
copy(0x1000, 0x2000);
|
|
copy(0x1100, 0x2000);
|
|
copy(0x1180, 0x2040);
|
|
const pe = { read: (va, len) => image.subarray(va - 0x1000, va - 0x1000 + len) };
|
|
assert.equal(sameOriginal(pe, 0x1000, 0x1100, 12), true, 'same call target from two places');
|
|
assert.equal(sameOriginal(pe, 0x1000, 0x1180, 12), false, 'calls another function');
|
|
image[0x101] = 2; // mov ecx, 2
|
|
assert.equal(sameOriginal(pe, 0x1000, 0x1100, 12), false, 'an immediate differs');
|
|
assert.equal(sameOriginal(pe, 0x1000, 0x1100, 0), false, 'no size, no claim');
|
|
});
|