fix(python): pin only the 3.14 and 3.15 magics checked locally

Keep magic 3627 (CPython 3.14.6) and 3666 (CPython 3.15.0b3), whose code
layout and inline-cache tables match those interpreters' opcode tables and
whose decoded code objects match marshal.loads across a stdlib sample. Leave
the 3.15.0a8 magic 3663 unpinned, since no 3.15.0a8 interpreter was available
to check it, and regenerate the 3.15 slice fixture with 3.15.0b3.

Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
gensokyo-devandgensokyo-dev authored and Ysopwnable committed 2026-11-14 13:37:55 +01:00
1 parent 1d08253ff2
commit 9d3eb083dd
7 files changed
+108 -148

No files matched your search

+4 -5
View File
@@ -81,11 +81,10 @@ The inline Evidence result contains the artifact path, SHA-256, and byte length.
- `magic`, the 16-bit little-endian field;
- `python_version` and `code_layout`, mapped from the pinned CPython marshal
table (`py37`, `py38`, or `py311`): 3.7–3.13, plus three 3.14 and 3.15
magics: 3627 (3.14.4) and 3663 (3.15.0a8), both read back from real
interpreters, and 3666 (3.15.2), checked with real 3.15.2 release bytes
decoded by the 3.15.0a8 interpreter (3.15.2 itself was not run). Other 3.14
and 3.15 pre-release magics are not pinned and are unsupported. 3.11 alpha
table (`py37`, `py38`, or `py311`): 3.7–3.13, plus the 3.14 magic 3627 and
the 3.15 magic 3666, each checked against a real interpreter (CPython 3.14.6
and 3.15.0b3). Other 3.14 and 3.15 pre-release magics are not pinned and are
unsupported. 3.11 alpha
magics before 3494 used other code layouts and are unsupported. The
3.7.0a1/a2 magics 3390/3391 used 12-byte headers and are outside this
reader's supported format;
+1 -2
View File
@@ -27,8 +27,7 @@ const PYTHON_BYTECODE_IDENTITIES: ReadonlyMap<number, PythonBytecodeIdentity> =
...identities(range(3550, 3571), "3.13"),
...identities([3627], "3.14"),
// Reported against Python 3.15, the default `python3` on current Homebrew.
// 3663 is the 3.15.0a8 magic, read back from a real interpreter.
...identities([3663, 3666], "3.15"),
...identities([3666], "3.15"),
]);
export const pythonBytecodeIdentity = (
+1 -6
View File
@@ -88,15 +88,10 @@ it("keeps pinned CPython identity separate from standalone marshal coverage", ()
pythonVersion: "3.15",
headerBytes: 16,
});
expect(pythonBytecodeIdentity(3663)).toEqual({
pythonVersion: "3.15",
headerBytes: 16,
});
expect(pythonPycMarshalProfile(3663)?.code_layout).toBe("py311");
expect(pythonPycMarshalProfile(3666)?.code_layout).toBe("py311");
// Only magics checked against real CPython are pinned: the neighbouring
// 3.14 and 3.15 pre-release magics and the 3.16 magics stay unknown.
for (const magic of [3600, 3626, 3650, 3662, 3664, 3665, 3706]) {
for (const magic of [3600, 3626, 3650, 3662, 3663, 3664, 3665, 3706]) {
expect(pythonBytecodeIdentity(magic), String(magic)).toBeNull();
expect(pythonPycMarshalProfile(magic), String(magic)).toBeNull();
}
+3 -5
View File
@@ -63,11 +63,9 @@ export const PYTHON_PYC_MARSHAL_PROFILES: ReadonlyMap<
...consecutiveProfiles(3550, 3571, "py311"),
// 3.14 and 3.15 keep the 3.11 code layout; 3.14a1 magic 3608 adds slice
// constants to marshal, which the reader gates by magic. Only magics checked
// against real CPython are pinned: 3627 (3.14.4) and 3663 (3.15.0a8) were
// read back from those interpreters; 3666 (3.15.2) was checked with real
// 3.15.2 release bytes decoded by the 3.15.0a8 interpreter. Other
// pre-release magics stay unpinned.
...profiles([3627, 3663, 3666], "py311"),
// against a real interpreter are pinned: 3627 (CPython 3.14.6) and 3666
// (CPython 3.15.0b3). Other pre-release magics stay unpinned.
...profiles([3627, 3666], "py311"),
]);
export const pythonPycMarshalProfile = (
+4 -36
View File
@@ -40,16 +40,10 @@ const inlineCacheProfiles = (
* profile only where their source changes do not alter cache widths or IDs.
* Final 3.13 magic 3571 matches the opcode IDs and cache widths in CPython
* v3.13.17's Lib/_opcode_metadata.py and Lib/opcode.py.
* The 3.14 and 3.15 tables are pinned only to magics checked against real
* CPython: 3627 (final 3.14, matches v3.14.4) and 3663 (3.15.0a8) were read
* back from those builds; 3666 (final 3.15, matches v3.15.0) was checked with
* real 3.15.2 release bytes, not by running 3.15.2. Each is the generated
* _PyOpcode_Caches and opmap from CPython's Include/internal/pycore_magic_number.h
* history at the last cache-changing bump commit at or before it:
* 60202609a2c2d0971aadfa4729ba30b50e89c6ea (3623, shared by 3624 to 3627),
* 879c85f6e4ca8424ff6b4fddfbc946889572bf13 (3663) and
* 600f4dbd54bb35f56ecf1430134d2072b0520aa3 (3665, shared by 3666). Other 3.14
* and 3.15 pre-release magics have no table here and are not decoded.
* The 3.14 and 3.15 tables are pinned only to magics checked against a real
* interpreter: 3627 equals `opcode._inline_cache_entries` and `opcode.opmap`
* in CPython 3.14.6, and 3666 equals them in CPython 3.15.0b3. Other 3.14 and
* 3.15 pre-release magics have no table here and are not decoded.
*/
const INLINE_CACHE_ENTRIES_BY_MAGIC: ReadonlyMap<
number,
@@ -450,32 +444,6 @@ const INLINE_CACHE_ENTRIES_BY_MAGIC: ReadonlyMap<
[119, 1],
],
),
...inlineCacheProfiles(
[3663],
[
[4, 1],
[36, 1],
[37, 3],
[42, 5],
[50, 3],
[53, 3],
[54, 1],
[55, 1],
[68, 1],
[74, 1],
[79, 9],
[91, 4],
[95, 1],
[99, 1],
[100, 1],
[101, 1],
[102, 1],
[105, 1],
[109, 4],
[118, 1],
[128, 1],
],
),
...inlineCacheProfiles(
[3666],
[
+2 -2
View File
@@ -241,8 +241,8 @@ export const parsePythonPyc = (
}`;
return unsupported(
identity === null
? `Pyc magic ${magic} is not a pinned CPython identity, so its header cannot be qualified; standalone marshal decoding is pinned for CPython 3.7–3.13 and the 3.14 and 3.15 magics 3627, 3663 and 3666.`
: `CPython ${identity.pythonVersion} header is recognized (magic ${magic}, ${headerFacts}), but no marshal code layout is pinned for that version, so its code objects cannot be decoded. Standalone marshal decoding is pinned for CPython 3.7–3.13 and the 3.14 and 3.15 magics 3627, 3663 and 3666.`,
? `Pyc magic ${magic} is not a pinned CPython identity, so its header cannot be qualified; standalone marshal decoding is pinned for CPython 3.7–3.13 and the 3.14 and 3.15 magics 3627 and 3666.`
: `CPython ${identity.pythonVersion} header is recognized (magic ${magic}, ${headerFacts}), but no marshal code layout is pinned for that version, so its code objects cannot be decoded. Standalone marshal decoding is pinned for CPython 3.7–3.13 and the 3.14 and 3.15 magics 3627 and 3666.`,
);
}
const flags = bytes.readUInt32LE(4);
+93 -92
View File
@@ -148,19 +148,29 @@ cliTest.for([
version: "3.14.4",
pythonVersion: "3.14",
magic: 3627,
moduleConsts: [
{ kind: "code", name: "f", firstlineno: 1 },
{ kind: "none" },
{ kind: "frozenset", length: 2 },
],
encoded:
"Kw4NCgAAAAAAAAAAHwAAAOMAAAAAAAAAAAAAAAABAAAAAAAAAPMMAAAAgABSABcAdABSASMAKQNjAQAAAAAAAAAAAAAAAgAAAAMAAADzFAAAAIAAVgBSACwaAAAAAAAAAAAAACMAKQE66QEAAADpCgAAAOkCAAAAqQApAdoBYXMBAAAAJtoKZml4dHVyZS5wedoBZnIJAAAAAQAAAHMMAAAAgADYCwyIVo050AQU8wAAAABOPgIAAADjAgAAAAAAAAAAAAAAAgAAAAMAAADzHgAAAIAAVgBQAAAAAAAAAAAAAAAAAAAAAAAAAFYAMwIjAKkBTqkB2gF4qQJyBwAAANoBYnMCAAAAJiZyCAAAANoIPGxhbWJkYT5yEgAAAAEAAABzDAAAAIAAsAG3A7EDsFGpeHIKAAAA4wIAAAAAAAAAAAAAAAIAAAADAAAA8x4AAACAAFYAUAAAAAAAAAAAAAAAAAAAAAAAAABWATMCIwByDQAAAHIOAAAAchAAAABzAgAAACYmcggAAAByEgAAAHISAAAAAQAAAHMMAAAAgACQcZdzkXOYQZFocgoAAAApAXIJAAAAcgYAAAByCgAAAHIIAAAA2gg8bW9kdWxlPnIVAAAAAQAAAHMKAAAA8AMBAQH0AgEBFXIKAAAA",
},
{
version: "3.15.0a8",
version: "3.15.0b3",
pythonVersion: "3.15",
magic: 3663,
magic: 3666,
// 3.15 loads None through LOAD_COMMON_CONSTANT, so it is not a constant.
moduleConsts: [
{ kind: "code", name: "f", firstlineno: 1 },
{ kind: "frozenset", length: 2 },
],
encoded:
"Tw4NCgAAAAAAAAAAHwAAAOMAAAAAAAAAAAAAAAABAAAAAAAAAPMOAAAAgAAAAFEAFQBzAFEBIQApA2MBAAAAAAAAAAAAAAACAAAAAwAAAPMWAAAAgAAAAFUAUQAqGgAAAAAAAAAAAAAhACkBOukBAAAA6QoAAADpAgAAAKkAKQHaAWFzAQAAACbaCmZpeHR1cmUucHnaAWZyCQAAAAEAAABzDAAAAIEA2AsMiFaNOdAEFPMAAAAATj4CAAAA4wIAAAAAAAAAAAAAAAIAAAADAAAA8yAAAACAAAAAVQBPAAAAAAAAAAAAAAAAAAAAAAAAAFUAMQIhAKkBTqkB2gF4qQJyBwAAANoBYnMCAAAAJiZyCAAAANoIPGxhbWJkYT5yEgAAAAEAAABzDAAAAIEAsAG3A7EDsFGpeHIKAAAA4wIAAAAAAAAAAAAAAAIAAAADAAAA8yAAAACAAAAAVQBPAAAAAAAAAAAAAAAAAAAAAAAAAFUBMQIhAHINAAAAcg4AAAByEAAAAHMCAAAAJiZyCAAAAHISAAAAchIAAAABAAAAcwwAAACBAJBxl3ORc5hBkWhyCgAAACkBcgkAAAByBgAAAHIKAAAAcggAAADaCDxtb2R1bGU+chUAAAABAAAAcwoAAADxAwEBAfQCAQEVcgoAAAA=",
"Ug4NCgAAAAAAAAAAHwAAAOMAAAAAAAAAAAAAAAABAAAAAAAAAPMOAAAAgAAAAFEAFQBzAFAHIQApAmMBAAAAAAAAAAAAAAACAAAAAwAAAPMWAAAAgAAAAFUAUQAqGgAAAAAAAAAAAAAhACkBOukBAAAA6QoAAADpAgAAAKkAKQHaAWFzAQAAACbaCmZpeHR1cmUucHnaAWZyCQAAAAEAAABzDAAAAIEA2AsMiFaNOdAEFPMAAAAAPgIAAADjAgAAAAAAAAAAAAAAAgAAAAMAAADzIAAAAIAAAABVAE8AAAAAAAAAAAAAAAAAAAAAAAAAVQAxAiEAKQFOKQHaAXgpAnIHAAAA2gFicwIAAAAmJnIIAAAA2gg8bGFtYmRhPnIPAAAAAQAAAHMMAAAAgQCIYY9jiWOQMYlYcgoAAADjAgAAAAAAAAAAAAAAAgAAAAMAAADzIAAAAIAAAABVAE8AAAAAAAAAAAAAAAAAAAAAAAAAVQExAiEAKQFOKQFyDQAAACkCcgcAAAByDgAAAHMCAAAAJiZyCAAAAHIPAAAAcg8AAAABAAAAcwwAAACBAIhhj2OJY5AxiVhyCgAAACkBcgkAAAByBgAAAHIKAAAAcggAAADaCDxtb2R1bGU+chIAAAABAAAAcwoAAADxAwEBAfQCAQEVcgoAAAA=",
},
])(
"reads CPython $version output with a slice constant and distinct lambdas",
async ({ version, pythonVersion, magic, encoded }, { cli }) => {
async ({ version, pythonVersion, magic, moduleConsts, encoded }, { cli }) => {
// CPython marshal output of `def f(a): return a[1:10:2]` plus a frozenset
// of two same-line lambdas, `(a.x, b)` and `(a.x, a)`, whose bytecode
// differs only in the LOAD_FAST_BORROW operand after LOAD_ATTR's caches.
@@ -180,11 +190,7 @@ cliTest.for([
});
expect(evidence.normalized_result.code_objects).toHaveLength(4);
expect(evidence.normalized_result.code_objects[0]).toMatchObject({
consts: [
{ kind: "code", name: "f", firstlineno: 1 },
{ kind: "none" },
{ kind: "frozenset", length: 2 },
],
consts: moduleConsts,
nested_code: [1, 2, 3],
});
expect(evidence.normalized_result.code_objects[1]).toMatchObject({
@@ -196,88 +202,84 @@ cliTest.for([
},
);
cliTest(
"reads the final CPython 3.15.2 magic 3666 from a real release pyc",
async ({ cli }) => {
// `encodings/__pycache__/utf_8.cpython-315.pyc`, byte for byte as shipped in
// python-build-standalone cpython-3.15.2+20261026-aarch64-apple-darwin
// (install_only tarball, SHA-256 285a6b92...e64a6): the header carries magic 3666.
// The expected rows are what a CPython 3.15.0a8 interpreter's own
// `marshal.loads` reports for these bytes (qualname, first line, SHA-256 of
// `co_code`), so they are not derived from this reader. a8 and the final
// release share the 3.11 code layout; 3.15.2 itself was not executed.
const pyc = Buffer.from(
"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",
"base64",
);
const root = await createTestTempDirectory("rea-python-pyc-final-315-");
const path = join(root, "utf_8.cpython-315.pyc");
await writeFile(path, pyc);
const result = await cli.run({
arguments: ["inspect-python-pyc", path, "--json"],
});
expect(result.exitCode, result.stderr).toBe(0);
const evidence = completePycEvidenceSchema.parse(result.json);
expect(evidence.normalized_result.header).toMatchObject({
magic: 3666,
python_version: "3.15",
code_layout: "py311",
flags: 0,
hash_based: false,
});
expect(
evidence.normalized_result.code_objects.map((code) => [
code.qualname,
code.firstlineno,
code.bytecode.sha256,
]),
).toEqual([
[
"<module>",
1,
"dec009b8d182273295bb1af2572eaf510c5a2c9b79c9ed5b5867acd3ab3f3532",
],
[
"decode",
15,
"fe1a50cda8d8510aee7c6c8a4300c4fb151a360ba9e830b7ac9bd797d4b522f6",
],
[
"IncrementalEncoder",
18,
"32afcda5f0c08dccd01a0e42c4a2b5575247c8ed5723bc704db2dd995511c73b",
],
[
"IncrementalEncoder.encode",
19,
"df5053d342e0600648ddbd48ec64a5f1c4336c6956cde360b8799c61da5d93fe",
],
[
"IncrementalDecoder",
22,
"2d2cf7a62ece3c133ac82e9d59fee2ecfd21eb1a79c31b7d4a6e64a30fc2072d",
],
[
"StreamWriter",
25,
"b77dff0cd7cf304af6ae747b34e03499353b914282cb7d782ec4d8e24e442c67",
],
[
"StreamReader",
28,
"0b21e2f77e98d3f01c2698ed2b9cff5778331afb1a8bdc12b3eb80c389d36861",
],
[
"getregentry",
33,
"98cecbb09af6594440e3caab695700c9ca4d5a1355a3e8a83e2172dd8a9fc6a5",
],
]);
expect(evidence.normalized_result.artifact.sha256).toBe(
createHash("sha256").update(pyc).digest("hex"),
);
},
);
cliTest("reads a CPython 3.15 magic 3666 stdlib pyc", async ({ cli }) => {
// `encodings/__pycache__/utf_8.cpython-315.pyc`, byte for byte as shipped in
// python-build-standalone cpython-3.15.2+20261026-aarch64-apple-darwin
// (install_only tarball, SHA-256 285a6b92...e64a6): the header carries magic 3666.
// The expected rows are what CPython 3.15.0b3 (magic 3666) reports for
// these bytes through its own `marshal.loads` (qualname, first line,
// SHA-256 of `co_code`), so they are not derived from this reader.
const pyc = Buffer.from(
"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",
"base64",
);
const root = await createTestTempDirectory("rea-python-pyc-final-315-");
const path = join(root, "utf_8.cpython-315.pyc");
await writeFile(path, pyc);
const result = await cli.run({
arguments: ["inspect-python-pyc", path, "--json"],
});
expect(result.exitCode, result.stderr).toBe(0);
const evidence = completePycEvidenceSchema.parse(result.json);
expect(evidence.normalized_result.header).toMatchObject({
magic: 3666,
python_version: "3.15",
code_layout: "py311",
flags: 0,
hash_based: false,
});
expect(
evidence.normalized_result.code_objects.map((code) => [
code.qualname,
code.firstlineno,
code.bytecode.sha256,
]),
).toEqual([
[
"<module>",
1,
"dec009b8d182273295bb1af2572eaf510c5a2c9b79c9ed5b5867acd3ab3f3532",
],
[
"decode",
15,
"fe1a50cda8d8510aee7c6c8a4300c4fb151a360ba9e830b7ac9bd797d4b522f6",
],
[
"IncrementalEncoder",
18,
"32afcda5f0c08dccd01a0e42c4a2b5575247c8ed5723bc704db2dd995511c73b",
],
[
"IncrementalEncoder.encode",
19,
"df5053d342e0600648ddbd48ec64a5f1c4336c6956cde360b8799c61da5d93fe",
],
[
"IncrementalDecoder",
22,
"2d2cf7a62ece3c133ac82e9d59fee2ecfd21eb1a79c31b7d4a6e64a30fc2072d",
],
[
"StreamWriter",
25,
"b77dff0cd7cf304af6ae747b34e03499353b914282cb7d782ec4d8e24e442c67",
],
[
"StreamReader",
28,
"0b21e2f77e98d3f01c2698ed2b9cff5778331afb1a8bdc12b3eb80c389d36861",
],
[
"getregentry",
33,
"98cecbb09af6594440e3caab695700c9ca4d5a1355a3e8a83e2172dd8a9fc6a5",
],
]);
expect(evidence.normalized_result.artifact.sha256).toBe(
createHash("sha256").update(pyc).digest("hex"),
);
});
cliTest(
"uses magic-specific cache tables for synthetic pyc bytecode equality",
@@ -293,7 +295,6 @@ cliTest(
{ magic: 3564, opcode: 58, cacheUnits: 1 },
{ magic: 3571, opcode: 82, cacheUnits: 9 },
{ magic: 3627, opcode: 80, cacheUnits: 9 },
{ magic: 3663, opcode: 79, cacheUnits: 9 },
{ magic: 3666, opcode: 128, cacheUnits: 1 },
];
for (const { magic, opcode, cacheUnits } of cases) {