From cd914263c6c8909dd245eaf01925e6b938abb02f Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Mon, 17 May 2021 20:48:06 +1000 Subject: [PATCH 1/5] Add test for stack corruption in issue 38748. --- Lib/ctypes/test/test_callbacks.py | 13 ++++++++++++- Modules/_ctypes/_ctypes_test.c | 13 +++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index d8e9c5a760e2c2..bcc7285f69153b 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -146,6 +146,18 @@ def __del__(self): gc.collect() CFUNCTYPE(None)(lambda x=Nasty(): None) + @unittest.expectedFailure + @need_symbol('WINFUNCTYPE') + def test_i38748_stackCorruption(self): + callback_funcType = WINFUNCTYPE(c_long, c_long, c_longlong) + @callback_funcType + def callback(a, b): + c = a + b + print(f"a={a}, b={b}, c={c}") + return c + dll = cdll[_ctypes_test.__file__] + # With no fix for i38748, the next line will raise OSError and cause the test to fail. + self.assertEqual(dll._test_i38748_runCallback(callback, 5, 10), 15) @need_symbol('WINFUNCTYPE') class StdcallCallbacks(Callbacks): @@ -319,6 +331,5 @@ def func(): "of ctypes callback function") self.assertIs(cm.unraisable.object, func) - if __name__ == '__main__': unittest.main() diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index 770c96c60d1f7d..e1f91b476a49fd 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -1034,6 +1034,19 @@ EXPORT (HRESULT) KeepObject(IUnknown *punk) #endif +#ifdef MS_WIN32 + +// i38748: c stub for testing stack corruption +// When executing a Python callback with a long and a long long + +typedef long(__stdcall *_test_i38748_funcType)(long, long long); + +EXPORT(long) _test_i38748_runCallback(_test_i38748_funcType callback, int a, int b) { + return callback(a, b); +} + +#endif + static struct PyModuleDef_Slot _ctypes_test_slots[] = { {0, NULL} }; From 07d66cb9bc7d32dc8c2f8ecbd8516c7122f5330d Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 11 Nov 2021 15:34:28 +1000 Subject: [PATCH 2/5] ctypes.test.test_callbacks.Callbacks.test_i38748_stackCorruption: Only mark this test as an expected failure on 32 bit (x86). --- Lib/ctypes/test/test_callbacks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index bcc7285f69153b..3a9fed966715d9 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -1,3 +1,4 @@ +import sys import functools import unittest from test import support @@ -146,7 +147,6 @@ def __del__(self): gc.collect() CFUNCTYPE(None)(lambda x=Nasty(): None) - @unittest.expectedFailure @need_symbol('WINFUNCTYPE') def test_i38748_stackCorruption(self): callback_funcType = WINFUNCTYPE(c_long, c_long, c_longlong) @@ -158,6 +158,9 @@ def callback(a, b): dll = cdll[_ctypes_test.__file__] # With no fix for i38748, the next line will raise OSError and cause the test to fail. self.assertEqual(dll._test_i38748_runCallback(callback, 5, 10), 15) + # Mark the above test as an expected failure on 32 bit (x86) + if (sys.maxsize + 1) == 2**31: # 32 bit + test_i38748_stackCorruption = unittest.expectedFailure(test_i38748_stackCorruption) @need_symbol('WINFUNCTYPE') class StdcallCallbacks(Callbacks): From b1c6e53783d3dfdeb0e636439d5546dc97284cdd Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 11 Nov 2021 16:28:10 +1000 Subject: [PATCH 3/5] Add back newline --- Lib/ctypes/test/test_callbacks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index 3a9fed966715d9..bb4e40ab31c2f3 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -334,5 +334,6 @@ def func(): "of ctypes callback function") self.assertIs(cm.unraisable.object, func) + if __name__ == '__main__': unittest.main() From 800f0c0f950f8cff2fdaa0e4402289d9302f34ba Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 7 Jun 2022 00:44:41 +0100 Subject: [PATCH 4/5] Update Lib/ctypes/test/test_callbacks.py --- Lib/ctypes/test/test_callbacks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lib/ctypes/test/test_callbacks.py b/Lib/ctypes/test/test_callbacks.py index 35083224093cbb..5e4b5b831b167d 100644 --- a/Lib/ctypes/test/test_callbacks.py +++ b/Lib/ctypes/test/test_callbacks.py @@ -159,7 +159,9 @@ def callback(a, b): dll = cdll[_ctypes_test.__file__] # With no fix for i38748, the next line will raise OSError and cause the test to fail. self.assertEqual(dll._test_i38748_runCallback(callback, 5, 10), 15) - # Mark the above test as an expected failure on 32 bit (x86) + + # This test is expected to fail on 32-bit (x86) because it uses stdcall + # convention, while 64-bit will ignore that and use a safe stack layout. if (sys.maxsize + 1) == 2**31: # 32 bit test_i38748_stackCorruption = unittest.expectedFailure(test_i38748_stackCorruption) From 3f2fc412d0ad0db041ae548ba93ecaa79aed9644 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Sat, 24 Sep 2022 10:01:23 +1000 Subject: [PATCH 5/5] Ctypes callback test for 38748: no longer mark as expected failure as the underlying issue in libffi has been addressed in libffi-3.4.3. --- Lib/test/test_ctypes/test_callbacks.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/test/test_ctypes/test_callbacks.py b/Lib/test/test_ctypes/test_callbacks.py index a78de96659e52d..e8fa3e6f7aca51 100644 --- a/Lib/test/test_ctypes/test_callbacks.py +++ b/Lib/test/test_ctypes/test_callbacks.py @@ -163,10 +163,6 @@ def callback(a, b): # With no fix for i38748, the next line will raise OSError and cause the test to fail. self.assertEqual(dll._test_i38748_runCallback(callback, 5, 10), 15) - # This test is expected to fail on 32-bit (x86) because it uses stdcall - # convention, while 64-bit will ignore that and use a safe stack layout. - if (sys.maxsize + 1) == 2**31: # 32 bit - test_i38748_stackCorruption = unittest.expectedFailure(test_i38748_stackCorruption) @need_symbol('WINFUNCTYPE') class StdcallCallbacks(Callbacks):