/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 ); }