From fb434ba96a8871b60e7731115aa28a10887db37d Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Fri, 5 Dec 2025 16:31:27 +0900 Subject: [PATCH 1/6] gh-142282 Fix winreg_QueryValueEx_impl to use retSize retSize was ignored bufSize is the size of buffer including uninitializd buffers. --- PC/winreg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PC/winreg.c b/PC/winreg.c index c1be920fc1d92f..bcb02b12299055 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -1651,7 +1651,7 @@ winreg_QueryValueEx_impl(PyObject *module, HKEY key, const wchar_t *name) return PyErr_SetFromWindowsErrWithFunction(rc, "RegQueryValueEx"); } - obData = Reg2Py(retBuf, bufSize, typ); + obData = Reg2Py(retBuf, retSize, typ); PyMem_Free(retBuf); if (obData == NULL) return NULL; From aec85c5ffcb63d7629c602f345843a8bb034a4a6 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Fri, 5 Dec 2025 18:27:04 +0900 Subject: [PATCH 2/6] NEWS --- .../2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst new file mode 100644 index 00000000000000..15907dd3e0cadb --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst @@ -0,0 +1 @@ +Fix winreg.QueryValueEx not to accidently read garbage buffer From ae60654685d5670ff4506d3f38cfea731ce4e6e3 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 6 Dec 2025 09:32:05 +0900 Subject: [PATCH 3/6] Fix NEWS --- .../2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst index 15907dd3e0cadb..d038cd40f4f57a 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-05-18-26-50.gh-issue-142282.g6RQUN.rst @@ -1 +1 @@ -Fix winreg.QueryValueEx not to accidently read garbage buffer +Fix :func:`winreg.QueryValueEx` to not accidentally read garbage buffer under race condition. From 8e2e55e72b55c1bb4170555b20a8bec82bb1ef22 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Sat, 6 Dec 2025 10:44:57 +0900 Subject: [PATCH 4/6] test_queryvalueex_race_condition --- Lib/test/test_winreg.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 733d30b3922d35..3b34184c9e4cff 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -318,6 +318,46 @@ def run(self): DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value') DeleteKey(HKEY_CURRENT_USER, test_key_name) + def test_queryvalueex_race_condition(self): + # gh-142282: QueryValueEx could read garbage buffer under race + # condition when another thread changes the value size + done = False + error_found = None + values = [b'ham', b'spam'] + + class WriterThread(threading.Thread): + def run(self): + with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: + use_first = True + while not done: + val = values[0] if use_first else values[1] + use_first = not use_first + SetValueEx(key, 'test_value', 0, REG_BINARY, val) + + thread = WriterThread() + thread.start() + try: + with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: + for _ in range(1000): + try: + result, typ = QueryValueEx(key, 'test_value') + except FileNotFoundError: + # Value not yet created + continue + # The result must be one of the written values, + # not garbage data from uninitialized buffer + if result not in values: + error_found = result + break + finally: + done = True + thread.join() + DeleteKey(HKEY_CURRENT_USER, test_key_name) + + if error_found is not None: + self.fail(f"QueryValueEx returned unexpected value: {error_found!r}, " + f"expected one of {values}") + def test_long_key(self): # Issue2810, in 2.6 and 3.1 when the key name was exactly 256 # characters, EnumKey raised "WindowsError: More data is From 118abde2ad7c024233e633743b410e33343123c0 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 8 Dec 2025 22:55:02 +0900 Subject: [PATCH 5/6] Fix test --- Lib/test/test_winreg.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 3b34184c9e4cff..099ba833bd9403 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -3,6 +3,7 @@ import gc import os, sys, errno +import itertools import threading import unittest from platform import machine, win32_edition @@ -322,42 +323,33 @@ def test_queryvalueex_race_condition(self): # gh-142282: QueryValueEx could read garbage buffer under race # condition when another thread changes the value size done = False - error_found = None + ready = threading.Event() values = [b'ham', b'spam'] class WriterThread(threading.Thread): def run(self): with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: - use_first = True + values_iter = itertools.cycle(values) while not done: - val = values[0] if use_first else values[1] - use_first = not use_first + val = next(values_iter) SetValueEx(key, 'test_value', 0, REG_BINARY, val) + ready.set() thread = WriterThread() thread.start() try: + ready.wait() with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: for _ in range(1000): - try: - result, typ = QueryValueEx(key, 'test_value') - except FileNotFoundError: - # Value not yet created - continue + result, typ = QueryValueEx(key, 'test_value') # The result must be one of the written values, # not garbage data from uninitialized buffer - if result not in values: - error_found = result - break + self.assertIn(result, values, f"QueryValueEx returned unexpected value: {result!r}") finally: done = True thread.join() DeleteKey(HKEY_CURRENT_USER, test_key_name) - if error_found is not None: - self.fail(f"QueryValueEx returned unexpected value: {error_found!r}, " - f"expected one of {values}") - def test_long_key(self): # Issue2810, in 2.6 and 3.1 when the key name was exactly 256 # characters, EnumKey raised "WindowsError: More data is From d1af504c303c3767aae995de6de1dbaceee46a74 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 9 Dec 2025 12:50:57 +0200 Subject: [PATCH 6/6] Update Lib/test/test_winreg.py --- Lib/test/test_winreg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_winreg.py b/Lib/test/test_winreg.py index 099ba833bd9403..fc0533c6e1531f 100644 --- a/Lib/test/test_winreg.py +++ b/Lib/test/test_winreg.py @@ -344,7 +344,7 @@ def run(self): result, typ = QueryValueEx(key, 'test_value') # The result must be one of the written values, # not garbage data from uninitialized buffer - self.assertIn(result, values, f"QueryValueEx returned unexpected value: {result!r}") + self.assertIn(result, values) finally: done = True thread.join()