Skip to content

Commit 87e1ff8

Browse files
authored
Merge branch 'main' into filecmp-cache-tests
2 parents 963b2e3 + 88fd5fe commit 87e1ff8

27 files changed

Lines changed: 359 additions & 310 deletions

‎.github/ISSUE_TEMPLATE/bug.yml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ body:
3434
label: "CPython versions tested on:"
3535
multiple: true
3636
options:
37-
- "3.10"
3837
- "3.11"
3938
- "3.12"
4039
- "3.13"

‎.github/ISSUE_TEMPLATE/crash.yml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ body:
2828
label: "CPython versions tested on:"
2929
multiple: true
3030
options:
31-
- "3.10"
3231
- "3.11"
3332
- "3.12"
3433
- "3.13"

‎.github/dependabot.yml‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,6 @@ updates:
9292
cooldown:
9393
default-days: 14
9494

95-
- package-ecosystem: "github-actions"
96-
target-branch: "3.10"
97-
directory: "/"
98-
schedule:
99-
interval: "quarterly"
100-
labels: ["skip issue", "skip news"]
101-
groups:
102-
actions:
103-
patterns: ["*"]
104-
cooldown:
105-
default-days: 14
106-
10795
# Only bump bugfix branches for pip. Remove
10896
# the entry when branch goes security-only.
10997
- package-ecosystem: "pip"

‎.github/workflows/mypy.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
7070
with:
7171
persist-credentials: false
72-
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
72+
- uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
7373
with:
7474
python-version: "3.15"
7575
activate-environment: true

‎.github/workflows/reusable-cifuzz.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
path: ./out/artifacts
4444
- name: Upload SARIF
4545
if: always() && steps.build.outcome == 'success'
46-
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
46+
uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
4747
with:
4848
sarif_file: cifuzz-sarif/results.sarif
4949
checkout_path: cifuzz-sarif

‎Include/internal/pycore_object.h‎

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11-
#include "pycore_emscripten_trampoline.h" // _PyCFunction_TrampolineCall()
11+
#include "pycore_wasm_trampoline.h" // _PyCFunction_TrampolineCall()
1212
#include "pycore_gc.h" // _PyObject_GC_TRACK()
1313
#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_PTR_ACQUIRE()
1414
#include "pycore_pystate.h" // _PyInterpreterState_GET()
@@ -977,27 +977,6 @@ extern PyObject* _PyObject_NextNotImplemented(PyObject *);
977977
// Export for '_datetime' shared extension
978978
PyAPI_FUNC(PyObject*) _PyObject_GetState(PyObject *);
979979

980-
/* C function call trampolines to mitigate bad function pointer casts.
981-
*
982-
* Typical native ABIs ignore additional arguments or fill in missing
983-
* values with 0/NULL in function pointer cast. Compilers do not show
984-
* warnings when a function pointer is explicitly casted to an
985-
* incompatible type.
986-
*
987-
* Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict
988-
* function signature checks. Argument count, types, and return type must
989-
* match.
990-
*
991-
* Third party code unintentionally rely on problematic fpcasts. The call
992-
* trampoline mitigates common occurrences of bad fpcasts on Emscripten.
993-
*/
994-
#if !(defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE))
995-
#define _PyCFunction_TrampolineCall(meth, self, args) \
996-
(meth)((self), (args))
997-
#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
998-
(meth)((self), (args), (kw))
999-
#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE
1000-
1001980
// Export these 2 symbols for '_pickle' shared extension
1002981
PyAPI_DATA(PyTypeObject) _PyNone_Type;
1003982
PyAPI_DATA(PyTypeObject) _PyNotImplemented_Type;

‎Include/internal/pycore_runtime_structs.h‎

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,16 +271,6 @@ struct pyruntimestate {
271271
struct _types_runtime_state types;
272272
struct _Py_time_runtime_state time;
273273

274-
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
275-
// Used in "Python/emscripten_trampoline.c" to choose between wasm-gc
276-
// trampoline and JavaScript trampoline.
277-
PyObject* (*emscripten_trampoline)(int* success,
278-
PyCFunctionWithKeywords func,
279-
PyObject* self,
280-
PyObject* args,
281-
PyObject* kw);
282-
#endif
283-
284274
/* All the objects that are shared by the runtime's interpreters. */
285275
struct _Py_cached_objects cached_objects;
286276
struct _Py_static_objects static_objects;
Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
#ifndef Py_EMSCRIPTEN_TRAMPOLINE_H
2-
#define Py_EMSCRIPTEN_TRAMPOLINE_H
1+
#ifndef Py_WASM_TRAMPOLINE_H
2+
#define Py_WASM_TRAMPOLINE_H
33

4-
#include "pycore_typedefs.h" // _PyRuntimeState
4+
#include "Python.h"
55

66
/**
77
* C function call trampolines to mitigate bad function pointer casts.
@@ -18,37 +18,44 @@
1818
* with 0/NULL in function pointer cast. Compilers do not show warnings when a
1919
* function pointer is explicitly casted to an incompatible type.
2020
*
21-
* Bad fpcasts are an issue in WebAssembly. WASM's indirect_call has strict
21+
* Bad fpcasts are an issue in WebAssembly. Wasm's indirect_call has strict
2222
* function signature checks. Argument count, types, and return type must match.
2323
*
2424
* Third party code unintentionally rely on problematic fpcasts. The call
25-
* trampoline mitigates common occurrences of bad fpcasts on Emscripten.
25+
* trampoline mitigates common occurrences of bad fpcasts on Wasm targets.
2626
*/
2727

28-
#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
28+
#if defined(__wasm__) && defined(PY_CALL_TRAMPOLINE)
2929

3030
PyObject*
31-
_PyEM_TrampolineCall(PyCFunctionWithKeywords func,
32-
PyObject* self,
33-
PyObject* args,
34-
PyObject* kw);
31+
_PyWasm_TrampolineCall(PyCFunctionWithKeywords func,
32+
PyObject* self,
33+
PyObject* args,
34+
PyObject* kw);
35+
36+
int
37+
_PyWasm_TrampolineCallSetter(setter func,
38+
PyObject* self,
39+
PyObject* val,
40+
void* closure);
3541

3642
#define _PyCFunction_TrampolineCall(meth, self, args) \
37-
_PyEM_TrampolineCall(*_PyCFunctionWithKeywords_CAST(meth), (self), (args), NULL)
43+
_PyWasm_TrampolineCall(*_PyCFunctionWithKeywords_CAST(meth), (self), (args), NULL)
3844

3945
#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
40-
_PyEM_TrampolineCall((meth), (self), (args), (kw))
46+
_PyWasm_TrampolineCall((meth), (self), (args), (kw))
4147

4248
#define descr_set_trampoline_call(set, obj, value, closure) \
43-
((int)_PyEM_TrampolineCall(_PyCFunctionWithKeywords_CAST(set), (obj), \
44-
(value), (PyObject*)(closure)))
49+
_PyWasm_TrampolineCallSetter((set), (obj), (value), (closure))
4550

4651
#define descr_get_trampoline_call(get, obj, closure) \
47-
_PyEM_TrampolineCall(_PyCFunctionWithKeywords_CAST(get), (obj), \
48-
(PyObject*)(closure), NULL)
52+
_PyWasm_TrampolineCall(_PyCFunctionWithKeywords_CAST(get), (obj), \
53+
(PyObject*)(closure), NULL)
54+
4955

56+
#else // defined(__wasm__) && defined(PY_CALL_TRAMPOLINE)
5057

51-
#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
58+
// Disable trampolines by directly calling the method.
5259

5360
#define _PyCFunction_TrampolineCall(meth, self, args) \
5461
(meth)((self), (args))
@@ -62,6 +69,6 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func,
6269
#define descr_get_trampoline_call(get, obj, closure) \
6370
(get)((obj), (closure))
6471

65-
#endif // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
72+
#endif // defined(__wasm__) && defined(PY_CALL_TRAMPOLINE)
6673

67-
#endif // ndef Py_EMSCRIPTEN_SIGNAL_H
74+
#endif // ndef Py_WASM_TRAMPOLINE_H

‎Lib/test/test_capi/test_fpcast.py‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""Tests for calling C functions through mis-cast function pointers.
2+
3+
Extension modules frequently cast C functions with the wrong number of
4+
arguments to PyCFunction, getter, setter or ternaryfunc. Native ABIs tolerate
5+
this; on WebAssembly, call_indirect checks the signature, so CPython routes
6+
these calls through a trampoline (Python/wasm_trampoline.c) that detects the
7+
real signature. Calling each variant must work everywhere.
8+
"""
9+
import unittest
10+
from test.support import check_sanitizer, import_helper, is_wasm32
11+
12+
_testcapi = import_helper.import_module('_testcapi')
13+
14+
15+
# Native ABIs tolerate these calls but they are implementation-defined behavior.
16+
# -fsanitize=undefined correctly trips on them.
17+
@unittest.skipIf(check_sanitizer(ub=True),
18+
"calls through mis-cast function pointers are UB natively")
19+
class FpcastTest(unittest.TestCase):
20+
def check_calls(self, obj, prefix):
21+
for arity in range(4):
22+
with self.subTest(kind="noargs", arity=arity):
23+
self.assertIsNone(getattr(obj, f"{prefix}noargs{arity}")())
24+
with self.subTest(kind="o", arity=arity):
25+
self.assertIsNone(getattr(obj, f"{prefix}o{arity}")(1))
26+
with self.subTest(kind="varargs", arity=arity):
27+
self.assertIsNone(getattr(obj, f"{prefix}varargs{arity}")())
28+
self.assertIsNone(getattr(obj, f"{prefix}varargs{arity}")(1, 2))
29+
with self.subTest(kind="kwargs", arity=arity):
30+
self.assertIsNone(getattr(obj, f"{prefix}kwargs{arity}")())
31+
self.assertIsNone(getattr(obj, f"{prefix}kwargs{arity}")(1, x=2))
32+
33+
def test_module_functions(self):
34+
self.check_calls(_testcapi, "fpcast_")
35+
36+
def test_methods(self):
37+
self.check_calls(_testcapi.FpcastTestType(), "")
38+
39+
def test_tp_call(self):
40+
for arity in range(4):
41+
with self.subTest(arity=arity):
42+
cls = getattr(_testcapi, f"FpcastCallable{arity}")
43+
self.assertIsNone(cls()())
44+
self.assertIsNone(cls()(1, x=2))
45+
46+
def test_getset(self):
47+
t = _testcapi.FpcastTestType()
48+
self.assertIsNone(t.getset0)
49+
self.assertIsNone(t.getset1)
50+
self.assertIsNone(t.getset2)
51+
t.getset1 = 5
52+
sentinel = object()
53+
t.getset2 = sentinel
54+
self.assertIs(_testcapi.fpcast_last_set_value(), sentinel)
55+
with self.assertRaises(AttributeError):
56+
t.getset0 = 1
57+
58+
@unittest.skipUnless(is_wasm32, "requires the wasm call trampoline")
59+
def test_unsupported_signature(self):
60+
# A function with four pointer arguments matches none of the
61+
# signatures the trampoline knows about: it must raise SystemError
62+
# rather than trap.
63+
t = _testcapi.FpcastTestType()
64+
with self.assertRaises(SystemError):
65+
_testcapi.fpcast_noargs4()
66+
with self.assertRaises(SystemError):
67+
t.noargs4()
68+
with self.assertRaises(SystemError):
69+
_testcapi.FpcastCallable4()()
70+
with self.assertRaises(SystemError):
71+
t.getset4
72+
with self.assertRaises(SystemError):
73+
t.getset4 = 1
74+
75+
76+
if __name__ == "__main__":
77+
unittest.main()

‎Makefile.pre.in‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,7 @@ PYTHON_HEADERS= \
14391439
$(srcdir)/Include/internal/pycore_uop.h \
14401440
$(srcdir)/Include/internal/pycore_uop_ids.h \
14411441
$(srcdir)/Include/internal/pycore_uop_metadata.h \
1442+
$(srcdir)/Include/internal/pycore_wasm_trampoline.h \
14421443
$(srcdir)/Include/internal/pycore_warnings.h \
14431444
$(srcdir)/Include/internal/pycore_weakref.h \
14441445
$(DTRACE_HEADERS) \
@@ -3153,13 +3154,6 @@ Python/asm_trampoline_universal2.o: $(srcdir)/Python/asm_trampoline_aarch64.S $(
31533154
rm -f Python/asm_trampoline_arm64-apple-darwin.o \
31543155
Python/asm_trampoline_x86_64-apple-darwin.o
31553156

3156-
Python/emscripten_trampoline_inner.wasm: $(srcdir)/Python/emscripten_trampoline_inner.c
3157-
# emcc has a path that ends with emsdk/upstream/emscripten/emcc, we're looking for emsdk/upstream/bin/clang.
3158-
$$(em-config LLVM_ROOT)/clang -o $@ $< -mgc -O2 -Wl,--no-entry -Wl,--import-table -Wl,--import-memory -target wasm32-unknown-unknown -nostdlib
3159-
3160-
Python/emscripten_trampoline_wasm.c: Python/emscripten_trampoline_inner.wasm
3161-
$(PYTHON_FOR_REGEN) $(srcdir)/Platforms/emscripten/prepare_external_wasm.py $< $@ getWasmTrampolineModule
3162-
31633157
JIT_SHIM_BUILD_OBJS= @JIT_SHIM_BUILD_O@
31643158
JIT_UNWIND_INFO_H= $(if $(JIT_OBJS),jit_unwind_info.h $(patsubst jit_stencils-%.h,jit_unwind_info-%.h,@JIT_STENCILS_H@))
31653159
JIT_BUILD_TARGETS= jit_stencils.h @JIT_STENCILS_H@ $(JIT_UNWIND_INFO_H) $(JIT_SHIM_BUILD_OBJS)

0 commit comments

Comments
 (0)