windows-nt/Source/XPSP1/NT/com/rpc/ndr20/misc.cxx
2020-09-26 16:20:57 +08:00

843 lines
23 KiB
C++

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Copyright (c) 1993 Microsoft Corporation
Module Name :
misc.c
Abstract :
Contains miscelaneous helper routines.
Author :
David Kays dkays December 1993.
Revision History :
---------------------------------------------------------------------*/
#include "ndrp.h"
#include "attack.h"
uchar *
NdrpMemoryIncrement(
PMIDL_STUB_MESSAGE pStubMsg,
uchar * pMemory,
PFORMAT_STRING pFormat
)
/*++
Routine Description :
Returns a memory pointer incremeted past a complex data type. This routine
is also overloaded to compute the size of a complex data type by passing
a 0 memory pointer.
Arguments :
pStubMsg - Pointer to the stub message.
pMemory - Pointer to the complex type, or 0 if a size is being computed.
pFormat - Format string description.
Return :
A memory pointer incremented past the complex type. If a 0 memory pointer
was passed in then the returned value is the size of the complex type.
--*/
{
long Elements;
long ElementSize;
switch ( *pFormat )
{
//
// Structs
//
case FC_STRUCT :
case FC_PSTRUCT :
case FC_HARD_STRUCT :
pMemory += *((ushort *)(pFormat + 2));
break;
case FC_CSTRUCT :
case FC_CVSTRUCT :
case FC_CPSTRUCT :
pMemory += *((ushort *)(pFormat + 2));
// Get conformant array or string description.
pFormat += 4;
pFormat += *((signed short *)pFormat);
// This flag is set by the endianess pass only.
if ( ! IS_TOPMOST_CONF_STRUCT( pStubMsg->uFlags ) )
{
// When embedding in a bogus struct, array is accounted
// at the conf struct level, due to pointer layout.
// Get the memory pointer past the conformant array.
pMemory = NdrpMemoryIncrement( pStubMsg,
pMemory,
pFormat );
}
break;
case FC_BOGUS_STRUCT :
pMemory += *((ushort *)(pFormat + 2));
pFormat += 4;
// Check for a conformant array or string in the struct.
if ( *((signed short *)pFormat) )
{
pFormat += *((signed short *)pFormat);
if ( !IS_EMBED_CONF_STRUCT( pStubMsg->uFlags ) &&
! IS_CONF_ARRAY_DONE( pStubMsg->uFlags ) )
{
// Get the memory pointer past the conformant array.
pMemory = NdrpMemoryIncrement( pStubMsg,
pMemory,
pFormat );
}
}
break;
//
// Unions
//
case FC_ENCAPSULATED_UNION :
pMemory += HIGH_NIBBLE(pFormat[1]);
pMemory += *((ushort *)(pFormat + 2));
break;
case FC_NON_ENCAPSULATED_UNION :
// Go to the size/arm description.
pFormat += 6;
CORRELATION_DESC_INCREMENT( pFormat );
pFormat += *((signed short *)pFormat);
pMemory += *((ushort *)pFormat);
break;
//
// Arrays
//
case FC_SMFARRAY :
case FC_SMVARRAY :
pMemory += *((ushort *)(pFormat + 2));
break;
case FC_LGFARRAY :
case FC_LGVARRAY :
pMemory += *((ulong UNALIGNED *)(pFormat + 2));
break;
case FC_CARRAY:
case FC_CVARRAY:
{
ULONG_PTR ConfSize = NdrpComputeConformance( pStubMsg,
pMemory,
pFormat);
// check for possible mulitplication overflow attack here.
pMemory += MultiplyWithOverflowCheck( ConfSize, *((ushort *)(pFormat + 2)) );
}
break;
case FC_BOGUS_ARRAY :
{
PARRAY_INFO pArrayInfo = pStubMsg->pArrayInfo;
if ( *((long UNALIGNED *)(pFormat + 4)) == 0xffffffff )
Elements = *((ushort *)(pFormat + 2));
else
{
if ( pArrayInfo &&
pArrayInfo->MaxCountArray &&
(pArrayInfo->MaxCountArray ==
pArrayInfo->BufferConformanceMark) )
{
Elements = pArrayInfo->MaxCountArray[pArrayInfo->Dimension];
}
else
{
Elements = (ulong) NdrpComputeConformance( pStubMsg,
pMemory,
pFormat );
}
}
// Go to the array element's description.
pFormat += 12;
CORRELATION_DESC_INCREMENT( pFormat );
CORRELATION_DESC_INCREMENT( pFormat );
//
// Get the size of one element.
//
switch ( *pFormat )
{
case FC_ENUM16 :
ElementSize = sizeof(int);
break;
case FC_RP :
case FC_UP :
case FC_FP :
case FC_OP :
ElementSize = PTR_MEM_SIZE;
break;
case FC_EMBEDDED_COMPLEX :
//
// It's some complicated thingy.
//
pFormat += 2;
pFormat += *((signed short *)pFormat);
if ( (*pFormat == FC_TRANSMIT_AS) ||
(*pFormat == FC_REPRESENT_AS) ||
(*pFormat == FC_USER_MARSHAL) )
{
//
// Get the presented type size.
//
ElementSize = *((ushort *)(pFormat + 4));
}
else
{
if ( pArrayInfo )
pArrayInfo->Dimension++;
ElementSize = (long)
( NdrpMemoryIncrement( pStubMsg,
pMemory,
pFormat ) - pMemory );
if ( pArrayInfo )
pArrayInfo->Dimension--;
}
break;
case FC_RANGE:
ElementSize = SIMPLE_TYPE_MEMSIZE( (pFormat[1] & 0x0f) );
break;
default :
if ( IS_SIMPLE_TYPE(*pFormat) )
{
ElementSize = SIMPLE_TYPE_MEMSIZE(*pFormat);
break;
}
NDR_ASSERT(0,"NdrpMemoryIncrement : bad format char");
RpcRaiseException( RPC_S_INTERNAL_ERROR );
return 0;
}
// check for possible mulitplication overflow attack here.
pMemory += MultiplyWithOverflowCheck( Elements, ElementSize );
}
break;
//
// String arrays (a.k.a. non-conformant strings).
//
case FC_CSTRING :
case FC_BSTRING :
case FC_WSTRING :
{
ULONG ElementSize = (*pFormat == FC_WSTRING) ? sizeof(wchar_t) : sizeof(char) ;
ULONG Elements = *((ushort *)(pFormat + 2)) ;
// check for possible mulitplication overflow attack here.
pMemory += MultiplyWithOverflowCheck( Elements, ElementSize );
}
break;
case FC_SSTRING :
// check for possible mulitplication overflow attack here.
pMemory += MultiplyWithOverflowCheck( pFormat[1], *((ushort *)(pFormat + 2) ) );
break;
//
// Sized conformant strings.
//
case FC_C_CSTRING:
case FC_C_BSTRING:
case FC_C_WSTRING:
{
PARRAY_INFO pArrayInfo = pStubMsg->pArrayInfo;
ULONG ElementSize = (*pFormat == FC_C_WSTRING) ?
sizeof(wchar_t) :
sizeof(char);
NDR_ASSERT(pFormat[1] == FC_STRING_SIZED,
"NdrpMemoryIncrement : called for non-sized string");
if ( pArrayInfo &&
pArrayInfo->MaxCountArray &&
(pArrayInfo->MaxCountArray ==
pArrayInfo->BufferConformanceMark) )
{
Elements = pArrayInfo->MaxCountArray[pArrayInfo->Dimension];
}
else
{
Elements = (ulong) NdrpComputeConformance( pStubMsg,
pMemory,
pFormat );
}
// check for possible mulitplication overflow attack here.
pMemory += MultiplyWithOverflowCheck( Elements, ElementSize );
}
break;
case FC_C_SSTRING:
{
PARRAY_INFO pArrayInfo = pStubMsg->pArrayInfo;
if ( pArrayInfo &&
pArrayInfo->MaxCountArray &&
(pArrayInfo->MaxCountArray ==
pArrayInfo->BufferConformanceMark) )
{
Elements = pArrayInfo->MaxCountArray[pArrayInfo->Dimension];
}
else
{
Elements = (ulong) NdrpComputeConformance( pStubMsg,
pMemory,
pFormat );
}
// check for possible mulitplication overflow attack here.
pMemory += MultiplyWithOverflowCheck( Elements, pFormat[1] );
}
break;
//
// Transmit as, represent as, user marshal
//
case FC_TRANSMIT_AS :
case FC_REPRESENT_AS :
case FC_USER_MARSHAL :
// Get the presented type size.
pMemory += *((ushort *)(pFormat + 4));
break;
case FC_BYTE_COUNT_POINTER:
//
// Should only hit this case when called from NdrSrvOutInit().
// In this case it's the total byte count allocation size we're
// looking for.
//
// We are taking the larger of conformant size and the real data
// type size, otherwise, we might give user a partially invalid
// buffer, and that'll come back to bite us during sizing/marshalling
{
uchar * pMemory1 = pMemory;
uchar * pMemory2 = pMemory;
PFORMAT_STRING pFormat2 = pFormat;
if ( pFormat[1] != FC_PAD )
{
pMemory2 += SIMPLE_TYPE_MEMSIZE( pFormat[1] );
}
else
{
// go pass the conformance & get to the real type
pFormat2 += 6;
CORRELATION_DESC_INCREMENT( pFormat2 );
pFormat2 += *((signed short *)pFormat2);
pMemory2 = NdrpMemoryIncrement( pStubMsg,
pMemory2,
pFormat2 );
}
pMemory1 += NdrpComputeConformance( pStubMsg,
pMemory1,
pFormat );
pMemory = ( pMemory1 > pMemory2 )? pMemory1 : pMemory2;
break;
}
case FC_IP :
pMemory += PTR_MEM_SIZE;
break;
case FC_RANGE:
pMemory += SIMPLE_TYPE_MEMSIZE( (pFormat[1] & 0x0f) );
break;
#ifdef _CS_CHAR_
case FC_CSARRAY:
{
uchar * OldBuffer = pStubMsg->Buffer;
ulong OldSize = pStubMsg->MemorySize;
pStubMsg->MemorySize = 0;
pMemory += NdrCsArrayMemorySize( pStubMsg, pFormat );
pStubMsg->MemorySize = OldSize;
pStubMsg->Buffer = OldBuffer;
break;
}
case FC_CS_TAG:
pMemory += sizeof( ulong );
break;
#endif _CS_CHAR_
default :
NDR_ASSERT(0,"NdrpMemoryIncrement : bad format char");
RpcRaiseException( RPC_S_INTERNAL_ERROR );
return 0;
}
return pMemory;
}
long
NdrpArrayDimensions(
PMIDL_STUB_MESSAGE pStubMsg,
PFORMAT_STRING pFormat,
BOOL fIgnoreStringArrays
)
/*++
Routine description :
This routine determines the number of dimensions in a complex array, which
is the only array type which is allowed to have multiple dimensions other
than an array of multiple fixed dimensions.
Arguments :
pFormat - Complex array's format string description.
fIgnoreStringArrays - TRUE if a string array should not be counted as
a dimension, FALSE if it should.
Return :
The number of dimensions in the array.
--*/
{
long Dimensions;
//
// Only a complex array can have multiple dimensions.
//
if ( *pFormat != FC_BOGUS_ARRAY )
return 1;
Dimensions = 1;
pFormat += 12;
CORRELATION_DESC_INCREMENT( pFormat );
CORRELATION_DESC_INCREMENT( pFormat );
for ( ; *pFormat == FC_EMBEDDED_COMPLEX; )
{
pFormat += 2;
pFormat += *((signed short *)pFormat);
//
// Search for a fixed, complex, or string array.
//
switch ( *pFormat )
{
case FC_SMFARRAY :
pFormat += 4;
break;
case FC_LGFARRAY :
pFormat += 6;
break;
case FC_BOGUS_ARRAY :
pFormat += 12;
CORRELATION_DESC_INCREMENT( pFormat );
CORRELATION_DESC_INCREMENT( pFormat );
break;
case FC_CSTRING :
case FC_BSTRING :
case FC_WSTRING :
case FC_SSTRING :
case FC_C_CSTRING :
case FC_C_BSTRING :
case FC_C_WSTRING :
case FC_C_SSTRING :
//
// Can't have any more dimensions after a string array.
//
return fIgnoreStringArrays ? Dimensions : Dimensions + 1;
default :
return Dimensions;
}
Dimensions++;
}
//
// Get here if you have only one dimension.
//
return Dimensions;
}
long
NdrpArrayElements(
PMIDL_STUB_MESSAGE pStubMsg,
uchar * pMemory,
PFORMAT_STRING pFormat
)
/*++
Routine description :
This routine determines the number of elements (allocation size) in an
array. Used to handle multidimensional arrays.
Arguments :
pStubMsg - The stub message.
pMemory - The array.
pFormat - Array's format string description.
Return :
The number of elements in the array.
--*/
{
long TotalSize;
long ElementSize;
switch ( *pFormat )
{
case FC_SMFARRAY :
TotalSize = (long) *((ushort *)(pFormat + 2));
pFormat += 4;
break;
case FC_LGFARRAY :
TotalSize = *((long UNALIGNED *)(pFormat + 2));
pFormat += 6;
break;
case FC_SMVARRAY :
return (long) *((ushort *)(pFormat + 4));
case FC_LGVARRAY :
return *((long UNALIGNED *)(pFormat + 6));
case FC_BOGUS_ARRAY :
if ( *((long *)(pFormat + 4)) == 0xffffffff )
return (long) *((ushort *)(pFormat + 2));
// else fall through
case FC_CARRAY :
case FC_CVARRAY :
return (ulong) NdrpComputeConformance( pStubMsg,
pMemory,
pFormat );
default :
NDR_ASSERT(0,"NdrpArrayElements : bad format char");
RpcRaiseException( RPC_S_INTERNAL_ERROR );
return 0;
}
//
// We get here for a non-complex fixed array.
//
// Since a fixed array's format string description does not
// contain the number of elements in the array, we have to compute
// it by computing the array's element size and dividing this into
// the total array size.
//
// A fixed array's child can only be a nice struct, another
// fixed array, a pointer, or a simple type.
//
//
// Skip pointer layout if one is present.
//
if ( *pFormat == FC_PP )
pFormat = NdrpSkipPointerLayout( pFormat );
switch ( *pFormat )
{
case FC_EMBEDDED_COMPLEX :
pFormat += 2;
pFormat += *((signed short *)pFormat);
//
// We must be at FC_STRUCT, FC_PSTRUCT, FC_SMFARRAY, or FC_LGFARRAY.
// All these have the total size as a short at 2 bytes past the
// beginning of the description except for large fixed array.
//
if ( *pFormat != FC_LGFARRAY )
ElementSize = (long) *((ushort *)(pFormat + 2));
else
ElementSize = *((long UNALIGNED *)(pFormat + 2));
break;
//
// Simple type (enum16 not possible).
//
default :
ElementSize = SIMPLE_TYPE_MEMSIZE( *pFormat );
break;
}
return TotalSize / ElementSize;
}
void
NdrpArrayVariance(
PMIDL_STUB_MESSAGE pStubMsg,
uchar * pMemory,
PFORMAT_STRING pFormat,
long * pOffset,
long * pLength
)
/*++
Routine description :
This routine determines the offset and length values for an array.
Used to handle multidimensional arrays.
Arguments :
pStubMsg - The stub message.
pMemory - The array.
pFormat - Array's format string description.
pOffset - Returned offset value.
pLength - Returned length value.
Return :
None.
--*/
{
switch ( *pFormat )
{
case FC_SMFARRAY :
case FC_LGFARRAY :
case FC_CARRAY :
*pOffset = 0;
*pLength = NdrpArrayElements( pStubMsg,
pMemory,
pFormat );
break;
case FC_BOGUS_ARRAY :
{
PFORMAT_STRING pFormatBAV = pFormat + 8;
CORRELATION_DESC_INCREMENT( pFormatBAV );
if ( *((long UNALIGNED *)(pFormatBAV + 8)) == 0xffffffff )
{
*pOffset = 0;
*pLength = NdrpArrayElements( pStubMsg,
pMemory,
pFormat );
return;
}
}
// else fall through
case FC_CVARRAY :
case FC_SMVARRAY :
case FC_LGVARRAY :
NdrpComputeVariance( pStubMsg,
pMemory,
pFormat );
*pOffset = (long) pStubMsg->Offset;
*pLength = (long) pStubMsg->ActualCount;
break;
default :
NDR_ASSERT(0,"NdrpArrayVariance : bad format char");
RpcRaiseException( RPC_S_INTERNAL_ERROR );
return;
}
}
PFORMAT_STRING
NdrpSkipPointerLayout(
PFORMAT_STRING pFormat
)
/*--
RoutineDescription :
Skips a pointer layout format string description.
Arguments :
pFormat - Format string pointer layout description. Must currently
point to the FC_PP beginning the pointer layout.
Return :
Format string pointer past the pointer layout.
--*/
{
long Pointers;
NDR_ASSERT( *pFormat == FC_PP,
"NdrpSkipPointerLayout : format string not at FC_PP" );
// Skip FC_PP and FC_PAD.
pFormat += 2;
for (;;)
{
switch ( *pFormat )
{
case FC_END :
return pFormat + 1;
case FC_NO_REPEAT :
pFormat += 10;
break;
case FC_FIXED_REPEAT :
pFormat += 2;
// fall through...
case FC_VARIABLE_REPEAT :
pFormat += 6;
Pointers = *((ushort * &)pFormat)++;
pFormat += Pointers * 8;
break;
default :
NDR_ASSERT( 0, "NdrpSkipPointerLayout : bad format char" );
RpcRaiseException( RPC_S_INTERNAL_ERROR );
return 0;
}
}
}
long
NdrpStringStructLen(
uchar * pMemory,
long ElementSize
)
/*--
RoutineDescription :
Determines a stringable struct's length.
Arguments :
pMemory - Pointer to stringable struct.
ElementSize - Number of bytes of each string element.
Return :
Length of string.
--*/
{
long Length;
uchar Buffer[256];
// Note : String struct element size is limited to 256 bytes.
MIDL_memset( Buffer, 0, 256 );
for ( Length = 0; ; Length++ )
{
if ( memcmp( pMemory, Buffer, ElementSize ) == 0 )
break;
pMemory += ElementSize;
}
return Length;
}
void
NdrpCheckBound(
ulong Bound,
int Type
)
{
ulong Mask;
switch ( Type )
{
case FC_ULONG :
#if defined(__RPC_WIN64__)
case FC_UINT3264 :
case FC_INT3264 :
#endif
//
// We use a mask here which will raise an exception for counts
// of 2GB or more since this is the max NT allocation size.
//
Mask = 0x80000000UL;
break;
case FC_LONG :
Mask = 0x80000000UL;
break;
case FC_USHORT :
Mask = 0xffff0000UL;
break;
case FC_SHORT :
Mask = 0xffff8000UL;
break;
case FC_USMALL :
Mask = 0xffffff00UL;
break;
case FC_SMALL :
Mask = 0xffffff80UL;
break;
case 0 :
//
// For variance which requires calling an auxilary expression
// evaluation routine a type is not emitted in the format string.
// We have to just give up.
//
// The same thing happens with constant conformance type
// we emit zero on the var type nibble.
//
return;
default :
NDR_ASSERT( 0, "NdrpCheckBound : bad type" );
RpcRaiseException( RPC_S_INTERNAL_ERROR );
return;
}
if ( Bound & Mask )
RpcRaiseException( RPC_X_INVALID_BOUND );
}