364 lines
11 KiB
C
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;
|
||
|
}
|
||
|
|