crt: gdtoa: Fix race conditions during initialization of dtoa lock Use InterlockedExchange() for changing the dtoa_CS_init variable to ensure that new value would be immediately visible in all threads. When a thread saw `dtoa_CS_init == 0`, it was possible that another thread got scheduled and completed initialization, so `dtoa_CS_init` became 2; and then the current thread did `last_CS_init = InterlockedExchange (&dtoa_CS_init, 1)` and saw `last_CS_init == 2` and restored it with `InterlockedExchange (&dtoa_CS_init, 2)`. The value of `dtoa_CS_init` went from 2 to 1 to 2, which was an ABA pattern. The other thread which actually completed initialization should only call `EnterCriticalSection(&dtoa_CritSec[n])` when it saw `dtoa_CS_init == 2`. In the middle of this ABA sequence, the call might get missed. The fix is to update `dtoa_CS_init` to 1 with a CAS from 0 so it will not be updated from anything else. Co-authored-by: LIU Hao <lh_mouse@126.com> Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/gdtoa/misc.c b/mingw-w64-crt/gdtoa/misc.c index adc955e..74a2ca6 100644 --- a/mingw-w64-crt/gdtoa/misc.c +++ b/mingw-w64-crt/gdtoa/misc.c
@@ -76,16 +76,13 @@ return; } else if (0 == dtoa_CS_init) { - long last_CS_init = InterlockedExchange (&dtoa_CS_init, 1); - if (0 == last_CS_init) { + if (0 == InterlockedCompareExchange (&dtoa_CS_init, 1, 0)) { int i; for (i = 0; i < NLOCKS; i++) InitializeCriticalSection (&dtoa_CritSec[i]); atexit (dtoa_lock_cleanup); - dtoa_CS_init = 2; + (void)InterlockedExchange (&dtoa_CS_init, 2); } - else if (2 == last_CS_init) - dtoa_CS_init = 2; } /* Another thread is initializing. Wait. */ while (1 == dtoa_CS_init)