Understanding an x64 bugcheck

Hi all,

I’m struggling to understand a bugcheck I’m seeing in a filter driver I own.
I only have access to a crash dump, so I’m currently doing a post-mortem on
it.
I’m hitting null pointer in RtlEqualSid, however value is valid when it’s
passed into its parent function.
My x64 assembler isn’t the best it could be, but I’ve added some debug info
below in the hope that someone can help me understand what’s happening.

Thanks in advance.

// In the call to RtlEqualSid:
// rcx is ffffdf8f`26ccc5a0
// rdx is 0000000000000000
// Something is resetting rbx to 0?

// offending assembly
fffff803ffdc21cf 488b5d28 mov rbx,qword ptr [rbp+28h] // \<--- poi(rbp+28) is ffffdf8f280c1a60
fffff803ffdc21d3 4c8d442448 lea r8,[rsp+48h] fffff803ffdc21d8 4c89ac24a0000000 mov qword ptr [rsp+0A0h],r13
fffff803ffdc21e0 498bce mov rcx,r14 fffff803ffdc21e3 41bd01000000 mov r13d,1
fffff803ffdc21e9 418bd5 mov edx,r13d fffff803ffdc21ec ff15c6d00000 call qword ptr
[AMFileSystemFilter!_imp_SeQueryInformationToken (fffff803ffdcf2b8)] fffff803ffdc21f2 448bc0 mov r8d,eax
fffff803ffdc21f5 85c0 test eax,eax fffff803ffdc21f7 7838 js
AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x161 (fffff803ffdc2231) fffff803ffdc21f9 488b4c2448 mov rcx,qword ptr [rsp+48h]
fffff803ffdc21fe 488bd3 mov rdx,rbx // \<---- rbx is 0 here fffff803ffdc2201 488b09 mov rcx,qword ptr [rcx]
fffff803ffdc2204 ff15ced00000 call qword ptr [AMFileSystemFilter!_imp_RtlEqualSid (fffff803ffdcf2d8)]
**BANG**

1: kd> k
*** Stack trace for last set context - .thread/.cxr resets it

Child-SP RetAddr Call Site

00 ffff96018de6c6a0 fffff803ffdc220a nt!RtlEqualSid+0x4
01 (Inline Function) ---------------- AMFileSystemFilter!IsTokenFileOwner+0x37 [f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste mfilter\trustedowner.c @ 102] 02 ffff96018de6c6d0 fffff803ffdd4c67 AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x13a [f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste mfilter\callbacks.c @ 1560] 03 ffff96018de6c770 fffff803fd1246ca AMFileSystemFilter!AmFltPreWrite+0x167 [f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste mfilter\callbacks.c @ 869] 04 ffff96018de6c7f0 fffff803fd124278 FLTMGR!FltpPerformPreCallbacks+0x2ea 05 ffff96018de6c900 fffff803`fd123386 FLTMGR!FltpPassThroughInternal+0x88

1: kd> r
Last set context:
rax=0000000000000000 rbx=0000000000000000 rcx=ffffdf8f26ccc5a0
rdx=0000000000000000 rsi=8000000000000000 rdi=000000000000136c
rip=fffff801a502e6c4 rsp=ffff96018de6c6a0 rbp=ffffc0896a426b00

// instruction that sets rbx is : mov rbx,qword ptr [rbp+28h]
// that address contains a valid pointer, so rbx is valid here
1: kd> dd poi(rbp+0x28)
ffffdf8f280c1a60 00000101 05000000 00000012 00000000 ffffdf8f280c1a70 03180102 6e664d46 093054c3 db9fc9b8
ffffdf8f280c1a80 016cf204 00000000 69b90010 ffffc089 ffffdf8f280c1a90 000000ee 00000000 28102618 ffffdf8f
ffffdf8f280c1aa0 00000000 00000000 00000000 00000000 ffffdf8f280c1ab0 6a6196f0 ffffc089 6a5db250 ffffc089
ffffdf8f280c1ac0 69b90010 ffffc089 00014000 00000000 ffffdf8f280c1ad0 00000078 00000002 009c009a 00000000

1: kd> !sid ffffdf8f`280c1a60
SID is: S-1-5-18

// This is the struct that contains the FileOwner that’s stored in rbx
before it’s 0
1: kd> dt _UCA_FILE_CONTEXT 0xffffc0896a426b00 AMFileSystemFilter!_UCA_FILE_CONTEXT +0x000 Lock : 0 +0x008 Flags : 0 +0x010 FileName : _UNICODE_STRING "\Device\Mup\infrastore\PIPE\srvsvc" +0x020 CleanupCallback : 0xfffff803ffdc20c0 void
AMFileSystemFilter!AmCtxStreamContextCleanupCallback+0
+0x028 FileOwner : 0xffffdf8f`280c1a60 Void <— this is what’s
stored in rbx (rbp+28h)
+0x030 ContainsStreams : 0 ‘’
+0x031 IsDirectory : 0 ‘’
+0x032 FileIsManaged : 0 ‘’

// Looking inside SeQueryInformationToken shows that rbx is saved to rsp+18
and restored from rsp+140??
1: kd> u nt!SeQueryInformationToken
nt!SeQueryInformationToken:
fffff801a541ffa0 48895c2418 mov qword ptr [rsp+18h],rbx fffff801a541ffa5 55 push rbp
fffff801a541ffa6 56 push rsi fffff801a541ffa7 57 push rdi
fffff801a541ffa8 4154 push r12 ... fffff801a5420008 418918 mov dword ptr [r8],ebx
fffff801a542000b 33c0 xor eax,eax fffff801a542000d 488b9c2440010000 mov rbx,qword ptr [rsp+140h]
fffff801a5420015 4881c4f0000000 add rsp,0F0h fffff801a542001c 415f pop r15

//
// Code for the above
//
NTSTATUS
HandleOwnershipCheckOnWrite(In PFLT_INSTANCE Instance,
In PFILE_OBJECT FileObject,
In PUCA_FILE_CONTEXT FileContext,
In HANDLE ProcessId,
In HANDLE ThreadId)
{
PACCESS_TOKEN CallingContextToken;
ERULE_RESULT RuleResult;
BOOLEAN ChangeOwnership;
BOOLEAN CallerIsTrusted;
BOOLEAN IsFileOwner;
NTSTATUS Status;

RuleResult = RULE_DENY;
ChangeOwnership = FALSE;

/*
* The context can change from operation to operation due to
impersonation,
* let’s start by looking up the sid for the user that initiated this
call
*/
CallingContextToken = GetTokenFromCallingContext();

/* Check if the caller is trusted or is a member of a group that is */
Status = IsCallerTrusted(CallingContextToken,
&CallerIsTrusted);
if (!NT_SUCCESS(Status))
{
TRACE_ERROR(TraceHandle, “Failed to check if the caller is trusted :
0x%X”, Status);
}

/* Only trusted owners and callers who own the file are allowed to make
changes */
if (CallerIsTrusted == TRUE)
{
/* We allow this request to go through */
RuleResult = RULE_ALLOW;
return STATUS_SUCCESS;
}

/* Check if the caller owns the file */
Status = IsTokenFileOwner(CallingContextToken,
FileContext->FileOwner,
&IsFileOwner);
if (!NT_SUCCESS(Status))
{
TRACE_ERROR(TraceHandle, “Failed to check if the caller is file
owner : 0x%X”, Status);
}

NTSTATUS
IsTokenFileOwner(In PACCESS_TOKEN AccessToken,
In PSID FileOwner,
Out PBOOLEAN IsOwner)
{
PTOKEN_USER CallerTokenUser;
NTSTATUS Status;

FLT_ASSERT(FileOwner);

*IsOwner = FALSE;

/* Get the token user for this security context */
Status = SeQueryInformationToken(AccessToken,
TokenUser,
&CallerTokenUser);
if (!NT_SUCCESS(Status))
{
return Status;
}

/* Check if the sids match */
if (RtlEqualSid(CallerTokenUser->User.Sid, FileOwner))
{
/* It is, set the flag */
*IsOwner = TRUE;
}

/* Free the pool, it’s owned by the security manager */
ExFreePoolWithTag(CallerTokenUser, ’ eS’);

return STATUS_SUCCESS;
}

Apologies, the assembly formatting was messed up. Let’s try again :slight_smile:

fffff803ffdc21cf 488b5d28 mov rbx,qword ptr [rbp+28h] // \<--- poi(rbp+28) is ffffdf8f280c1a60
fffff803ffdc21d3 4c8d442448 lea r8,[rsp+48h] fffff803ffdc21d8 4c89ac24a0000000 mov qword ptr [rsp+0A0h],r13
fffff803ffdc21e0 498bce mov rcx,r14 fffff803ffdc21e3 41bd01000000 mov r13d,1
fffff803ffdc21e9 418bd5 mov edx,r13d fffff803ffdc21ec ff15c6d00000 call qword ptr
[AMFileSystemFilter!_imp_SeQueryInformationToken (fffff803ffdcf2b8)] fffff803ffdc21f2 448bc0 mov r8d,eax
fffff803ffdc21f5 85c0 test eax,eax fffff803ffdc21f7 7838 js
AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x161 (fffff803ffdc2231) fffff803ffdc21f9 488b4c2448 mov rcx,qword ptr [rsp+48h]
fffff803ffdc21fe 488bd3 mov rdx,rbx // \<---- rbx is 0 here fffff803ffdc2201 488b09 mov rcx,qword ptr [rcx]
fffff803ffdc2204 ff15ced00000 call qword ptr [AMFileSystemFilter!_imp_RtlEqualSid (fffff803ffdcf2d8)]
**BANG**

-----Original Message-----
From: Ged Murphy [mailto:xxxxx@gmail.com]
Sent: 28 September 2016 14:07
To: ‘Windows System Software Devs Interest List’
Subject: Understanding an x64 bugcheck

Hi all,

I’m struggling to understand a bugcheck I’m seeing in a filter driver I own.
I only have access to a crash dump, so I’m currently doing a post-mortem on
it.
I’m hitting null pointer in RtlEqualSid, however value is valid when it’s
passed into its parent function.
My x64 assembler isn’t the best it could be, but I’ve added some debug info
below in the hope that someone can help me understand what’s happening.

Thanks in advance.

// In the call to RtlEqualSid:
// rcx is ffffdf8f26ccc5a0<br>// rdx is 0000000000000000<br>// Something is resetting rbx to 0?<br><br>// offending assembly<br>fffff803ffdc21cf 488b5d28 mov rbx,qword ptr [rbp+28h]
// <— poi(rbp+28) is ffffdf8f280c1a60<br>fffff803ffdc21d3 4c8d442448 lea r8,[rsp+48h]
fffff803ffdc21d8 4c89ac24a0000000 mov qword ptr [rsp+0A0h],r13<br>fffff803ffdc21e0 498bce mov rcx,r14
fffff803ffdc21e3 41bd01000000 mov r13d,1<br>fffff803ffdc21e9 418bd5 mov edx,r13d
fffff803ffdc21ec ff15c6d00000 call qword ptr<br>[AMFileSystemFilter!_imp_SeQueryInformationToken (fffff803ffdcf2b8)]
fffff803ffdc21f2 448bc0 mov r8d,eax<br>fffff803ffdc21f5 85c0 test eax,eax
fffff803ffdc21f7 7838 js<br>AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x161 (fffff803ffdc2231)
fffff803ffdc21f9 488b4c2448 mov rcx,qword ptr [rsp+48h]<br>fffff803ffdc21fe 488bd3 mov rdx,rbx // <---- rbx
is 0 here
fffff803ffdc2201 488b09 mov rcx,qword ptr [rcx]<br>fffff803ffdc2204 ff15ced00000 call qword ptr
[AMFileSystemFilter!_imp_RtlEqualSid (fffff803ffdcf2d8)]<br> **BANG** <br><br>1: kd&gt; k<br> *** Stack trace for last set context - .thread/.cxr resets it<br> # Child-SP RetAddr Call Site<br>00 ffff96018de6c6a0 fffff803ffdc220a nt!RtlEqualSid+0x4<br>01 (Inline Function) ----------------
AMFileSystemFilter!IsTokenFileOwner+0x37
[f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste
mfilter\trustedowner.c @ 102]
02 ffff96018de6c6d0 fffff803ffdd4c67
AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x13a
[f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste
mfilter\callbacks.c @ 1560]
03 ffff96018de6c770 fffff803fd1246ca
AMFileSystemFilter!AmFltPreWrite+0x167
[f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste
mfilter\callbacks.c @ 869]
04 ffff96018de6c7f0 fffff803fd124278 FLTMGR!FltpPerformPreCallbacks+0x2ea
05 ffff96018de6c900 fffff803fd123386 FLTMGR!FltpPassThroughInternal+0x88


1: kd> r
Last set context:
rax=0000000000000000 rbx=0000000000000000 rcx=ffffdf8f26ccc5a0
rdx=0000000000000000 rsi=8000000000000000 rdi=000000000000136c
rip=fffff801a502e6c4 rsp=ffff96018de6c6a0 rbp=ffffc0896a426b00

// instruction that sets rbx is : mov rbx,qword ptr [rbp+28h]
// that address contains a valid pointer, so rbx is valid here
1: kd> dd poi(rbp+0x28)
ffffdf8f280c1a60 00000101 05000000 00000012 00000000<br>ffffdf8f280c1a70 03180102 6e664d46 093054c3 db9fc9b8
ffffdf8f280c1a80 016cf204 00000000 69b90010 ffffc089<br>ffffdf8f280c1a90 000000ee 00000000 28102618 ffffdf8f
ffffdf8f280c1aa0 00000000 00000000 00000000 00000000<br>ffffdf8f280c1ab0 6a6196f0 ffffc089 6a5db250 ffffc089
ffffdf8f280c1ac0 69b90010 ffffc089 00014000 00000000<br>ffffdf8f280c1ad0 00000078 00000002 009c009a 00000000

1: kd> !sid ffffdf8f280c1a60<br>SID is: S-1-5-18<br><br>// This is the struct that contains the FileOwner that's stored in rbx<br>before it's 0<br>1: kd&gt; dt _UCA_FILE_CONTEXT 0xffffc0896a426b00
AMFileSystemFilter!_UCA_FILE_CONTEXT
+0x000 Lock : 0
+0x008 Flags : 0
+0x010 FileName : _UNICODE_STRING
“\Device\Mup\infrastore\PIPE\srvsvc”
+0x020 CleanupCallback : 0xfffff803ffdc20c0 void<br>AMFileSystemFilter!AmCtxStreamContextCleanupCallback+0<br> +0x028 FileOwner : 0xffffdf8f280c1a60 Void <— this is what’s
stored in rbx (rbp+28h)
+0x030 ContainsStreams : 0 ‘’
+0x031 IsDirectory : 0 ‘’
+0x032 FileIsManaged : 0 ‘’

// Looking inside SeQueryInformationToken shows that rbx is saved to rsp+18
and restored from rsp+140??
1: kd> u nt!SeQueryInformationToken
nt!SeQueryInformationToken:
fffff801a541ffa0 48895c2418 mov qword ptr [rsp+18h],rbx<br>fffff801a541ffa5 55 push rbp
fffff801a541ffa6 56 push rsi<br>fffff801a541ffa7 57 push rdi
fffff801a541ffa8 4154 push r12<br>...<br>fffff801a5420008 418918 mov dword ptr [r8],ebx
fffff801a542000b 33c0 xor eax,eax<br>fffff801a542000d 488b9c2440010000 mov rbx,qword ptr [rsp+140h]
fffff801a5420015 4881c4f0000000 add rsp,0F0h<br>fffff801a542001c 415f pop r15


//
// Code for the above
//
NTSTATUS
HandleOwnershipCheckOnWrite(In PFLT_INSTANCE Instance,
In PFILE_OBJECT FileObject,
In PUCA_FILE_CONTEXT FileContext,
In HANDLE ProcessId,
In HANDLE ThreadId)
{
PACCESS_TOKEN CallingContextToken;
ERULE_RESULT RuleResult;
BOOLEAN ChangeOwnership;
BOOLEAN CallerIsTrusted;
BOOLEAN IsFileOwner;
NTSTATUS Status;

RuleResult = RULE_DENY;
ChangeOwnership = FALSE;

/*
* The context can change from operation to operation due to
impersonation,
* let’s start by looking up the sid for the user that initiated this
call
/
CallingContextToken = GetTokenFromCallingContext();

/
Check if the caller is trusted or is a member of a group that is /
Status = IsCallerTrusted(CallingContextToken,
&CallerIsTrusted);
if (!NT_SUCCESS(Status))
{
TRACE_ERROR(TraceHandle, “Failed to check if the caller is trusted :
0x%X”, Status);
}

/
Only trusted owners and callers who own the file are allowed to make
changes /
if (CallerIsTrusted == TRUE)
{
/
We allow this request to go through /
RuleResult = RULE_ALLOW;
return STATUS_SUCCESS;
}

/
Check if the caller owns the file */
Status = IsTokenFileOwner(CallingContextToken,
FileContext->FileOwner,
&IsFileOwner);
if (!NT_SUCCESS(Status))
{
TRACE_ERROR(TraceHandle, “Failed to check if the caller is file
owner : 0x%X”, Status);
}


NTSTATUS
IsTokenFileOwner(In PACCESS_TOKEN AccessToken,
In PSID FileOwner,
Out PBOOLEAN IsOwner)
{
PTOKEN_USER CallerTokenUser;
NTSTATUS Status;

FLT_ASSERT(FileOwner);

IsOwner = FALSE;

/
Get the token user for this security context /
Status = SeQueryInformationToken(AccessToken,
TokenUser,
&CallerTokenUser);
if (!NT_SUCCESS(Status))
{
return Status;
}

/
Check if the sids match /
if (RtlEqualSid(CallerTokenUser->User.Sid, FileOwner))
{
/
It is, set the flag */
IsOwner = TRUE;
}

/
Free the pool, it’s owned by the security manager */
ExFreePoolWithTag(CallerTokenUser, ’ eS’);

return STATUS_SUCCESS;
}

>// In the call to RtlEqualSid:

// rcx is ffffdf8f`26ccc5a0
// rdx is 0000000000000000

This means the second SID passed to RtlEqualSid is NULL but it must be a valid SID. Look at the documentation.

kd> uf nt!RtlEqualSid
nt!RtlEqualSid:
fffff803bbe306c0 4883ec28 sub rsp,28h fffff803bbe306c4 0fb702 movzx eax,word ptr [rdx]
fffff803bbe306c7 663901 cmp word ptr [rcx],ax fffff803bbe306ca 751c jne nt!RtlEqualSid+0x28 (fffff803`bbe306e8) Branch

Clearly, the second SID is accessed (pointer is dereferenced) at RtlEqualSid+0x4 and you get an exception and a bugcheck if it is not handled.

I think you misunderstood. Of course I know the reason for bugcheck. As
you’ll see from the debug info I posted, I’m trying to understand why it’s
null at the point RtlEqualSid is called when it’s valid just before that
call.

The debug info I posted tries to prove the following:

  • The address of the sid is stored in ‘UCA_FILE_CONTEXT: FileOwner’ and is a
    valid pointer, even when the bugcheck occurs.
  • Within the asm, the above address is stored in rbx, so that’s valid at
    when it’s set.
  • Before the call to RtlEqualSid, rcx and rdx are pushed onto the stack
    ready for the call into RtlEqualSid
  • Rcx is set from poi(rcx) and rdx is set from rbx. At this point rbx is
    null. Why is that??

Between the time rbx is set with a valid address, and is used to set rdx,
the code calls SeQueryInformationToken.
Looking at the disassembly, this appears to trample over rbx??

The above only seems to happen in rare cases on a release build of my
driver, I haven’t seen it happen in 6 months of development time using a
debug build.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@gmail.com
Sent: 28 September 2016 15:07
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Understanding an x64 bugcheck

>// In the call to RtlEqualSid:
>// rcx is ffffdf8f26ccc5a0<br>&gt;// rdx is 0000000000000000<br><br>This means the second SID passed to RtlEqualSid is NULL but it must be a<br>valid SID. Look at the documentation.<br><br>kd&gt; uf nt!RtlEqualSid<br>nt!RtlEqualSid:<br>fffff803bbe306c0 4883ec28 sub rsp,28h
fffff803bbe306c4 0fb702 movzx eax,word ptr [rdx]<br>fffff803bbe306c7 663901 cmp word ptr [rcx],ax
fffff803bbe306ca 751c jne nt!RtlEqualSid+0x28<br>(fffff803bbe306e8) Branch

Clearly, the second SID is accessed (pointer is dereferenced) at
RtlEqualSid+0x4 and you get an exception and a bugcheck if it is not
handled.


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

>- Before the call to RtlEqualSid, rcx and rdx are pushed

onto the stack ready for the call into RtlEqualSid

Err, sorry not pushed onto the stack, I mean stored in the first 2 call
registers.
I’m always thinking in x86/__stdcall terms…

Anyway, the rest of what I said still applies :slight_smile:

-----Original Message-----
From: Ged Murphy [mailto:xxxxx@gmail.com]
Sent: 28 September 2016 15:26
To: ‘Windows System Software Devs Interest List’
Subject: RE: [ntdev] Understanding an x64 bugcheck

I think you misunderstood. Of course I know the reason for bugcheck. As
you’ll see from the debug info I posted, I’m trying to understand why it’s
null at the point RtlEqualSid is called when it’s valid just before that
call.

The debug info I posted tries to prove the following:

- The address of the sid is stored in ‘UCA_FILE_CONTEXT: FileOwner’ and is a
valid pointer, even when the bugcheck occurs.
- Within the asm, the above address is stored in rbx, so that’s valid at
when it’s set.
- Before the call to RtlEqualSid, rcx and rdx are pushed onto the stack
ready for the call into RtlEqualSid
- Rcx is set from poi(rcx) and rdx is set from rbx. At this point rbx is
null. Why is that??

Between the time rbx is set with a valid address, and is used to set rdx,
the code calls SeQueryInformationToken.
Looking at the disassembly, this appears to trample over rbx??

The above only seems to happen in rare cases on a release build of my
driver, I haven’t seen it happen in 6 months of development time using a
debug build.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@gmail.com
Sent: 28 September 2016 15:07
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Understanding an x64 bugcheck

>// In the call to RtlEqualSid:
>// rcx is ffffdf8f26ccc5a0<br>&gt;// rdx is 0000000000000000<br><br>This means the second SID passed to RtlEqualSid is NULL but it must be a<br>valid SID. Look at the documentation.<br><br>kd&gt; uf nt!RtlEqualSid<br>nt!RtlEqualSid:<br>fffff803bbe306c0 4883ec28 sub rsp,28h
fffff803bbe306c4 0fb702 movzx eax,word ptr [rdx]<br>fffff803bbe306c7 663901 cmp word ptr [rcx],ax
fffff803bbe306ca 751c jne nt!RtlEqualSid+0x28<br>(fffff803bbe306e8) Branch

Clearly, the second SID is accessed (pointer is dereferenced) at
RtlEqualSid+0x4 and you get an exception and a bugcheck if it is not
handled.


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

Open the crash dump file in WinDbg and run the !analyze -v command.

How could the RDX register content be modified ? When the processor is interrupted, the context is saved and restored when the interrupt handler has completed.

You must focus on the structure that holds the SID. Is this member guaranteed to be non null ?

> Open the crash dump file in WinDbg and run the !analyze -v command.
I’m past !analyze -v being useful. The debug info I added to my first post
goes way beyond what that shows.

How could the RDX register content be modified ?
I’ve already shown that SeQueryInformationToken modifies it and doesn’t seem
to restore it correctly.

You must focus on the structure that holds the SID.
Is this member guaranteed to be non null ?
Yes, not only is it guaranteed to be non-null, but as I’ve said previously,
it’s non-null at the time of the bugcheck.
It’s only the register that the address was moved into that is null, so
something weird is going on.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@gmail.com
Sent: 28 September 2016 15:46
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Understanding an x64 bugcheck

Open the crash dump file in WinDbg and run the !analyze -v command.

How could the RDX register content be modified ? When the processor is
interrupted, the context is saved and restored when the interrupt handler
has completed.

You must focus on the structure that holds the SID. Is this member
guaranteed to be non null ?


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

Well, the most obvious conclusion is that FileOwner parameter is invalid. The following lines confirm it

Last set context: rax=0000000000000000 rbx=0000000000000000 rcx=ffffdf8f26ccc5a0 rdx=0000000000000000 rsi=8000000000000000 rdi=000000000000136c rip=fffff801a502e6c4 rsp=ffff96018de6c6a0 rbp=ffffc0896a426b00

As you can see, rdx (i.e. the register in which the second parameter to a function is passed under MSFT x86_64 calling convention) is NULL. You are getting it from FileContext->FileOwner. Therefore,you should move your search down the call stack and show us how you get this parameter - the solution lies there. Certainly, you can waste your time guessing what happens to RBX(which, BTW, is considered nonvolatile and must be saved and restored by a function that
uses it) as much as you want, but the most obvious conclusion is that FileContext->FileOwner is invalid,even if you try to convince yourself this is not the case…

Anton Bassov

Hi Anton, thanks for chiming in :slight_smile:

Yeah that would be the obvious conclusion, however dumping that address
shows that it’s a valid pointer.
IsTokenFileOwner is inlined by the compiler (this is a release build), so it
has the same frame as the parent function. I’m assuming in this case that
whatever is normally ‘passed’ to that function is already stored in the rbx
register and pulled directly from there when dumping the frame.

Frame 1:
1: kd> .frame 1; dv /t /v
01 (Inline Function) ---------------- AMFileSystemFilter!IsTokenFileOwner+0x37 @rbx void \* FileOwner = 0x0000000000000000
long Status =
ffff96018de6c718 struct _TOKEN_USER * CallerTokenUser =<br>0xffffdf8f26ccc5901: kd> ?? FileOwner
void * 0x0000000000000000<br><br>However as I said, the 'FileOwner' address is still valid. <br>1: kd&gt; .frame 2<br>02 ffff96018de6c6d0 fffff803ffdd4c67<br>AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x13a<br>1: kd&gt; ?? FileContext-&gt;FileOwner<br>void * 0xffffdf8f280c1a60
1: kd> !sid 0xffffdf8f280c1a60<br>SID is: S-1-5-18<br><br>1: kd&gt; dt _UCA_FILE_CONTEXT 0xffffc0896a426b00
AMFileSystemFilter!_UCA_FILE_CONTEXT
+0x000 Lock : 0
+0x008 Flags : 0
+0x010 FileName : _UNICODE_STRING
“\Device\Mup\infrastore\PIPE\srvsvc”
+0x020 CleanupCallback : 0xfffff803ffdc20c0 void<br>AMFileSystemFilter!AmCtxStreamContextCleanupCallback+0<br> +0x028 FileOwner : 0xffffdf8f280c1a60 Void
+0x030 ContainsStreams : 0 ‘’
+0x031 IsDirectory : 0 ‘’
+0x032 FileIsManaged : 0 ‘’

How can FileOwner still be valid when rbx is 0?

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@hotmail.com
Sent: 28 September 2016 16:28
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Understanding an x64 bugcheck

Well, the most obvious conclusion is that FileOwner parameter is invalid.
The following lines confirm it

Last set context: rax=0000000000000000 rbx=0000000000000000
rcx=ffffdf8f26ccc5a0 rdx=0000000000000000 rsi=8000000000000000
rdi=000000000000136c rip=fffff801a502e6c4 rsp=ffff96018de6c6a0
rbp=ffffc0896a426b00

As you can see, rdx (i.e. the register in which the second parameter to a
function is passed under MSFT x86_64 calling convention) is NULL. You are
getting it from FileContext->FileOwner. Therefore,you should move your
search down the call stack and show us how you get this parameter - the
solution lies there. Certainly, you can waste your time guessing what
happens to RBX(which, BTW, is considered nonvolatile and must be saved and
restored by a function that
uses it) as much as you want, but the most obvious conclusion is that
FileContext->FileOwner is invalid,even if you try to convince yourself this
is not the case…

Anton Bassov


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

>I’ve already shown that SeQueryInformationToken modifies it (RDX) and doesn’t seem to restore it correctly.

RDX is volatile and need not be restored.

// Looking inside SeQueryInformationToken shows that rbx is saved to rsp+18 and restored from rsp+140??

If SeQueryInformationToken does not restore RBX than the OS would fail well before your driver loads. Don’t you think ?

Note that the Release version of your code is not protected by the FLT_ASSERT(FileOwner) macro call.

The PUCA_FILE_CONTEXT type is probably a type of your own, isn’t it ? Where do you get the PSID from ?

And post the !analyze -v output.

> RDX is volatile and need not be restored.
Sorry that was a typo, I meant RBX is modified and isn’t restored correctly.
On looking again, perhaps my cacls were wrong. 0x18 + 0xF0 + 7 * 8 = 0x140
which does seem to restore it correctly.

If SeQueryInformationToken does not restore RBX than the OS would fail
well before your driver loads. Don’t you think ?
Yeah, I’m not insinuating this is a Windows bug, I’m just trying to
understand why rbx is valid before the call to SeQueryInformationToken but
is 0 after the call.
Perhaps the stack is being hit somewhere and modifying the location that rbx
was stored, setting it to 0 when it’s restored?

Note that the Release version of your code is not protected by the
FLT_ASSERT(FileOwner) macro call.
Yeah, I’m not too worried about the assert for now. I was just pasting my
unmodified source.

The PUCA_FILE_CONTEXT type is probably a type of your own, isn’t it ?
Where do you get the PSID from ?
Yeah, that’s my file context, set via FltSetContext. It’s allocated from
paged pool when the file context is setup.
It’s still valid when the bugcheck occurs

I’m just trying to understand why RtlEqualSid is being given a NULL value,
when the address that’s moved into the register is valid and the memory it
points to is also valid?

And post the !analyze -v output.

1: kd> !analyze -v
****************************************************************************
***
*
*
* Bugcheck Analysis
*
*
*
****************************************************************************
***

SYSTEM_SERVICE_EXCEPTION (3b)
An exception happened while executing a system service routine.
Arguments:
Arg1: 00000000c0000005, Exception code that caused the bugcheck
Arg2: fffff801a502e6c4, Address of the instruction which caused the bugcheck
Arg3: ffff96018de6bc90, Address of the context record for the exception that
caused the bugcheck
Arg4: 0000000000000000, zero.

Debugging Details:

DUMP_CLASS: 1

DUMP_QUALIFIER: 401

BUILD_VERSION_STRING: 14393.0.amd64fre.rs1_release.160715-1616

SYSTEM_MANUFACTURER: Microsoft Corporation

VIRTUAL_MACHINE: HyperV

SYSTEM_PRODUCT_NAME: Virtual Machine

SYSTEM_VERSION: 7.0

BIOS_VENDOR: American Megatrends Inc.

BIOS_VERSION: 090006

BIOS_DATE: 05/23/2012

BASEBOARD_MANUFACTURER: Microsoft Corporation

BASEBOARD_PRODUCT: Virtual Machine

BASEBOARD_VERSION: 7.0

DUMP_TYPE: 1

BUGCHECK_P1: c0000005

BUGCHECK_P2: fffff801a502e6c4

BUGCHECK_P3: ffff96018de6bc90

BUGCHECK_P4: 0

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced
memory at 0x%p. The memory could not be %s.

FAULTING_IP:
nt!RtlEqualSid+4
fffff801`a502e6c4 0fb702 movzx eax,word ptr [rdx]

CONTEXT: ffff96018de6bc90 – (.cxr 0xffff96018de6bc90)
rax=0000000000000000 rbx=0000000000000000 rcx=ffffdf8f26ccc5a0
rdx=0000000000000000 rsi=8000000000000000 rdi=000000000000136c
rip=fffff801a502e6c4 rsp=ffff96018de6c6a0 rbp=ffffc0896a426b00
r8=0000000000000000 r9=0000000000000000 r10=0000000000000801
r11=ffffdf8f26ccc5a0 r12=ffffc0896a1fec30 r13=0000000000000001
r14=ffffdf8f281088e0 r15=ffffc0896a7148e0
iopl=0 nv up ei ng nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b
efl=00010286
nt!RtlEqualSid+0x4:
fffff801a502e6c4 0fb702 movzx eax,word ptr [rdx] ds:002b:0000000000000000=???
Resetting default scope

CPU_COUNT: 2

CPU_MHZ: a25

CPU_VENDOR: GenuineIntel

CPU_FAMILY: 6

CPU_MODEL: 3f

CPU_STEPPING: 2

CPU_MICROCODE: 6,3f,2,0 (F,M,S,R) SIG: FFFFFFFF’00000000 (cache)
FFFFFFFF’00000000 (init)

DEFAULT_BUCKET_ID: WIN8_DRIVER_FAULT

BUGCHECK_STR: 0x3B

PROCESS_NAME: explorer.exe

CURRENT_IRQL: 0

ANALYSIS_SESSION_HOST: GEDDEV

ANALYSIS_SESSION_TIME: 09-28-2016 20:03:44.0053

ANALYSIS_VERSION: 10.0.10586.567 amd64fre

LAST_CONTROL_TRANSFER: from fffff803ffdc220a to fffff801a502e6c4

STACK_TEXT:
ffff96018de6c6a0 fffff803ffdc220a : 0000000000000000 ffffdf8f26ccc500
0000000000000000 ffffdf8f27ad1788 : nt!RtlEqualSid+0x4
ffff96018de6c6d0 fffff803ffdd4c67 : ffffc0896a1fec30 0000000000001af0
ffffc0896a426b00 ffffc0896a426b00 :
AMFileSystemFilter!HandleOwnershipCheckOnWrite+0x13a
[f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste
mfilter\callbacks.c @ 1560]
ffff96018de6c770 fffff803fd1246ca : ffff96018de6c899 ffffc0896a861d78
ffffc0896a426b00 ffffc0896a670750 : AMFileSystemFilter!AmFltPreWrite+0x167
[f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste
mfilter\callbacks.c @ 869]
ffff96018de6c7f0 fffff803fd124278 : ffff96018de6c9d0 ffffc08968935900
0000000000000004 ffffc08968935100 : FLTMGR!FltpPerformPreCallbacks+0x2ea
ffff96018de6c900 fffff803fd123386 : 0000000000000000 ffff96018de6c9d0
ffffc08969b98d30 ffff96018de6c9e0 : FLTMGR!FltpPassThroughInternal+0x88
ffff96018de6c930 fffff803fd12312e : 0000000000000005 ffffc08900000002
0000000000000001 fffff801a5031649 : FLTMGR!FltpPassThrough+0x1a6
ffff96018de6c9b0 fffff801a5418a20 : ffffc0896a7148e0 ffff96018de6ccc0
0000000000000000 0000000000000000 : FLTMGR!FltpDispatch+0x9e
ffff96018de6ca10 fffff801a5474e2b : ffffc08900000000 0000000000000004
ffffc0896a714930 ffff96018de6ccc0 : nt!IopSynchronousServiceTail+0x1a0
ffff96018de6cad0 fffff801a516cc93 : 0000000000002abc 000000000000262d
0000000000000000 0000000000000000 : nt!NtWriteFile+0x65b
ffff96018de6cbd0 00007ff8b59b4f54 : 0000000000000000 0000000000000000
0000000000000000 0000000000000000 : nt!KiSystemServiceCopyEnd+0x13
000000000f66c568 0000000000000000 : 0000000000000000 0000000000000000
0000000000000000 0000000000000000 : 0x00007ff8`b59b4f54

THREAD_SHA1_HASH_MOD_FUNC: 5daf6bb4d841c1bb826e13008994673ebee94b3c

THREAD_SHA1_HASH_MOD_FUNC_OFFSET: 81557dfec777cf317bab0c4775d37f52cea84ccb

THREAD_SHA1_HASH_MOD: 782c37f088241155cf3319e3530803a25354b1ad

FOLLOWUP_IP:
AMFileSystemFilter!HandleOwnershipCheckOnWrite+13a
[f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesyste
mfilter\callbacks.c @ 1560]
fffff803`ffdc220a 32c9 xor cl,cl

FAULT_INSTR_CODE: 53bac932

FAULTING_SOURCE_LINE:
f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesystem
filter\callbacks.c

FAULTING_SOURCE_FILE:
f:\builds\211\am\f_vnext_full\sources\am\source\amfilterdrivers\amfilesystem
filter\callbacks.c

FAULTING_SOURCE_LINE_NUMBER: 1560

SYMBOL_STACK_INDEX: 1

SYMBOL_NAME: AMFileSystemFilter!HandleOwnershipCheckOnWrite+13a

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: AMFileSystemFilter

IMAGE_NAME: AMFileSystemFilter.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 57d30540

STACK_COMMAND: .cxr 0xffff96018de6bc90 ; kb

BUCKET_ID_FUNC_OFFSET: 13a

FAILURE_BUCKET_ID: 0x3B_AMFileSystemFilter!HandleOwnershipCheckOnWrite

BUCKET_ID: 0x3B_AMFileSystemFilter!HandleOwnershipCheckOnWrite

PRIMARY_PROBLEM_CLASS: 0x3B_AMFileSystemFilter!HandleOwnershipCheckOnWrite

TARGET_TIME: 2016-09-19T14:23:51.000Z

OSBUILD: 14393

OSSERVICEPACK: 0

SERVICEPACK_NUMBER: 0

OS_REVISION: 0

SUITE_MASK: 272

PRODUCT_TYPE: 1

OSPLATFORM_TYPE: x64

OSNAME: Windows 10

OSEDITION: Windows 10 WinNt TerminalServer SingleUserTS

OS_LOCALE:

USER_LCID: 0

OSBUILD_TIMESTAMP: 2016-07-16 03:16:17

BUILDDATESTAMP_STR: 160715-1616

BUILDLAB_STR: rs1_release

BUILDOSVER_STR: 10.0.14393.0.amd64fre.rs1_release.160715-1616

ANALYSIS_SESSION_ELAPSED_TIME: 460b

ANALYSIS_SOURCE: KM

FAILURE_ID_HASH_STRING:
km:0x3b_amfilesystemfilter!handleownershipcheckonwrite

FAILURE_ID_HASH: {4dbccc59-e9d5-d1ef-4a6e-aa28e21f1eb2}

Followup: MachineOwner

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@gmail.com
Sent: 28 September 2016 18:03
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Understanding an x64 bugcheck

>I’ve already shown that SeQueryInformationToken modifies it (RDX) and
doesn’t seem to restore it correctly.

RDX is volatile and need not be restored.

>// Looking inside SeQueryInformationToken shows that rbx is saved to rsp+18
and restored from rsp+140??

If SeQueryInformationToken does not restore RBX than the OS would fail well
before your driver loads. Don’t you think ?

Note that the Release version of your code is not protected by the
FLT_ASSERT(FileOwner) macro call.

The PUCA_FILE_CONTEXT type is probably a type of your own, isn’t it ? Where
do you get the PSID from ?

And post the !analyze -v output.


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

Both RDX and RBX are null.

So how do you fill the PUCA_FILE_CONTEXT typed structure ?

Where does the PSID come from ?

> Both RDX and RBX are null.
Yes I know, the question is, why is RBX null when it had a valid memory
address moved into it at the start of the function.

Memory address is
1: kd> ?? FileContext->FileOwner
void * 0xffffdf8f280c1a60 1: kd\> !sid 0xffffdf8f280c1a60
SID is: S-1-5-18
1: kd> !pool 0xffffdf8f`280c1a60 1

*ffffdf8f280c1a50 size: 20 previous size: 90 (Allocated) *AMft
ffffdf8f280c1a60 00000101 05000000 00000012 00000000

As I say, the memory is still valid at the time of the bugcheck, but RBX no
longer contains that memory address
I’m beginning to wonder if this is a compiler error. We’re running a new
build through our test environments today to see if we can reproduce it, but
I really want to understand what’s gone wrong here. This code has been
running in millions of daily tests in our automation suite for the last 3
months without any problems, which is why I’m a little perplexed as to why
it’s suddenly failed and in such a way that’s not making any sense (to me)

So how do you fill the PUCA_FILE_CONTEXT typed structure ?
Where does the PSID come from ?

Standard method:
Get the file owner with FltQuerySecurityObject/RtlGetOwnerSecurityDescriptor
Get the length of the sid and allocate some paged pool to hold a copy of it.
Copy the sid to the new memory. This is the memory that’s stored in
UCA_FILE_CONTEXT::FileOwner
(see below for code)

NTSTATUS
UcaLibGetFileOwnerSid(In PFLT_INSTANCE Instance,
In PFILE_OBJECT FileObject,
Out PSID *OwnerSid)
{
PSECURITY_DESCRIPTOR SecurityDescriptor;
BOOLEAN OwnerDefaulted;
ULONG LengthNeeded;
ULONG SidLength;
PSID Owner;
NTSTATUS Status;

PAGED_CODE();

/* Get the required buffer to hold the file security descriptor */
Status = FltQuerySecurityObject(Instance,
FileObject,
OWNER_SECURITY_INFORMATION,
NULL,
0,
&LengthNeeded);
if (Status != STATUS_BUFFER_TOO_SMALL)
{
if (FlagOn(FileObject->Flags, FO_VOLUME_OPEN))
{
TRACE_ERROR(TraceHandle, “Failed to lookup the volume owner :
0x%X”, Status);
}
return Status;
}

/* Allocate the buffer */
SecurityDescriptor = ExAllocatePoolWithTag(PagedPool, LengthNeeded,
AM_POOL_TAG);
if (SecurityDescriptor == NULL) return STATUS_INSUFFICIENT_RESOURCES;

/* Now get the security descriptor for the file */
Status = FltQuerySecurityObject(Instance,
FileObject,
OWNER_SECURITY_INFORMATION,
SecurityDescriptor,
LengthNeeded,
&LengthNeeded);
if (NT_SUCCESS(Status))
{
/* Lookup the owner sid in the security descriptor */
Status = RtlGetOwnerSecurityDescriptor(SecurityDescriptor,
&Owner,
&OwnerDefaulted);
if (NT_SUCCESS(Status))
{
/* Get the length of the owner sid */
SidLength = RtlLengthSid(Owner);

/* Allocate some memory to hold a copy of the owner sid */
*OwnerSid = ExAllocatePoolWithTag(PagedPool, SidLength,
AM_POOL_TAG);
if (*OwnerSid)
{
/* Copy the sid to the caller buffer */
RtlCopySid(SidLength, *OwnerSid, Owner);
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
}

/* Cleanup the buffer */
ExFreePoolWithTag(SecurityDescriptor, AM_POOL_TAG);

return Status;
}

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@gmail.com
Sent: 29 September 2016 09:32
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] Understanding an x64 bugcheck

Both RDX and RBX are null.

So how do you fill the PUCA_FILE_CONTEXT typed structure ?

Where does the PSID come from ?


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

>I really want to understand what’s gone wrong here. This code has been

running in millions of daily tests in our automation suite for the last 3
months without any problems, which is why I’m a little perplexed as to why
it’s suddenly failed and in such a way that’s not making any sense (to me)

Do you mean to say this failed only once ? Have you considered the
possibility of a hardware failure ? Even if the system passes a RAM test
when it’s cold, memory errors can easily occur if a machine gets overheated.

//Daniel

The test engineer said it had failed a couple of times, but only in that one
virtual machine, which is one of many.
He also didn’t have any crash dumps from any of the previous crashes, so I
only had this one to go off and no way to check if any of the other crashes
had happened in the same way.

Sorry, I should have been more clear on that point from the start.

I did consider the possibility of a VM failure, but as that’s normally as
unlikely as finding a compiler issue or a bug in the kernel, I try to avoid
pointing fingers at such things until I fully understand the cause. I guess
this could be one of those rare cases, as the machine state is currently
making no sense to me.

Does anyone ever see VM / hyper-v errors where registers can contain invalid
data? It’s certainly a new one for me if this is the case.

Thanks,
Ged.

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of
xxxxx@resplendence.com
Sent: 29 September 2016 11:29
To: Windows System Software Devs Interest List
Subject: Re:[ntdev] Understanding an x64 bugcheck

>I really want to understand what’s gone wrong here. This code has been
>running in millions of daily tests in our automation suite for the last 3
>months without any problems, which is why I’m a little perplexed as to why
>it’s suddenly failed and in such a way that’s not making any sense (to me)

Do you mean to say this failed only once ? Have you considered the
possibility of a hardware failure ? Even if the system passes a RAM test
when it’s cold, memory errors can easily occur if a machine gets overheated.

//Daniel


NTDEV is sponsored by OSR

Visit the list online at:
http:

MONTHLY seminars on crash dump analysis, WDF, Windows internals and software
drivers!
Details at http:

To unsubscribe, visit the List Server section of OSR Online at
http:</http:></http:></http:>

Verify that you check for STATUS_PENDING for all functions that might return it and wait for completion or do not allocate variables for asynchronous IO returned data on a stack. It might happen that this is a case of asynchronous IO completion that saves operation status of STATUS_SUCCESS ( which is 0 ) in a stack allocated variable. The function that allocated this variable already returned and the stack is reused to save thread context ( including the registers ) by scheduler.

Post the entire disas of the function that calls RtlEqualSid and we’ll see.

Add logging capabilities to the driver and log an event when PSID is NULL.

The RtlLengthSid doc states that the returned value is undefined if the passed SID is not valid. That’s why any call to RtlLengthSid should be preceeded by a call to RtlValidSid.