//--------------------------------------------------------------------------- // // Module: validate.cpp // // Description: // // //@@BEGIN_MSINTERNAL // Development Team: // Alper Selcuk // // History: Date Author Comment // 02/28/02 AlperS Created // // To Do: Date Author Comment // //@@END_MSINTERNAL // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 2002-2002 Microsoft Corporation. All Rights Reserved. // //--------------------------------------------------------------------------- #include "common.h" DEFINE_KSPROPERTY_TABLE(AudioPropertyValidationHandlers) { DEFINE_KSPROPERTY_ITEM( KSPROPERTY_AUDIO_QUALITY, // idProperty NULL, // pfnGetHandler sizeof(KSPROPERTY), // cbMinGetPropertyInput sizeof(ULONG), // cbMinGetDataInput SadValidateAudioQuality, // pfnSetHandler NULL, // Values 0, // RelationsCount NULL, // Relations NULL, // SupportHandler 0 // SerializedSize ), DEFINE_KSPROPERTY_ITEM( KSPROPERTY_AUDIO_MIX_LEVEL_CAPS, // idProperty NULL, // pfnGetHandler sizeof(KSPROPERTY), // cbMinGetPropertyInput sizeof(ULONG) + sizeof(ULONG), // cbMinGetDataInput SadValidateAudioMixLevelCaps, // pfnSetHandler NULL, // Values 0, // RelationsCount NULL, // Relations NULL, // SupportHandler 0 // SerializedSize ), DEFINE_KSPROPERTY_ITEM( KSPROPERTY_AUDIO_STEREO_ENHANCE, // idProperty NULL, // pfnGetHandler sizeof(KSPROPERTY), // cbMinGetPropertyInput sizeof(KSAUDIO_STEREO_ENHANCE), // cbMinGetDataInput SadValidateAudioStereoEnhance, // pfnSetHandler NULL, // Values 0, // RelationsCount NULL, // Relations NULL, // SupportHandler 0 // SerializedSize ), DEFINE_KSPROPERTY_ITEM( KSPROPERTY_AUDIO_PREFERRED_STATUS, // idProperty NULL, // pfnGetHandler sizeof(KSPROPERTY), // cbMinGetPropertyInput sizeof(KSAUDIO_PREFERRED_STATUS), // cbMinGetDataInput SadValidateAudioPreferredStatus, // pfnSetHandler NULL, // Values 0, // RelationsCount NULL, // Relations NULL, // SupportHandler 0 // SerializedSize ) }; DEFINE_KSPROPERTY_TABLE(PinConnectionValidationHandlers) { DEFINE_KSPROPERTY_ITEM( KSPROPERTY_CONNECTION_STATE, // idProperty NULL, // pfnGetHandler sizeof(KSPROPERTY), // cbMinGetPropertyInput sizeof(ULONG), // cbMinGetDataInput SadValidateConnectionState, // pfnSetHandler NULL, // Values 0, // RelationsCount NULL, // Relations NULL, // SupportHandler 0 // SerializedSize ), DEFINE_KSPROPERTY_ITEM( KSPROPERTY_CONNECTION_DATAFORMAT, // idProperty SadValidateDataFormat, // pfnGetHandler sizeof(KSPROPERTY), // cbMinGetPropertyInput sizeof(KSDATAFORMAT_WAVEFORMATEX), // cbMinGetDataInput SadValidateDataFormat, // pfnSetHandler NULL, // Values 0, // RelationsCount NULL, // Relations NULL, // SupportHandler 0 // SerializedSize ) }; DEFINE_KSPROPERTY_SET_TABLE(ValidationPropertySet) { DEFINE_KSPROPERTY_SET( &KSPROPSETID_Audio, // Set SIZEOF_ARRAY(AudioPropertyValidationHandlers), // PropertiesCount AudioPropertyValidationHandlers, // PropertyItem 0, // FastIoCount NULL // FastIoTable ), DEFINE_KSPROPERTY_SET( &KSPROPSETID_Connection, // Set SIZEOF_ARRAY(PinConnectionValidationHandlers), // PropertiesCount PinConnectionValidationHandlers, // PropertyItem 0, // FastIoCount NULL // FastIoTable ) }; //=========================================================================== // // Validates the integrity of KSDATAFORMAT structure for AUDIO. // Assumptions: // - pDataFormat is totally trusted. It has been probed and buffered // properly. // - This function should only be called if MajorFormat is AUDIO. // NTSTATUS ValidateAudioDataFormats( PKSDATAFORMAT pDataFormat ) { NTSTATUS ntStatus = STATUS_SUCCESS; ULONG cbAudioFormat; PWAVEFORMATEX pWaveFormatEx; ASSERT(pDataFormat); ASSERT(IsEqualGUID(&pDataFormat->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO)); // // We only support two specifiers in audio land. All the rest will be // accepted without further checks, because we don't know how to validate // them. // pWaveFormatEx = GetWaveFormatExFromKsDataFormat(pDataFormat, &cbAudioFormat); if (NULL == pWaveFormatEx) { DPF(5, "ValidataAudioDataFormats : invalid format specifier"); ntStatus = STATUS_SUCCESS; goto exit; } // // Make sure that we have enough space for the actual format packet. // Note that this will make sure we can at least touch the WAVEFORMATEX // part. // if (pDataFormat->FormatSize < cbAudioFormat) { DPF(10, "ValidataAudioDataFormats : format size does not match specifier"); ntStatus = STATUS_INVALID_PARAMETER; goto exit; } // // Check to see if WAVEFORMATEXTENSIBLE size is specified correctly. // if ((WAVE_FORMAT_EXTENSIBLE == pWaveFormatEx->wFormatTag) && (pWaveFormatEx->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))) { DPF1(4, "ValidataAudioDataFormats : WAVEFORMATEXTENSIBLE size does not match %d", pWaveFormatEx->cbSize); ntStatus = STATUS_INVALID_PARAMETER; goto exit; } // // Now that WaveFormatEx is guaranteed to be safe, check if we have extended // information in WAVEFORMATEX. // cbSize specifies the size of the extension structure. // Validate that FormatSize accomodates cbSize // Validate that cbSize does not cause an overflow. // if (pDataFormat->FormatSize < cbAudioFormat + pWaveFormatEx->cbSize || cbAudioFormat + pWaveFormatEx->cbSize < cbAudioFormat) { DPF1(10, "ValidataAudioDataFormats : format size does not match waveformatex.cbSize %d", pWaveFormatEx->cbSize); ntStatus = STATUS_INVALID_PARAMETER; goto exit; } // // Now we validated that the data buffer passed to us actually matches // with its specifier. // exit: return ntStatus; } // ValidateAudioDataFormats //=========================================================================== // // Validates the integrity of KSDATAFORMAT structure. // Calls ValidateAudioDataFormat if MajorFormat is audio. // Or checks the buffers size if Specifier is NONE. // Assumptions: // - pDataFormat is totally trusted. It has been probed and buffered // properly. // NTSTATUS ValidateDataFormat( PKSDATAFORMAT pDataFormat ) { NTSTATUS ntStatus = STATUS_SUCCESS; ASSERT(pDataFormat); if (IsEqualGUID(&pDataFormat->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO)) { ntStatus = ValidateAudioDataFormats(pDataFormat); } return ntStatus; } // ValidateDataFormat //=========================================================================== // // ValidateDeviceIoControl // // Probe Ioctl parameters by calling KS functions. // All the KS functions called here first probe the input and output buffers // and then copy them to Irp->SystemBuffer for further safe usage. // Calling these functions with NULL parameter basically means probe and copy // the buffers and return. // NTSTATUS ValidateDeviceIoControl( PIRP pIrp ) { NTSTATUS Status = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack; ASSERT(pIrp); pIrpStack = IoGetCurrentIrpStackLocation(pIrp); switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_KS_PROPERTY: Status = KsPropertyHandler( pIrp, SIZEOF_ARRAY(ValidationPropertySet), ValidationPropertySet); break; case IOCTL_KS_ENABLE_EVENT: Status = KsEnableEvent(pIrp, 0, NULL, NULL, KSEVENTS_NONE, NULL); break; case IOCTL_KS_METHOD: Status = KsMethodHandler(pIrp, 0, NULL); break; // // IOCTL_KS_DISABLE_EVENT // KsDisableEvent does not use and touch input parameters. // So input buffer validation is not necessary. // // // IOCTL_KS_RESET_STATE // The reset request only takes a ULONG. KsAcquireResetValue safely // extracts the RESET value from the IRP. // Sysaudio cannot do any validation here, because KsAcquireResetValue // works on Input buffer directly. // // // IOCTL_KS_WRITE_STREAM // IOCTL_KS_READ_STREAM // We are not doing any validation on these. // default: Status = STATUS_NOT_FOUND; } // // If there is no validation function ValidationPropertySet, Ks // will return one of these. Even in this case buffers must have // been probed and copied to kernel. // if (Status == STATUS_NOT_FOUND || Status == STATUS_PROPSET_NOT_FOUND) { Status = STATUS_SUCCESS; } if (!NT_SUCCESS(Status)) { DPF1(5, "Rejected DeviceIOCTL - %X", pIrpStack->Parameters.DeviceIoControl.IoControlCode); } return Status; } // ValidateDeviceIoControl //=========================================================================== // // Return TRUE if sysaudio is interested in handling this IoControlCode // Otherwise return FALSE. // BOOL IsSysaudioIoctlCode( ULONG IoControlCode ) { if (IOCTL_KS_PROPERTY == IoControlCode || IOCTL_KS_ENABLE_EVENT == IoControlCode || IOCTL_KS_DISABLE_EVENT == IoControlCode || IOCTL_KS_METHOD == IoControlCode) { return TRUE; } return FALSE; } // IsSysaudioIoctlCode //=========================================================================== // // SadValidateConnectionState // Check that the KSSTATE is valid. // NTSTATUS SadValidateConnectionState( IN PIRP pIrp, IN PKSPROPERTY pProperty, IN PKSSTATE pState ) { ASSERT(pState); if (KSSTATE_STOP <= *pState && KSSTATE_RUN >= *pState) { return STATUS_SUCCESS; } DPF1(5, "SadValidateConnectionState: Invalid State %d", *pState); return STATUS_INVALID_PARAMETER; } // SadValidateConnectionState //=========================================================================== // // SadValidateDataFormat // Checks whether the given format is valid. // NTSTATUS SadValidateDataFormat( IN PIRP pIrp, IN PKSPROPERTY pProperty, PKSDATAFORMAT pDataFormat ) { ASSERT(pDataFormat); return ValidateDataFormat(pDataFormat); } // SadValidateDataFormat //=========================================================================== // // SadValidateAudioQuality // Checks if the quality is valid. // NTSTATUS SadValidateAudioQuality( IN PIRP pIrp, IN PKSPROPERTY pProperty, IN PLONG pQuality ) { ASSERT(pQuality); if (KSAUDIO_QUALITY_WORST <= *pQuality && KSAUDIO_QUALITY_ADVANCED >= *pQuality) { return STATUS_SUCCESS; } DPF1(5, "SadValidateAudioQuality: Invalid Quality %d", *pQuality); return STATUS_INVALID_PARAMETER; } // SadValidateAudioQuality //=========================================================================== // // SadValidateAudioQuality // Checks if the structure is valid. // NTSTATUS SadValidateAudioMixLevelCaps( IN PIRP pIrp, IN PKSPROPERTY pProperty, IN OUT PVOID pVoid ) { ASSERT(pVoid); ASSERT(pIrp->AssociatedIrp.SystemBuffer); PKSAUDIO_MIXCAP_TABLE pMixTable; PIO_STACK_LOCATION pIrpStack; ULONG ulTotalChannels; ULONG cbRequiredSize; pIrpStack = IoGetCurrentIrpStackLocation(pIrp); pMixTable = (PKSAUDIO_MIXCAP_TABLE) pVoid; if (pMixTable->InputChannels > 10000 || pMixTable->OutputChannels > 10000) { DPF2(5, "SadValidateAudioMixLevelCaps: Huge Channel numbers %d %d", pMixTable->InputChannels, pMixTable->OutputChannels); return STATUS_INVALID_PARAMETER; } ulTotalChannels = pMixTable->InputChannels + pMixTable->OutputChannels; if (ulTotalChannels) { cbRequiredSize = sizeof(KSAUDIO_MIXCAP_TABLE) + ulTotalChannels * sizeof(KSAUDIO_MIX_CAPS); if (cbRequiredSize < pIrpStack->Parameters.DeviceIoControl.InputBufferLength) { DPF1(5, "SadValidateAudioMixLevelCaps: Buffer too small %d", pIrpStack->Parameters.DeviceIoControl.InputBufferLength); return STATUS_BUFFER_TOO_SMALL; } } return STATUS_SUCCESS; } // SadValidateAudioMixLevelCaps //=========================================================================== // // SadValidateAudioQuality // Checks if the Technique is valid. // NTSTATUS SadValidateAudioStereoEnhance( IN PIRP pIrp, IN PKSPROPERTY pProperty, IN PKSAUDIO_STEREO_ENHANCE pStereoEnhance ) { ASSERT(pStereoEnhance); if (SE_TECH_NONE <= pStereoEnhance->Technique && SE_TECH_VLSI_TECH >= pStereoEnhance->Technique) { return STATUS_SUCCESS; } DPF1(5, "SadValidateAudioStereoEnhance: Invalid Technique %d", pStereoEnhance->Technique); return STATUS_INVALID_PARAMETER; } // SadValidateAudioStereoEnhance //=========================================================================== // // SadValidateAudioQuality // Checks if the quality is valid. // NTSTATUS SadValidateAudioPreferredStatus( IN PIRP pIrp, IN PKSPROPERTY pProperty, IN PKSAUDIO_PREFERRED_STATUS pPreferredStatus ) { ASSERT(pPreferredStatus); if (KSPROPERTY_SYSAUDIO_NORMAL_DEFAULT <= pPreferredStatus->DeviceType && KSPROPERTY_SYSAUDIO_MIXER_DEFAULT >= pPreferredStatus->DeviceType) { return STATUS_SUCCESS; } DPF1(5, "SadValidateAudioPreferredStatus: Invalid DeviceType %d", pPreferredStatus->DeviceType); return STATUS_INVALID_PARAMETER; } // SadValidateAudioPreferredStatus //=========================================================================== // // SadValidateDataIntersection // Checks the integrity of dataranges following pPin. // NTSTATUS SadValidateDataIntersection( IN PIRP pIrp, IN PKSP_PIN pPin ) { PIO_STACK_LOCATION pIrpStack; PKSMULTIPLE_ITEM pKsMultipleItem; PKSDATARANGE pKsDataRange; ULONG cbTotal; ASSERT(pIrp); ASSERT(pPin); pIrpStack = IoGetCurrentIrpStackLocation(pIrp); pKsMultipleItem = (PKSMULTIPLE_ITEM) (pPin + 1); pKsDataRange = (PKSDATARANGE) (pKsMultipleItem + 1); cbTotal = pKsMultipleItem->Size - sizeof(KSMULTIPLE_ITEM); // // Make sure that the Irp Input Size is valid. Basically // InputBufferLength must be greater or equal to // KSP_PIN + MULTIPLE_ITEM.Size // if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength - sizeof(KSP_PIN) < pKsMultipleItem->Size) { DPF(5, "SadValidateDataIntersection: InputBuffer too small"); return STATUS_INVALID_BUFFER_SIZE; } // // Make sure that the MULTIPLE_ITEM contains at least one DATARANGE. // if (cbTotal < sizeof(KSDATARANGE)) { DPF(5, "SadValidateDataIntersection: Not enough data for datarange"); return STATUS_INVALID_BUFFER_SIZE; } for (ULONG ii = 0; ii < pKsMultipleItem->Count; ii++) { // // Check if we can touch the FormatSize field. // Check if we the next data-range is fully available. // if (cbTotal < sizeof(ULONG) || cbTotal < pKsDataRange->FormatSize || pKsDataRange->FormatSize < sizeof(KSDATARANGE)) { DPF3(5, "SadValidateDataIntersection: Not enough data for datarange %d %d %d", ii, pKsDataRange->FormatSize, cbTotal); return STATUS_INVALID_BUFFER_SIZE; } // // Check if the MajorFormat and size are consistent. // if (IsEqualGUID(&pKsDataRange->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO)) { if (pKsDataRange->FormatSize < sizeof(KSDATARANGE_AUDIO)) { DPF(5, "SadValidateDataIntersection: InputBuffer too small for AUDIO"); return STATUS_INVALID_BUFFER_SIZE; } } // // Set next data range. // cbTotal -= pKsDataRange->FormatSize; pKsDataRange = (PKSDATARANGE) ( ((PBYTE) pKsDataRange) + pKsDataRange->FormatSize ); } // // SECURITY NOTE: // We are not checking the output buffer integrity. The underlying drivers // are responsible for checking the size of the output buffer, based on // the result of the intersection. // return STATUS_SUCCESS; } // SadValidateDataIntersection