|
3 | 3 |
|
4 | 4 | import gc |
5 | 5 | import os, sys, errno |
| 6 | +import itertools |
6 | 7 | import threading |
7 | 8 | import unittest |
8 | 9 | from platform import machine, win32_edition |
@@ -291,6 +292,37 @@ def run(self): |
291 | 292 | DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value') |
292 | 293 | DeleteKey(HKEY_CURRENT_USER, test_key_name) |
293 | 294 |
|
| 295 | + def test_queryvalueex_race_condition(self): |
| 296 | + # gh-142282: QueryValueEx could read garbage buffer under race |
| 297 | + # condition when another thread changes the value size |
| 298 | + done = False |
| 299 | + ready = threading.Event() |
| 300 | + values = [b'ham', b'spam'] |
| 301 | + |
| 302 | + class WriterThread(threading.Thread): |
| 303 | + def run(self): |
| 304 | + with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: |
| 305 | + values_iter = itertools.cycle(values) |
| 306 | + while not done: |
| 307 | + val = next(values_iter) |
| 308 | + SetValueEx(key, 'test_value', 0, REG_BINARY, val) |
| 309 | + ready.set() |
| 310 | + |
| 311 | + thread = WriterThread() |
| 312 | + thread.start() |
| 313 | + try: |
| 314 | + ready.wait() |
| 315 | + with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: |
| 316 | + for _ in range(1000): |
| 317 | + result, typ = QueryValueEx(key, 'test_value') |
| 318 | + # The result must be one of the written values, |
| 319 | + # not garbage data from uninitialized buffer |
| 320 | + self.assertIn(result, values) |
| 321 | + finally: |
| 322 | + done = True |
| 323 | + thread.join() |
| 324 | + DeleteKey(HKEY_CURRENT_USER, test_key_name) |
| 325 | + |
294 | 326 | def test_long_key(self): |
295 | 327 | # Issue2810, in 2.6 and 3.1 when the key name was exactly 256 |
296 | 328 | # characters, EnumKey raised "WindowsError: More data is |
|
0 commit comments