#define WIN32_LEAN_AND_MEAN #include #define CHECK(code,condition) do { if(!(condition)) return code; } while(0) static CRITICAL_SECTION section; static volatile LONG counter; static HANDLE gate_event; static DWORD tls_slot; static SRWLOCK lock; static CONDITION_VARIABLE ready_variable; static volatile LONG ready_flag; static volatile LONG apc_value; static volatile DWORD apc_thread; static HANDLE apc_ready; static HANDLE mutex; static volatile LONG order[8]; static volatile LONG order_count; static DWORD WINAPI counting(LPVOID parameter) { // Sleep(0) forces the scheduler to interleave the threads inside the critical section's neighbourhood. for(int i=0;i<100;++i) { EnterCriticalSection(§ion); LONG seen=counter; Sleep(0); counter=seen+1; LeaveCriticalSection(§ion); Sleep(0); } return (DWORD)(ULONG_PTR)parameter; } static DWORD WINAPI waiting(LPVOID parameter) { (void)parameter; if(WaitForSingleObject(gate_event,5000)!=WAIT_OBJECT_0) return 1; return 42; } static DWORD WINAPI tls_reader(LPVOID parameter) { (void)parameter; if(TlsGetValue(tls_slot)!=0) return 1; TlsSetValue(tls_slot,(LPVOID)(ULONG_PTR)77); Sleep(10); return TlsGetValue(tls_slot)==(LPVOID)(ULONG_PTR)77 ? 0 : 2; } static SLIST_HEADER list; static SLIST_ENTRY list_entries[16]; static DWORD WINAPI list_worker(LPVOID parameter) { // Each thread only pushes entries it owns, the ones it pushed or popped last, so no entry is ever in the list twice. PSLIST_ENTRY owned[4]; for(int i=0;i<4;++i) owned[i]=&list_entries[(ULONG_PTR)parameter*4+i]; for(int round=0;round<500;++round) { for(int i=0;i<4;++i) InterlockedPushEntrySList(&list,owned[i]); if(!(round&15)) Sleep(0); for(int i=0;i<4;++i) if(!(owned[i]=InterlockedPopEntrySList(&list))) return 1; } return 0; } static DWORD WINAPI producer(LPVOID parameter) { (void)parameter; Sleep(20); AcquireSRWLockExclusive(&lock); ready_flag=1; ReleaseSRWLockExclusive(&lock); WakeAllConditionVariable(&ready_variable); return 0; } static DWORD WINAPI ordered(LPVOID parameter) { Sleep((DWORD)(ULONG_PTR)parameter); order[order_count++]=(LONG)(ULONG_PTR)parameter; return 0; } static VOID CALLBACK apc(ULONG_PTR parameter) { apc_value=(LONG)parameter; apc_thread=GetCurrentThreadId(); } static DWORD WINAPI alertable_worker(LPVOID parameter) { const DWORD mode=(DWORD)(ULONG_PTR)parameter; SetEvent(apc_ready); const DWORD result=mode ? SleepEx(2000,TRUE) : WaitForSingleObjectEx(gate_event,2000,TRUE); return result==WAIT_IO_COMPLETION && apc_value==(LONG)(400+mode) && apc_thread==GetCurrentThreadId() ? 0 : 1; } static DWORD WINAPI mutex_owner(LPVOID parameter) { (void)parameter; if(WaitForSingleObject(mutex,0)!=WAIT_TIMEOUT) return 1; if(ReleaseMutex(mutex)) return 2; return GetLastError()==ERROR_NOT_OWNER ? 0 : 3; } static int critical_options(void) { ULONG_PTR peb; #ifdef __x86_64__ __asm__ volatile("movq %%gs:0x60,%0" : "=r"(peb)); const BYTE *version = (const BYTE *)(peb + 0x118); #else __asm__ volatile("movl %%fs:0x30,%0" : "=r"(peb)); const BYTE *version = (const BYTE *)(peb + 0xa4); #endif if (*(const DWORD *)version != 10 || *(const DWORD *)(version + 4) || *(const WORD *)(version + 8) != 19045 || *(const WORD *)(version + 10) || *(const DWORD *)(version + 12) != VER_PLATFORM_WIN32_NT) return 85; CRITICAL_SECTION option; const DWORD flags[] = {1, 0x00ffffff, 0x01000001, 0x10000000}; for (unsigned index = 0; index < sizeof(flags) / sizeof(flags[0]); ++index) { for (unsigned at = 0; at < sizeof(option); ++at) ((BYTE *)&option)[at] = 0xa5; if (!InitializeCriticalSectionEx(&option, 0, flags[index])) return 80; if ((flags[index] & 0x01000000) && option.DebugInfo && (ULONG_PTR)option.DebugInfo != (ULONG_PTR)-1) return 81; /* Without CRITICAL_SECTION_NO_DEBUG_INFO there is a debug record to write into, as the libraries built from Wine sources name their sections in it. */ if (!(flags[index] & 0x01000000)) { if (!option.DebugInfo || (ULONG_PTR)option.DebugInfo == (ULONG_PTR)-1) return 86; option.DebugInfo->Flags = 0x5a5a5a5a; if (option.DebugInfo->Flags != 0x5a5a5a5a) return 87; } EnterCriticalSection(&option); if (!TryEnterCriticalSection(&option)) return 82; LeaveCriticalSection(&option); LeaveCriticalSection(&option); DeleteCriticalSection(&option); } CRITICAL_SECTION plain; InitializeCriticalSection(&plain); if (!plain.DebugInfo || (ULONG_PTR)plain.DebugInfo == (ULONG_PTR)-1) return 88; plain.DebugInfo->Flags = 0x5a5a5a5a; DeleteCriticalSection(&plain); /* A section with a debug record of the program's own, as static initializers make: deleting it leaves the record alone. */ static RTL_CRITICAL_SECTION_DEBUG own; static CRITICAL_SECTION owned = {&own, -1, 0, 0, 0, 0}; own.CriticalSection = &owned; own.Flags = 7; EnterCriticalSection(&owned); LeaveCriticalSection(&owned); DeleteCriticalSection(&owned); if (own.Flags != 7 || own.CriticalSection != &owned) return 89; for (unsigned at = 0; at < sizeof(option); ++at) ((BYTE *)&option)[at] = 0xa5; if (InitializeCriticalSectionEx(&option, 0, 0x20000001) || GetLastError() != ERROR_INVALID_PARAMETER) return 83; for (unsigned at = 0; at < sizeof(option); ++at) if (((BYTE *)&option)[at] != 0xa5) return 84; return 0; } int raven_entry(void) { const int options = critical_options(); if (options) return options; ULONG_PTR stack_low=0,stack_high=0; GetCurrentThreadStackLimits(&stack_low,&stack_high); CHECK(78,stack_low && stack_low<(ULONG_PTR)&stack_low && (ULONG_PTR)&stack_low