@@ -318,6 +318,46 @@ def run(self):
318318 DeleteKey (HKEY_CURRENT_USER , test_key_name + '\\ changing_value' )
319319 DeleteKey (HKEY_CURRENT_USER , test_key_name )
320320
321+ def test_queryvalueex_race_condition (self ):
322+ # gh-142282: QueryValueEx could read garbage buffer under race
323+ # condition when another thread changes the value size
324+ done = False
325+ error_found = None
326+ values = [b'ham' , b'spam' ]
327+
328+ class WriterThread (threading .Thread ):
329+ def run (self ):
330+ with CreateKey (HKEY_CURRENT_USER , test_key_name ) as key :
331+ use_first = True
332+ while not done :
333+ val = values [0 ] if use_first else values [1 ]
334+ use_first = not use_first
335+ SetValueEx (key , 'test_value' , 0 , REG_BINARY , val )
336+
337+ thread = WriterThread ()
338+ thread .start ()
339+ try :
340+ with CreateKey (HKEY_CURRENT_USER , test_key_name ) as key :
341+ for _ in range (1000 ):
342+ try :
343+ result , typ = QueryValueEx (key , 'test_value' )
344+ except FileNotFoundError :
345+ # Value not yet created
346+ continue
347+ # The result must be one of the written values,
348+ # not garbage data from uninitialized buffer
349+ if result not in values :
350+ error_found = result
351+ break
352+ finally :
353+ done = True
354+ thread .join ()
355+ DeleteKey (HKEY_CURRENT_USER , test_key_name )
356+
357+ if error_found is not None :
358+ self .fail (f"QueryValueEx returned unexpected value: { error_found !r} , "
359+ f"expected one of { values } " )
360+
321361 def test_long_key (self ):
322362 # Issue2810, in 2.6 and 3.1 when the key name was exactly 256
323363 # characters, EnumKey raised "WindowsError: More data is
0 commit comments