ntdll: Don't modify LockCount in RtlLeaveCriticalSection if section is not acquired.

Jacek Caban jacek at codeweavers.com
Fri Mar 3 12:30:44 CST 2017


On 03.03.2017 19:13, Sebastian Lackner wrote:
> On 03.03.2017 18:48, Jacek Caban wrote:
>> Signed-off-by: Jacek Caban <jacek at codeweavers.com>
>> ---
>>  dlls/ntdll/critsection.c |  6 ++++-
>>  dlls/ntdll/tests/rtl.c   | 59
>> ++++++++++++++++++++++++++++++++++++++++++++++++
>>  2 files changed, 64 insertions(+), 1 deletion(-)
>>
>>
>>
>> 0001-ntdll-Don-t-modify-LockCount-in-RtlLeaveCriticalSecti.diff
>>
>>
>> diff --git a/dlls/ntdll/critsection.c b/dlls/ntdll/critsection.c
>> index e2eb364..e405b08 100644
>> --- a/dlls/ntdll/critsection.c
>> +++ b/dlls/ntdll/critsection.c
>> @@ -665,7 +665,11 @@ BOOL WINAPI RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION *crit )
>>   */
>>  NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit )
>>  {
>> -    if (--crit->RecursionCount) interlocked_dec( &crit->LockCount );
>> +    if (--crit->RecursionCount)
>> +    {
>> +        if (crit->RecursionCount > 0) interlocked_dec( &crit->LockCount );
>> +        else ERR( "section %p is not acquired\n", crit );
>> +    }
>>      else
>>      {
>>          crit->OwningThread = 0;
>> diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c
>> index 43c26bd..4b6ee35 100644
>> --- a/dlls/ntdll/tests/rtl.c
>> +++ b/dlls/ntdll/tests/rtl.c
>> @@ -98,6 +98,9 @@ static NTSTATUS  (WINAPI *pRtlCompressBuffer)(USHORT, const UCHAR*, ULONG, PUCHA
>>  static BOOL      (WINAPI *pRtlIsCriticalSectionLocked)(CRITICAL_SECTION *);
>>  static BOOL      (WINAPI *pRtlIsCriticalSectionLockedByThread)(CRITICAL_SECTION *);
>>  static NTSTATUS  (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, ULONG, ULONG);
>> +static NTSTATUS  (WINAPI *pRtlInitializeCriticalSection)(CRITICAL_SECTION *);
>> +static NTSTATUS  (WINAPI *pRtlEnterCriticalSection)(RTL_CRITICAL_SECTION *);
>> +static NTSTATUS  (WINAPI *pRtlLeaveCriticalSection)(RTL_CRITICAL_SECTION *);
>>  
>>  static HMODULE hkernel32 = 0;
>>  static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
>> @@ -151,6 +154,9 @@ static void InitFunctionPtrs(void)
>>          pRtlIsCriticalSectionLocked = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLocked");
>>          pRtlIsCriticalSectionLockedByThread = (void *)GetProcAddress(hntdll, "RtlIsCriticalSectionLockedByThread");
>>          pRtlInitializeCriticalSectionEx = (void *)GetProcAddress(hntdll, "RtlInitializeCriticalSectionEx");
>> +        pRtlInitializeCriticalSection = (void *)GetProcAddress(hntdll, "RtlInitializeCriticalSection");
>> +        pRtlEnterCriticalSection = (void *)GetProcAddress(hntdll, "RtlEnterCriticalSection");
>> +        pRtlLeaveCriticalSection = (void *)GetProcAddress(hntdll, "RtlLeaveCriticalSection");
> Those functions should not require dynamic loading. The *Ex function is only
> loaded dynamically because it was introduced in later Windows versions.

Hmm, sure, I may change it. But what's the convention here? Given that
basic functions such as NtReadFile are loaded dynamically, I was under
an impression that we want to load dynamically all functions from ntdll.
Now I see it's not the case for other functions. I recall considering to
change that, but I think Alexandre preferred it that way (I'm not sure
about that, I may remember it wrong).

>>      }
>>      hkernel32 = LoadLibraryA("kernel32.dll");
>>      ok(hkernel32 != 0, "LoadLibrary failed\n");
>> @@ -2094,6 +2100,58 @@ static void test_RtlInitializeCriticalSectionEx(void)
>>      RtlDeleteCriticalSection(&cs);
>>  }
>>  
>> +static void test_RtlLeaveCriticalSection(void)
>> +{
>> +    RTL_CRITICAL_SECTION cs;
>> +    NTSTATUS status;
>> +
>> +    pRtlInitializeCriticalSection(&cs);
>> +
>> +    status = pRtlEnterCriticalSection(&cs);
>> +    ok(!status, "RtlEnterCriticalSection failed: %x\n", status);
>> +    todo_wine
>> +    ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount);
> Some of these tests seem to fail on XP (see testbot).

Yeah, it seems to be post-XP behaviour. I will fix that.

>> +    ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount);
>> +    ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n");
>> +
>> +    status = pRtlLeaveCriticalSection(&cs);
>> +    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
>> +    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
>> +    ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount);
>> +    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
>> +
>> +    /*
>> +     * Trying to leave a section that wasn't acquired modifies RecusionCount to an invalid values,
> Should this be "invalid value" ?

Sure.

>> +     * but doesn't modify LockCount so that an attempt to enter the section later will work.
>> +     */
>> +    status = pRtlLeaveCriticalSection(&cs);
>> +    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
>> +    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
>> +    ok(cs.RecursionCount == -1, "expected RecursionCount == -1, got %d\n", cs.RecursionCount);
>> +    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
>> +
>> +    /* and again */
>> +    status = pRtlLeaveCriticalSection(&cs);
>> +    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
>> +    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
>> +    ok(cs.RecursionCount == -2, "expected RecursionCount == -1, got %d\n", cs.RecursionCount);
> Copy and paste error in the ok message.
>> +    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
>> +
>> +    /* entering section fixes RecursionCount */
>> +    status = pRtlEnterCriticalSection(&cs);
>> +    ok(!status, "RtlEnterCriticalSection failed: %x\n", status);
>> +    todo_wine
>> +    ok(cs.LockCount == -2, "expected LockCount == -2, got %d\n", cs.LockCount);
>> +    ok(cs.RecursionCount == 1, "expected RecursionCount == 1, got %d\n", cs.RecursionCount);
>> +    ok(cs.OwningThread == ULongToHandle(GetCurrentThreadId()), "unexpected OwningThread\n");
>> +
>> +    status = pRtlLeaveCriticalSection(&cs);
>> +    ok(!status, "RtlLeaveCriticalSection failed: %x\n", status);
>> +    ok(cs.LockCount == -1, "expected LockCount == -1, got %d\n", cs.LockCount);
>> +    ok(cs.RecursionCount == 0, "expected RecursionCount == 0, got %d\n", cs.RecursionCount);
>> +    ok(!cs.OwningThread, "unexpected OwningThread %p\n", cs.OwningThread);
> Please delete the critical section after the test to avoid leaks.

OK.

Thanks for the review,
Jacek



More information about the wine-devel mailing list