windows-nt/Source/XPSP1/NT/base/ntdll/sxsactctx.c
2020-09-26 16:20:57 +08:00

364 lines
11 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
sxsappctx.c
Abstract:
Side-by-side activation support for Windows/NT
Implementation of the application context object.
Author:
Michael Grier (MGrier) 2/1/2000
Revision History:
--*/
#include <ntos.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <sxstypes.h>
#include "sxsp.h"
typedef const void *PCVOID;
NTSTATUS
RtlpValidateActivationContextData(
IN ULONG Flags,
IN PCACTIVATION_CONTEXT_DATA Data,
IN SIZE_T BufferSize OPTIONAL
)
{
NTSTATUS Status = STATUS_SUCCESS;
PCACTIVATION_CONTEXT_DATA_TOC_HEADER TocHeader;
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER AssemblyRosterHeader;
(Flags);
if ((Data->Magic != ACTIVATION_CONTEXT_DATA_MAGIC) ||
(Data->FormatVersion != ACTIVATION_CONTEXT_DATA_FORMAT_WHISTLER) ||
((BufferSize != 0) &&
(BufferSize < Data->TotalSize)))
{
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
goto Exit;
}
// Check required elements...
if (Data->DefaultTocOffset == 0) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Warning: Activation context data at %p missing default TOC\n", Data);
}
// How can we not have an assembly roster?
if (Data->AssemblyRosterOffset == 0) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Warning: Activation context data at %p lacks assembly roster\n", Data);
}
if (Data->DefaultTocOffset != 0) {
if ((Data->DefaultTocOffset >= Data->TotalSize) ||
((Data->DefaultTocOffset + sizeof(ACTIVATION_CONTEXT_DATA_TOC_HEADER)) > Data->TotalSize)) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Activation context data at %p has invalid TOC header offset\n", Data);
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
goto Exit;
}
TocHeader = (PCACTIVATION_CONTEXT_DATA_TOC_HEADER) (((ULONG_PTR) Data) + Data->DefaultTocOffset);
if (TocHeader->HeaderSize < sizeof(ACTIVATION_CONTEXT_DATA_TOC_HEADER)) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Activation context data at %p has TOC header too small (%lu)\n", Data, TocHeader->HeaderSize);
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
goto Exit;
}
if ((TocHeader->FirstEntryOffset >= Data->TotalSize) ||
((TocHeader->FirstEntryOffset + (TocHeader->EntryCount * sizeof(ACTIVATION_CONTEXT_DATA_TOC_ENTRY))) > Data->TotalSize)) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Activation context data at %p has invalid TOC entry array offset\n", Data);
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
goto Exit;
}
}
// we should finish validating the rest of the structure...
if ((Data->AssemblyRosterOffset >= Data->TotalSize) ||
((Data->AssemblyRosterOffset + sizeof(ACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER)) > Data->TotalSize)) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Activation context data at %p has invalid assembly roster offset\n", Data);
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
goto Exit;
}
AssemblyRosterHeader = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER) (((ULONG_PTR) Data) + Data->AssemblyRosterOffset);
if (Data->AssemblyRosterOffset != 0) {
if (AssemblyRosterHeader->HeaderSize < sizeof(ACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER)) {
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_ERROR_LEVEL,
"SXS: Activation context data at %p has assembly roster header too small (%lu)\n", Data, AssemblyRosterHeader->HeaderSize);
Status = STATUS_SXS_INVALID_ACTCTXDATA_FORMAT;
goto Exit;
}
}
Status = STATUS_SUCCESS;
Exit:
return Status;
}
const ACTIVATION_CONTEXT_DATA RtlpTheEmptyActivationContextData =
{
ACTIVATION_CONTEXT_DATA_MAGIC,
sizeof(ACTIVATION_CONTEXT_DATA), // header size
ACTIVATION_CONTEXT_DATA_FORMAT_WHISTLER,
sizeof(ACTIVATION_CONTEXT_DATA), // total size
0, // default toc offset
0, // extended toc offset
0 // assembly roster index
};
// this struct is not const, its ref count can change (oops, no, that's
// no longer true, but leave it mutable for now to be safe)
ACTIVATION_CONTEXT RtlpTheEmptyActivationContext =
{
MAXULONG, // ref count, pinned
ACTIVATION_CONTEXT_NOT_HEAP_ALLOCATED, // flags
(PVOID)&RtlpTheEmptyActivationContextData
// the rest zeros and NULLs
};
NTSTATUS
NTAPI
RtlCreateActivationContext(
IN ULONG Flags,
IN PCACTIVATION_CONTEXT_DATA ActivationContextData,
IN ULONG ExtraBytes,
IN PACTIVATION_CONTEXT_NOTIFY_ROUTINE NotificationRoutine,
IN PVOID NotificationContext,
OUT PACTIVATION_CONTEXT *ActCtx
)
{
PACTIVATION_CONTEXT NewActCtx = NULL;
NTSTATUS Status = STATUS_SUCCESS;
ULONG i;
PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER AssemblyRosterHeader;
BOOLEAN UninitializeStorageMapOnExit = FALSE;
DbgPrintEx(
DPFLTR_SXS_ID,
DPFLTR_TRACE_LEVEL,
"SXS: RtlCreateActivationContext() called with parameters:\n"
" Flags = 0x%08lx\n"
" ActivationContextData = %p\n"
" ExtraBytes = %lu\n"
" NotificationRoutine = %p\n"
" NotificationContext = %p\n"
" ActCtx = %p\n",
Flags,
ActivationContextData,
ExtraBytes,
NotificationRoutine,
NotificationContext,
ActCtx);
RTLP_DISALLOW_THE_EMPTY_ACTIVATION_CONTEXT_DATA(ActivationContextData);
if (ActCtx != NULL)
*ActCtx = NULL;
if ((Flags != 0) ||
(ActivationContextData == NULL) ||
(ExtraBytes > 65536) ||
(ActCtx == NULL))
{
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
// Make sure that the activation context data passes muster
Status = RtlpValidateActivationContextData(0, ActivationContextData, 0);
if (NT_ERROR(Status))
goto Exit;
NewActCtx = (PACTIVATION_CONTEXT) RtlAllocateHeap(
RtlProcessHeap(),
0,
sizeof(ACTIVATION_CONTEXT) + ExtraBytes);
if (NewActCtx == NULL)
{
Status = STATUS_NO_MEMORY;
goto Exit;
}
AssemblyRosterHeader = (PCACTIVATION_CONTEXT_DATA_ASSEMBLY_ROSTER_HEADER) (((ULONG_PTR) ActivationContextData) + ActivationContextData->AssemblyRosterOffset);
Status = RtlpInitializeAssemblyStorageMap(
&NewActCtx->StorageMap,
AssemblyRosterHeader->EntryCount,
(AssemblyRosterHeader->EntryCount > NUMBER_OF(NewActCtx->InlineStorageMapEntries)) ? NULL : NewActCtx->InlineStorageMapEntries);
if (NT_ERROR(Status))
goto Exit;
UninitializeStorageMapOnExit = TRUE;
NewActCtx->RefCount = 1;
NewActCtx->Flags = 0;
NewActCtx->ActivationContextData = (PVOID) ActivationContextData;
NewActCtx->NotificationRoutine = NotificationRoutine;
NewActCtx->NotificationContext = NotificationContext;
for (i=0; i<NUMBER_OF(NewActCtx->SentNotifications); i++)
NewActCtx->SentNotifications[i] = 0;
for (i=0; i<NUMBER_OF(NewActCtx->DisabledNotifications); i++)
NewActCtx->DisabledNotifications[i] = 0;
*ActCtx = NewActCtx;
NewActCtx = NULL;
UninitializeStorageMapOnExit = FALSE;
Status = STATUS_SUCCESS;
Exit:
if (NewActCtx != NULL) {
if (UninitializeStorageMapOnExit) {
RtlpUninitializeAssemblyStorageMap(&NewActCtx->StorageMap);
}
RtlFreeHeap(RtlProcessHeap(), 0, NewActCtx);
}
return Status;
}
VOID
NTAPI
RtlAddRefActivationContext(
PACTIVATION_CONTEXT ActCtx
)
{
if ((ActCtx != NULL) &&
(!IS_SPECIAL_ACTCTX(ActCtx)) &&
(ActCtx->RefCount != MAXULONG))
{
LONG NewRefCount = 0;
ASSERT(ActCtx->RefCount != 0);
NewRefCount = InterlockedIncrement(&ActCtx->RefCount);
// Probably indicative of a reference leak that's wrapped the refcount;
// other probable cause is several concurrent releases.
ASSERT(NewRefCount != 0);
}
}
VOID
NTAPI
RtlReleaseActivationContext(
PACTIVATION_CONTEXT ActCtx
)
{
if ((ActCtx != NULL) &&
(!IS_SPECIAL_ACTCTX(ActCtx)) &&
(ActCtx->RefCount != MAXULONG))
{
LONG NewRefCount = 0;
ASSERT(ActCtx->RefCount != 0);
NewRefCount = InterlockedDecrement(&ActCtx->RefCount);
if (NewRefCount == 0)
{
if (ActCtx->NotificationRoutine != NULL)
{
// There's no need to check for the notification being disabled; destroy
// notifications are sent only once, so if the notification routine is not
// null, we can just call it.
BOOLEAN DisableNotification = FALSE;
(*(ActCtx->NotificationRoutine))(
ACTIVATION_CONTEXT_NOTIFICATION_DESTROY,
ActCtx,
ActCtx->ActivationContextData,
ActCtx->NotificationContext,
NULL,
&DisableNotification);
}
RtlpUninitializeAssemblyStorageMap(&ActCtx->StorageMap);
//
// This predates the MAXULONG refcount, maybe we can get rid of the the flag now?
//
if ((ActCtx->Flags & ACTIVATION_CONTEXT_NOT_HEAP_ALLOCATED) == 0) {
RtlFreeHeap(RtlProcessHeap(), 0, ActCtx);
}
}
}
}
NTSTATUS
NTAPI
RtlZombifyActivationContext(
PACTIVATION_CONTEXT ActCtx
)
{
NTSTATUS Status = STATUS_SUCCESS;
if ((ActCtx == NULL) || IS_SPECIAL_ACTCTX(ActCtx))
{
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if ((ActCtx->Flags & ACTIVATION_CONTEXT_ZOMBIFIED) == 0)
{
if (ActCtx->NotificationRoutine != NULL)
{
// Since disable is sent only once, there's no need to check for
// disabled notifications.
BOOLEAN DisableNotification = FALSE;
(*(ActCtx->NotificationRoutine))(
ACTIVATION_CONTEXT_NOTIFICATION_ZOMBIFY,
ActCtx,
ActCtx->ActivationContextData,
ActCtx->NotificationContext,
NULL,
&DisableNotification);
}
ActCtx->Flags |= ACTIVATION_CONTEXT_ZOMBIFIED;
}
Status = STATUS_SUCCESS;
Exit:
return Status;
}