From 2f49286258b7338d4ac0a23de83208005fbcabaf Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Tue, 23 Sep 2025 20:35:09 +0300 Subject: [PATCH 1/9] Use memcpy for patching values instead of direct assignment --- Python/jit.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Python/jit.c b/Python/jit.c index 01ec9c1fa6e8a9..79a6dd9aabda00 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -205,7 +205,8 @@ patch_32(unsigned char *location, uint64_t value) uint32_t *loc32 = (uint32_t *)location; // Check that we're not out of range of 32 unsigned bits: assert(value < (1ULL << 32)); - *loc32 = (uint32_t)value; + uint32_t final_value = (uint32_t)value; + memcpy(location, &final_value, sizeof(final_value)); } // 32-bit relative address. @@ -217,7 +218,8 @@ patch_32r(unsigned char *location, uint64_t value) // Check that we're not out of range of 32 signed bits: assert((int64_t)value >= -(1LL << 31)); assert((int64_t)value < (1LL << 31)); - *loc32 = (uint32_t)value; + uint32_t final_value = (uint32_t)value; + memcpy(location, &final_value, sizeof(final_value)); } // 64-bit absolute address. @@ -225,7 +227,7 @@ void patch_64(unsigned char *location, uint64_t value) { uint64_t *loc64 = (uint64_t *)location; - *loc64 = value; + memcpy(location, &value, sizeof(value)); } // 12-bit low part of an absolute address. Pairs nicely with patch_aarch64_21r @@ -393,7 +395,10 @@ patch_x86_64_32rx(unsigned char *location, uint64_t value) { uint8_t *loc8 = (uint8_t *)location; // Try to relax the GOT load into an immediate value: - uint64_t relaxed = *(uint64_t *)(value + 4) - 4; + uint64_t relaxed; + memcpy(&relaxed, (void *)(value + 4), sizeof(relaxed)); + relaxed -= 4; + if ((int64_t)relaxed - (int64_t)location >= -(1LL << 31) && (int64_t)relaxed - (int64_t)location + 1 < (1LL << 31)) { From f6765bab79646cadd49b7f844d7e0811a1f2bc3f Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Tue, 23 Sep 2025 21:01:25 +0300 Subject: [PATCH 2/9] Add news entry for JIT unaligned store fix in patch_* functions --- .../2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst new file mode 100644 index 00000000000000..cef258e9ef2681 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst @@ -0,0 +1 @@ +JIT: UB: unaligned store in patch_* functions From c45d0ccb91ee195bc81d94d266bb86e1568980eb Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Tue, 23 Sep 2025 21:26:43 +0300 Subject: [PATCH 3/9] Remove unused local variables from patch functions --- Python/jit.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Python/jit.c b/Python/jit.c index 79a6dd9aabda00..08088c4042e16a 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -202,7 +202,6 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, void patch_32(unsigned char *location, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; // Check that we're not out of range of 32 unsigned bits: assert(value < (1ULL << 32)); uint32_t final_value = (uint32_t)value; @@ -213,7 +212,6 @@ patch_32(unsigned char *location, uint64_t value) void patch_32r(unsigned char *location, uint64_t value) { - uint32_t *loc32 = (uint32_t *)location; value -= (uintptr_t)location; // Check that we're not out of range of 32 signed bits: assert((int64_t)value >= -(1LL << 31)); @@ -226,7 +224,6 @@ patch_32r(unsigned char *location, uint64_t value) void patch_64(unsigned char *location, uint64_t value) { - uint64_t *loc64 = (uint64_t *)location; memcpy(location, &value, sizeof(value)); } From 684ff40a0ce12012bc852b459e863c9822f0730d Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Wed, 24 Sep 2025 11:45:42 +0300 Subject: [PATCH 4/9] Fix UB in JIT patch_* functions with unaligned store --- .../2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst index cef258e9ef2681..5894dd7f935e5c 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst @@ -1 +1 @@ -JIT: UB: unaligned store in patch_* functions +Fix UB when using unaligned store in JIT's ``patch_*`` functions. From b5a86861e86299e6b045b275d8b1623dc98022e8 Mon Sep 17 00:00:00 2001 From: Shamil Date: Wed, 24 Sep 2025 13:27:47 +0300 Subject: [PATCH 5/9] Update Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- .../2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst index 5894dd7f935e5c..e36be529d2a5b9 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-23-21-01-12.gh-issue-139269.1rIaxy.rst @@ -1 +1 @@ -Fix UB when using unaligned store in JIT's ``patch_*`` functions. +Fix undefined behavior when using unaligned store in JIT's ``patch_*`` functions. From d19cf627842ba469cfb142185e61b86cbf226e50 Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Sun, 28 Sep 2025 21:27:31 +0300 Subject: [PATCH 6/9] Use memcpy in set_bits to avoid alignment issues --- Python/jit.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Python/jit.c b/Python/jit.c index 08088c4042e16a..15081d5d595672 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -157,12 +157,18 @@ set_bits(uint32_t *loc, uint8_t loc_start, uint64_t value, uint8_t value_start, uint8_t width) { assert(loc_start + width <= 32); + uint32_t temp_val; + // Use memcpy to safely read the value, avoiding potential alignment + // issues and strict aliasing violations. + memcpy(&temp_val, loc, sizeof(temp_val)); // Clear the bits we're about to patch: - *loc &= ~(((1ULL << width) - 1) << loc_start); - assert(get_bits(*loc, loc_start, width) == 0); + temp_val &= ~(((1ULL << width) - 1) << loc_start); + assert(get_bits(temp_val, loc_start, width) == 0); // Patch the bits: - *loc |= get_bits(value, value_start, width) << loc_start; - assert(get_bits(*loc, loc_start, width) == get_bits(value, value_start, width)); + temp_val |= get_bits(value, value_start, width) << loc_start; + assert(get_bits(temp_val, loc_start, width) == get_bits(value, value_start, width)); + // Safely write the modified value back to memory. + memcpy(loc, &temp_val, sizeof(temp_val)); } // See https://developer.arm.com/documentation/ddi0602/2023-09/Base-Instructions From 1e35b6195cba3efdbb3651076bd274c5f8e77d44 Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Mon, 29 Sep 2025 19:26:05 +0300 Subject: [PATCH 7/9] Track global and builtins dependencies for LOAD_GLOBAL --- Python/optimizer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Python/optimizer.c b/Python/optimizer.c index 7b76cddeabff44..fd224907202985 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -737,6 +737,13 @@ translate_bytecode_to_trace( ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); break; + // VVVVVV ПРЕДЛАГАЕМОЕ ИСПРАВЛЕНИЕ ЗДЕСЬ VVVVVV + case LOAD_GLOBAL: + _Py_BloomFilter_Add(dependencies, frame->f_globals); + _Py_BloomFilter_Add(dependencies, frame->f_builtins); + _Py_FALLTHROUGH; + // ^^^^^^ КОНЕЦ ИСПРАВЛЕНИЯ ^^^^^^ + default: { const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode]; From 39bcc4ce61d0addb20edacf010efc921a7bea638 Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Mon, 29 Sep 2025 19:33:43 +0300 Subject: [PATCH 8/9] Remove comments marking proposed fix in LOAD_GLOBAL case --- Python/optimizer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index fd224907202985..3f4805970c9acd 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -737,12 +737,10 @@ translate_bytecode_to_trace( ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); break; - // VVVVVV ПРЕДЛАГАЕМОЕ ИСПРАВЛЕНИЕ ЗДЕСЬ VVVVVV case LOAD_GLOBAL: _Py_BloomFilter_Add(dependencies, frame->f_globals); _Py_BloomFilter_Add(dependencies, frame->f_builtins); _Py_FALLTHROUGH; - // ^^^^^^ КОНЕЦ ИСПРАВЛЕНИЯ ^^^^^^ default: { From 44e9cf74ab63eb2fc36ca3ee757f9869894ca981 Mon Sep 17 00:00:00 2001 From: Shamil Abdulaev Date: Thu, 9 Oct 2025 02:10:29 +0300 Subject: [PATCH 9/9] remove LOAD_GLOBAL --- Python/optimizer.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 3f4805970c9acd..7b76cddeabff44 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -737,11 +737,6 @@ translate_bytecode_to_trace( ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target); break; - case LOAD_GLOBAL: - _Py_BloomFilter_Add(dependencies, frame->f_globals); - _Py_BloomFilter_Add(dependencies, frame->f_builtins); - _Py_FALLTHROUGH; - default: { const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];