Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1644 lines
39 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
psldt.c
Abstract:
This module contains code for the process and thread ldt support
Author:
Dave Hastings (daveh) 20 May 1991
Notes:
We return STATUS_SUCCESS for exceptions resulting from accessing the
process info structures in user space. This is by design (so Markl
tells me). By the time we reach these functions all of the parameters
in user space have been probed, so if we get an exception it indicates
that the user is altering portions of his address space that have been
passed to the system.
The paged pool consumed by the Ldt is returned to the system at process
deletion time. The process deletion handler calls PspDeleteLdt. We
do not keep a reference to the process once the ldt is created.
We capture the user mode parameters into local parameters to prevent
the possibility that the user will change them after we validate them.
Revision History:
--*/
#include "psp.h"
//
// Internal constants
//
#define DESCRIPTOR_GRAN 0x00800000
#define DESCRIPTOR_NP 0x00008000
#define DESCRIPTOR_SYSTEM 0x00001000
#define DESCRIPTOR_CONFORM 0x00001C00
#define DESCRIPTOR_DPL 0x00006000
#define DESCRIPTOR_TYPEDPL 0x00007F00
KMUTEX LdtMutex;
//
// Internal subroutines
//
PLDT_ENTRY
PspCreateLdt(
IN PLDT_ENTRY Ldt,
IN ULONG Offset,
IN ULONG Size,
IN ULONG AllocationSize
);
BOOLEAN
PspIsDescriptorValid(
IN PLDT_ENTRY Descriptor
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, PspLdtInitialize)
#pragma alloc_text(PAGE, NtSetLdtEntries)
#pragma alloc_text(PAGE, PspDeleteLdt)
#pragma alloc_text(PAGE, PspQueryLdtInformation)
#pragma alloc_text(PAGE, PspSetLdtSize)
#pragma alloc_text(PAGE, PspSetLdtInformation)
#pragma alloc_text(PAGE, PspCreateLdt)
#pragma alloc_text(PAGE, PspIsDescriptorValid)
#pragma alloc_text(PAGE, PspQueryDescriptorThread)
#endif
NTSTATUS
PspLdtInitialize(
)
/*++
Routine Description:
This routine initializes the Ldt support for the x86
Arguments:
None
Return Value:
TBS
--*/
{
KeInitializeMutex( &LdtMutex, MUTEX_LEVEL_PS_LDT );
return STATUS_SUCCESS;
}
NTSTATUS
PspQueryLdtInformation(
IN PEPROCESS Process,
OUT PPROCESS_LDT_INFORMATION LdtInformation,
IN ULONG LdtInformationLength,
OUT PULONG ReturnLength
)
/*++
Routine Description:
This function performs the work for the Ldt portion of the query
process information function. It copies the contents of the Ldt
for the specified process into the users buffer, up to the length
of the buffer.
Arguments:
Process -- Supplies a pointer to the process to return LDT info for
LdtInformation -- Supplies a pointer to the buffer
ReturnLength -- Returns the number of bytes put into the buffer
Return Value:
TBS
--*/
{
ULONG CopyLength, CopyEnd;
NTSTATUS Status;
ULONG HeaderLength;
ULONG Length, Start;
LONG MutexStatus;
PLDTINFORMATION ProcessLdtInfo;
BOOLEAN ReturnNow = FALSE;
PAGED_CODE();
//
// Verify the parameters
//
if ( LdtInformationLength < (ULONG)sizeof(PROCESS_LDT_INFORMATION) ) {
return STATUS_INFO_LENGTH_MISMATCH;
}
//
// This portion of the parameters may be in user space
//
try {
//
// Capture parameters
//
Length = LdtInformation->Length;
Start = LdtInformation->Start;
} except(EXCEPTION_EXECUTE_HANDLER) {
ReturnNow = TRUE;
}
if (ReturnNow) {
return STATUS_SUCCESS;
}
//
// The buffer containing the Ldt entries must be in the information
// structure. We subtract one Ldt entry, because the structure is
// declared to contain one.
//
if (LdtInformationLength < (sizeof(PROCESS_LDT_INFORMATION) +
Length - sizeof(LDT_ENTRY))) {
return STATUS_INFO_LENGTH_MISMATCH;
}
// An Ldt entry is a processor structure, and must be 8 bytes long
ASSERT((sizeof(LDT_ENTRY) == 8));
//
// The length of the structure must be an even number of Ldt entries
//
if (Length % sizeof(LDT_ENTRY)) {
return STATUS_INVALID_LDT_SIZE;
}
//
// The information to get from the Ldt must start on an Ldt entry
// boundary.
//
if (Start % sizeof(LDT_ENTRY)) {
return STATUS_INVALID_LDT_OFFSET;
}
//
// Acquire the Ldt mutex
//
Status = KeWaitForSingleObject(
&LdtMutex,
Executive,
KernelMode,
FALSE,
NULL
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
ProcessLdtInfo = Process->LdtInformation;
//
// If the process has an Ldt
//
if (( ProcessLdtInfo) && (ProcessLdtInfo->Size )) {
ASSERT ((ProcessLdtInfo->Ldt));
//
// Set the end of the copy to be the smaller of:
// the end of the information the user requested or
// the end of the information that is actually there
//
if (ProcessLdtInfo->Size > (Start + Length)) {
CopyEnd = Length + Start;
} else {
CopyEnd = ProcessLdtInfo->Size;
}
CopyLength = CopyEnd - Start;
try {
//
// Set the length field to the actual length of the Ldt
//
LdtInformation->Length = ProcessLdtInfo->Size;
//
// Copy the contents of the Ldt into the user's buffer
//
if (CopyLength) {
RtlMoveMemory(
&(LdtInformation->LdtEntries),
(PCHAR)ProcessLdtInfo->Ldt + Start,
CopyLength
);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
ReturnNow = TRUE;
MutexStatus = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexStatus == 0 ));
}
if (ReturnNow) {
return STATUS_SUCCESS;
}
} else {
//
// There is no Ldt
//
CopyLength = 0;
try {
LdtInformation->Length = 0;
} except(EXCEPTION_EXECUTE_HANDLER) {
ReturnNow = TRUE;
MutexStatus = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexStatus == 0 ));
}
if (ReturnNow) {
return STATUS_SUCCESS;
}
}
//
// Set the length of the information returned
//
if ( ARGUMENT_PRESENT(ReturnLength) ) {
try {
HeaderLength = (PCHAR)(&(LdtInformation->LdtEntries)) -
(PCHAR)(&(LdtInformation->Start));
*ReturnLength = CopyLength + HeaderLength;
} except(EXCEPTION_EXECUTE_HANDLER){
// We don't do anything here because we want to return success
}
}
MutexStatus = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexStatus == 0 ));
return STATUS_SUCCESS;
}
NTSTATUS
PspSetLdtSize(
IN PEPROCESS Process,
IN PPROCESS_LDT_SIZE LdtSize,
IN ULONG LdtSizeLength
)
/*++
Routine Description:
This routine changes the LDT size. It will shrink the LDT, but not
grow it. If the LDT shrinks by 1 or more pages from its current allocation,
the LDT will be reallocated for the new smaller size. If the allocated
size of the LDT changes, the quota charge for the Ldt will be reduced.
Arguments:
Process -- Supplies a pointer to the process whose Ldt is to be sized
LdtSize -- Supplies a pointer to the size information
Return Value:
TBS
--*/
{
ULONG OldSize = 0;
LONG MutexState;
ULONG Length;
PLDT_ENTRY OldLdt = NULL;
NTSTATUS Status;
PLDTINFORMATION ProcessLdtInfo;
BOOLEAN ReturnNow = FALSE;
PLDT_ENTRY Ldt;
PAGED_CODE();
//
// Verify the parameters
//
if ( LdtSizeLength != (ULONG)sizeof( PROCESS_LDT_SIZE ) ){
return STATUS_INFO_LENGTH_MISMATCH;
}
//
// The following parameters may be in user space
//
try {
//
// Capture the new Ldt length
//
Length = LdtSize->Length;
} except(EXCEPTION_EXECUTE_HANDLER){
ReturnNow = TRUE;
}
if (ReturnNow) {
return STATUS_SUCCESS;
}
ASSERT((sizeof(LDT_ENTRY) == 8));
//
// The Ldt must always be an integral number of LDT_ENTRIES
//
if (Length % sizeof(LDT_ENTRY)) {
return STATUS_INVALID_LDT_SIZE;
}
//
// Acquire the Ldt Mutex
//
Status = KeWaitForSingleObject(
&LdtMutex,
Executive,
KernelMode,
FALSE,
NULL
);
if ( !(NT_SUCCESS(Status)) ) {
return Status;
}
//
// If there isn't an Ldt we can't set the size of the LDT
//
ProcessLdtInfo = Process->LdtInformation;
if ((ProcessLdtInfo == NULL) || (ProcessLdtInfo->Size == 0)) {
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT((MutexState == 0));
return STATUS_NO_LDT;
}
//
// This function cannot be used to grow the Ldt
//
if (Length > ProcessLdtInfo->Size) {
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT((MutexState == 0));
return STATUS_INVALID_LDT_SIZE;
}
//
// Later, we will set ProcessLdtInfo->Ldt = Ldt. We may set the value
// of Ldt in the if statement below, but there is one case where we
// don't
//
Ldt = ProcessLdtInfo->Ldt;
//
// Adjust the size of the LDT
//
ProcessLdtInfo->Size = Length;
//
// Free some of the Ldt memory if conditions allow
//
if ( Length == 0 ) {
OldSize = ProcessLdtInfo->AllocatedSize;
OldLdt = ProcessLdtInfo->Ldt;
ProcessLdtInfo->AllocatedSize = 0;
Ldt = NULL;
} else if ( (ProcessLdtInfo->AllocatedSize - ProcessLdtInfo->Size) >=
PAGE_SIZE
) {
OldSize = ProcessLdtInfo->AllocatedSize;
OldLdt = ProcessLdtInfo->Ldt;
//
// Calculate new Ldt size (lowest integer number of pages
// large enough)
//
ProcessLdtInfo->AllocatedSize = (ProcessLdtInfo->Size + PAGE_SIZE - 1)
& ~(PAGE_SIZE - 1);
//
// Reallocate and copy the Ldt
//
Ldt = PspCreateLdt(
ProcessLdtInfo->Ldt,
0,
ProcessLdtInfo->Size,
ProcessLdtInfo->AllocatedSize
);
if ( Ldt == NULL ) {
// We cannot reduce the allocation, but we can reduce the
// Ldt selector limit (done using Ke386SetLdtProcess)
Ldt = OldLdt;
ProcessLdtInfo->AllocatedSize = OldSize;
OldLdt = NULL;
}
}
ProcessLdtInfo->Ldt = Ldt;
//
// Change the limit on the Process Ldt
//
Ke386SetLdtProcess(
&(Process->Pcb),
ProcessLdtInfo->Ldt,
ProcessLdtInfo->Size
);
//
// If we resized the Ldt, free to old one and reduce the quota charge
//
if (OldLdt) {
ExFreePool( OldLdt );
PsReturnPoolQuota(
Process,
PagedPool,
OldSize - ProcessLdtInfo->AllocatedSize
);
}
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT((MutexState == 0));
return STATUS_SUCCESS;
}
NTSTATUS
PspSetLdtInformation(
IN PEPROCESS Process,
IN PPROCESS_LDT_INFORMATION LdtInformation,
IN ULONG LdtInformationLength
)
/*++
Routine Description:
This function alters the ldt for a specified process. It can alter
portions of the LDT, or the whole LDT. If an Ldt is created or
grown, the specified process will be charged the quota for the LDT.
Each descriptor that is set will be verified.
Arguments:
Process -- Supplies a pointer to the process whose Ldt is to be modified
LdtInformation -- Supplies a pointer to the information about the Ldt
modifications
LdtInformationLength -- Supplies the length of the LdtInformation
structure.
Return Value:
TBS
--*/
{
NTSTATUS Status;
PLDT_ENTRY OldLdt = NULL;
ULONG OldSize = 0;
ULONG AllocatedSize;
ULONG Size;
ULONG MutexState;
ULONG LdtOffset;
PLDT_ENTRY CurrentDescriptor;
PPROCESS_LDT_INFORMATION LdtInfo;
PLDTINFORMATION ProcessLdtInfo;
PLDT_ENTRY Ldt;
PAGED_CODE();
if ( LdtInformationLength < (ULONG)sizeof( PROCESS_LDT_INFORMATION)) {
return STATUS_INFO_LENGTH_MISMATCH;
}
Status = STATUS_SUCCESS;
//
// alocate a local buffer to capture the ldt information to
//
try {
LdtInfo = ExAllocatePool(
PagedPool,
LdtInformationLength
);
if (LdtInfo) {
//
// Copy the information the user is supplying
//
RtlMoveMemory(
LdtInfo,
LdtInformation,
LdtInformationLength
);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
if (LdtInfo) {
ExFreePool(LdtInfo);
}
Status = GetExceptionCode();
}
//
// If the capture didn't succeed
//
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_ACCESS_VIOLATION) {
return STATUS_SUCCESS;
} else {
return Status;
}
}
if (LdtInfo == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Insure that the buffer it large enough to contain the specified number
// of selectors.
//
if (LdtInformationLength <
(sizeof(PROCESS_LDT_INFORMATION) + LdtInfo->Length - sizeof(LDT_ENTRY))
) {
ExFreePool(LdtInfo);
return STATUS_INFO_LENGTH_MISMATCH;
}
//
// The info to set must be an integral number of selectors
//
if (LdtInfo->Length % sizeof(LDT_ENTRY)) {
ExFreePool(LdtInfo);
return STATUS_INVALID_LDT_SIZE;
}
//
// The beginning of the info must be on a selector boundary
//
if (LdtInfo->Start % sizeof(LDT_ENTRY)) {
ExFreePool(LdtInfo);
return STATUS_INVALID_LDT_OFFSET;
}
//
// Verify all of the descriptors.
//
for (CurrentDescriptor = LdtInfo->LdtEntries;
(PCHAR)CurrentDescriptor < (PCHAR)LdtInfo->LdtEntries +
LdtInfo->Length;
CurrentDescriptor++
) {
if (!PspIsDescriptorValid( CurrentDescriptor )) {
ExFreePool(LdtInfo);
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
//
// Acquire the Ldt Mutex
//
Status = KeWaitForSingleObject(
&LdtMutex,
Executive,
KernelMode,
FALSE,
NULL
);
if ( !(NT_SUCCESS(Status)) ) {
return Status;
}
ProcessLdtInfo = Process->LdtInformation;
//
// If the process doen't have an Ldt information structure, allocate
// one and attach it to the process
//
if ( ProcessLdtInfo == NULL ) {
ProcessLdtInfo = ExAllocatePool(
PagedPool,
sizeof(LDTINFORMATION)
);
if ( ProcessLdtInfo == NULL ) {
goto SetInfoCleanup;
}
Process->LdtInformation = ProcessLdtInfo;
RtlZeroMemory( ProcessLdtInfo, sizeof(LDTINFORMATION) );
}
//
// If we are supposed to remove the LDT
//
if ( LdtInfo->Length == 0 ) {
//
// Remove the process' Ldt
//
if ( ProcessLdtInfo->Ldt ) {
OldSize = ProcessLdtInfo->AllocatedSize;
OldLdt = ProcessLdtInfo->Ldt;
ProcessLdtInfo->AllocatedSize = 0;
ProcessLdtInfo->Size = 0;
ProcessLdtInfo->Ldt = NULL;
Ke386SetLdtProcess(
&(Process->Pcb),
NULL,
0
);
PsReturnPoolQuota( Process, PagedPool, OldSize );
}
} else if ( ProcessLdtInfo->Ldt == NULL ) {
//
// Create a new Ldt for the process
//
//
// Allocate an integral number of pages for the LDT.
//
ASSERT(((PAGE_SIZE % 2) == 0));
AllocatedSize = (LdtInfo->Start + LdtInfo->Length + PAGE_SIZE - 1) &
~(PAGE_SIZE - 1);
Size = LdtInfo->Start + LdtInfo->Length;
Ldt = PspCreateLdt(
LdtInfo->LdtEntries,
LdtInfo->Start,
Size,
AllocatedSize
);
if ( Ldt == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto SetInfoCleanup;
}
try {
PsChargePoolQuota(
Process,
PagedPool,
AllocatedSize
);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status)) {
goto SetInfoCleanup;
}
ProcessLdtInfo->Ldt = Ldt;
ProcessLdtInfo->Size = Size;
ProcessLdtInfo->AllocatedSize = AllocatedSize;
Ke386SetLdtProcess(
&(Process->Pcb),
ProcessLdtInfo->Ldt,
ProcessLdtInfo->Size
);
} else if ( (LdtInfo->Length + LdtInfo->Start) > ProcessLdtInfo->Size ) {
//
// Grow the process' Ldt
//
if ( (LdtInfo->Length + LdtInfo->Start) >
ProcessLdtInfo->AllocatedSize
) {
//
// Current Ldt allocation is not large enough, so create a
// new larger Ldt
//
OldSize = ProcessLdtInfo->AllocatedSize;
Size = LdtInfo->Start + LdtInfo->Length;
AllocatedSize = (Size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
Ldt = PspCreateLdt(
ProcessLdtInfo->Ldt,
0,
OldSize,
AllocatedSize
);
if ( Ldt == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto SetInfoCleanup;
}
try {
PsChargePoolQuota(
Process,
PagedPool,
ProcessLdtInfo->AllocatedSize - OldSize
);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status)) {
goto SetInfoCleanup;
}
//
// Swap Ldt information
//
OldLdt = ProcessLdtInfo->Ldt;
ProcessLdtInfo->Ldt = Ldt;
ProcessLdtInfo->Size = Size;
ProcessLdtInfo->AllocatedSize = AllocatedSize;
//
// Put new selectors into the new ldt
//
RtlMoveMemory(
(PCHAR)(ProcessLdtInfo->Ldt) + LdtInfo->Start,
LdtInfo->LdtEntries,
LdtInfo->Length
);
Ke386SetLdtProcess(
&(Process->Pcb),
ProcessLdtInfo->Ldt,
ProcessLdtInfo->Size
);
} else {
//
// Current Ldt allocation is large enough
//
ProcessLdtInfo->Size = LdtInfo->Length + LdtInfo->Start;
Ke386SetLdtProcess(
&(Process->Pcb),
ProcessLdtInfo->Ldt,
ProcessLdtInfo->Size
);
//
// Change the selectors in the table
//
for (LdtOffset = LdtInfo->Start,
CurrentDescriptor = LdtInfo->LdtEntries;
LdtOffset < LdtInfo->Start + LdtInfo->Length;
LdtOffset += sizeof(LDT_ENTRY),
CurrentDescriptor++
) {
Ke386SetDescriptorProcess(
&(Process->Pcb),
LdtOffset,
*CurrentDescriptor
);
}
}
} else {
//
// Simply changing some selectors
//
for (LdtOffset = LdtInfo->Start,
CurrentDescriptor = LdtInfo->LdtEntries;
LdtOffset < LdtInfo->Start + LdtInfo->Length;
LdtOffset += sizeof(LDT_ENTRY),
CurrentDescriptor++
) {
Ke386SetDescriptorProcess(
&(Process->Pcb),
LdtOffset,
*CurrentDescriptor
);
}
Status = STATUS_SUCCESS;
}
SetInfoCleanup:
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexState == 0 ));
if (OldLdt != NULL) {
ExFreePool(OldLdt);
}
if (LdtInfo != NULL) {
ExFreePool(LdtInfo);
}
return Status;
}
PLDT_ENTRY
PspCreateLdt(
IN PLDT_ENTRY Ldt,
IN ULONG Offset,
IN ULONG Size,
IN ULONG AllocationSize
)
/*++
Routine Description:
This routine allocates space in paged pool for an LDT, and copies the
specified selectors into it. IT DOES NOT VALIDATE THE SELECTORS.
Selector validation must be done before calling this routine. IT
DOES NOT CHARGE THE QUOTA FOR THE LDT.
Arguments:
Ldt -- Supplies a pointer to the descriptors to be put into the Ldt.
Offset -- Supplies the offset in the LDT to copy the descriptors to.
Size -- Supplies the actualsize of the new Ldt
AllocationSize -- Supplies the size to allocate
Return Value:
Pointer to the new Ldt
--*/
{
PLDT_ENTRY NewLdt;
PAGED_CODE();
ASSERT(( AllocationSize >= Size ));
ASSERT(( (Size % sizeof(LDT_ENTRY)) == 0 ));
NewLdt = ExAllocatePool( PagedPool, AllocationSize );
if (NewLdt == NULL) {
return NewLdt;
}
RtlZeroMemory( NewLdt, AllocationSize );
RtlMoveMemory( (PCHAR)NewLdt + Offset, Ldt, Size - Offset );
return NewLdt;
}
BOOLEAN
PspIsDescriptorValid(
IN PLDT_ENTRY Descriptor
)
/*++
Routine Description:
This function determines if the supplied descriptor is valid to put
into a process Ldt. For the descriptor to be valid it must have the
following characteristics:
Base < MM_HIGHEST_USER_ADDRESS
Base + Limit < MM_HIGHEST_USER_ADDRESS
Type must be
ReadWrite, ReadOnly, ExecuteRead, ExecuteOnly, or Invalid
big or small
normal or grow down
Not a gate
Not conforming
DPL must be 3
Arguments:
Descriptor -- Supplies a pointer to the descriptor to check
Return Value:
True if the descriptor is valid (note: valid to put into an LDT. This
includes Invalid descriptors)
False if not
--*/
{
ULONG Base;
ULONG Limit;
PAGED_CODE();
//
// if descriptor is an invalid descriptor
//
if ( (Descriptor->HighWord.Bits.Type == 0) &&
(Descriptor->HighWord.Bits.Dpl == 0) ) {
return TRUE;
}
Base = Descriptor->BaseLow | (Descriptor->HighWord.Bytes.BaseMid << 16) |
(Descriptor->HighWord.Bytes.BaseHi << 24);
Limit = Descriptor->LimitLow | (Descriptor->HighWord.Bits.LimitHi << 16);
//
// Only have to check for present selectors
//
if (Descriptor->HighWord.Bits.Pres) {
if ( (PVOID)Base > MM_HIGHEST_USER_ADDRESS ) {
return FALSE;
}
if ( (PVOID)(Base + (Limit << (Descriptor->HighWord.Bits.Granularity *
12)) + 0xFFF * Descriptor->HighWord.Bits.Granularity) >
MM_HIGHEST_USER_ADDRESS ) {
return FALSE;
}
}
//
// if descriptor is a gate
//
if ( !(Descriptor->HighWord.Bits.Type & 0x18) ) {
return FALSE;
}
//
// if descriptor is conforming code
//
if ( ((Descriptor->HighWord.Bits.Type & 0x18) == 0x18) &&
(Descriptor->HighWord.Bits.Type & 0x4)) {
return FALSE;
}
//
// if Dpl is not 3
//
if ( Descriptor->HighWord.Bits.Dpl != 3 ) {
return FALSE;
}
return TRUE;
}
NTSTATUS
PspQueryDescriptorThread (
PETHREAD Thread,
PVOID ThreadInformation,
ULONG ThreadInformationLength,
PULONG ReturnLength
)
/*++
Routine Description:
This function retrieves a descriptor table entry for the specified thread.
This entry may be in either the Gdt or the Ldt, as specfied by the
supplied selector
Arguments:
Thread -- Supplies a pointer to the thread.
ThreadInformation -- Supplies information on the descriptor.
ThreadInformationLength -- Supplies the length of the information.
ReturnLength -- Returns the number of bytes returned.
Return Value:
TBS
--*/
{
DESCRIPTOR_TABLE_ENTRY DescriptorEntry;
PEPROCESS Process;
BOOLEAN ReturnNow = FALSE;
LONG MutexState;
NTSTATUS Status;
PAGED_CODE();
ASSERT( sizeof(KGDTENTRY) == sizeof(LDT_ENTRY) );
//
// Verify parameters
//
if ( ThreadInformationLength != sizeof(DESCRIPTOR_TABLE_ENTRY) ) {
return STATUS_INFO_LENGTH_MISMATCH;
}
try {
DescriptorEntry = *(PDESCRIPTOR_TABLE_ENTRY)ThreadInformation;
} except(EXCEPTION_EXECUTE_HANDLER){
ReturnNow = TRUE;
}
if (ReturnNow) {
return STATUS_SUCCESS;
}
Status = STATUS_SUCCESS;
//
// If its a Gdt entry, let the kernel find it for us
//
if ( !(DescriptorEntry.Selector & SELECTOR_TABLE_INDEX) ) {
if ( (DescriptorEntry.Selector & 0xFFFFFFF8) >= KGDT_NUMBER *
sizeof(KGDTENTRY) ) {
return STATUS_ACCESS_VIOLATION;
}
try {
Ke386GetGdtEntryThread( &(Thread->Tcb),
DescriptorEntry.Selector & 0xFFFFFFF8,
(PKGDTENTRY)
&(((PDESCRIPTOR_TABLE_ENTRY)ThreadInformation)->Descriptor)
);
if ( ARGUMENT_PRESENT(ReturnLength) ) {
*ReturnLength = sizeof(LDT_ENTRY);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
// We want to return STATUS_SUCCESS (see module notes), so
// do nothing and fall out of if.
}
} else {
//
// it's an Ldt entry, so copy it from the ldt
//
Process = THREAD_TO_PROCESS(Thread);
//
// Acquire the Ldt Mutex
//
Status = KeWaitForSingleObject(
&LdtMutex,
Executive,
KernelMode,
FALSE,
NULL
);
if ( !(NT_SUCCESS(Status)) ) {
return Status;
}
if ( Process->LdtInformation == NULL ) {
// If there is no Ldt
Status = STATUS_NO_LDT;
} else if ( (DescriptorEntry.Selector & 0xFFFFFFF8) >=
((PLDTINFORMATION)(Process->LdtInformation))->Size ) {
// Else If the selector is outside the table
Status = STATUS_ACCESS_VIOLATION;
} else try {
// Else return the contents of the descriptor
RtlMoveMemory(
&(((PDESCRIPTOR_TABLE_ENTRY)ThreadInformation)->Descriptor),
(PCHAR)(((PLDTINFORMATION)(Process->LdtInformation))->Ldt) +
(DescriptorEntry.Selector & 0xFFFFFFF8),
sizeof(LDT_ENTRY)
);
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = sizeof(LDT_ENTRY);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
// We want to return STATUS_SUCCESS (see module notes), so
// do nothing and fall out of if.
}
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexState == 0 ));
}
return Status;
}
VOID
PspDeleteLdt(
IN PEPROCESS Process
)
/*++
Routine Description:
This routine frees the paged pool associated with a process' Ldt, if
it has one.
Arguments:
Process -- Supplies a pointer to the process
Return Value:
None
--*/
{
PLDTINFORMATION LdtInformation;
PAGED_CODE();
LdtInformation = Process->LdtInformation;
if ( LdtInformation != NULL ) {
if ( LdtInformation->Ldt != NULL ) {
ExFreePool( LdtInformation->Ldt );
}
ExFreePool( LdtInformation );
}
}
NTSTATUS
NtSetLdtEntries(
IN ULONG Selector0,
IN ULONG Entry0Low,
IN ULONG Entry0Hi,
IN ULONG Selector1,
IN ULONG Entry1Low,
IN ULONG Entry1Hi
)
/*++
Routine Description:
This routine sets up to two selectors in the current process's LDT.
The LDT will be grown as necessary. A selector value of 0 indicates
that the specified selector was not passed (allowing the setting of
a single selector).
Arguments:
Selector0 -- Supplies the number of the first descriptor to set
Entry0Low -- Supplies the low 32 bits of the descriptor
Entry0Hi -- Supplies the high 32 bits of the descriptor
Selector1 -- Supplies the number of the first descriptor to set
Entry1Low -- Supplies the low 32 bits of the descriptor
Entry1Hi -- Supplies the high 32 bits of the descriptor
Return Value:
TBS
--*/
{
ULONG Base, Limit, LdtSize, AllocatedSize;
NTSTATUS Status;
PEPROCESS Process;
LDT_ENTRY Descriptor;
PLDT_ENTRY Ldt, OldLdt;
PLDTINFORMATION ProcessLdtInformation;
LONG MutexState;
PAGED_CODE();
//
// Verify the selectors. We do not allow selectors that point into
// Kernel space, system selectors, or conforming code selectors
//
// Change the selector values to indexes into the LDT
Selector0 = Selector0 & ~(RPL_MASK | SELECTOR_TABLE_INDEX);
Selector1 = Selector1 & ~(RPL_MASK | SELECTOR_TABLE_INDEX);
//
// Verify selector 0
//
if (Selector0) {
// Form the base and the limit
Base = ((Entry0Low & 0xFFFF0000) >> 16) + ((Entry0Hi & 0xFF) << 16)
+ (Entry0Hi & 0xFF000000);
Limit = (Entry0Low & 0xFFFF) + (Entry0Hi & 0xF0000);
// N.B. the interpretation of the limit depends on the G bit
// in the selector
if (Entry0Hi & DESCRIPTOR_GRAN) {
Limit = (Limit << 12) | 0xFFF;
}
//
// Base and limit don't matter for NP selectors, so only check
// for present selectors
//
if (Entry0Hi & DESCRIPTOR_NP) {
// Check selector base and limit
if (((PVOID)Base > MM_HIGHEST_USER_ADDRESS)
|| ((PVOID)(Base + Limit) > MM_HIGHEST_USER_ADDRESS))
{
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
//
// If type and DPL are 0, this is an invalid selector, otherwise,
// we have to check the type (invalid from the standpoint of a
// selector created to generate a GP fault)
//
if (Entry0Hi & DESCRIPTOR_TYPEDPL) {
// No system selectors (system descriptors have system bit = 0)
if (!(Entry0Hi & DESCRIPTOR_SYSTEM)) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
// No conforming code
if ((Entry0Hi & DESCRIPTOR_CONFORM) == DESCRIPTOR_CONFORM) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
// Dpl must be 3
if ((Entry0Hi & DESCRIPTOR_DPL) != DESCRIPTOR_DPL) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
}
//
// Verify selector 1
//
if (Selector1) {
// Form the base and the limit
Base = ((Entry1Low & 0xFFFF0000) >> 16) + ((Entry1Hi & 0xFF) << 16)
+ (Entry1Hi & 0xFF000000);
Limit = (Entry1Low & 0xFFFF) + (Entry1Hi & 0xF0000);
// N.B. the interpretation of the limit depends on the G bit
// in the selector
if (Entry1Hi & DESCRIPTOR_GRAN) {
Limit = (Limit << 12) | 0xFFF;
}
//
// Base and limit don't matter for NP selectors, so only check
// for present selectors
//
if (Entry1Hi & DESCRIPTOR_NP) {
// Check selector base and limit
if (((PVOID)Base > MM_HIGHEST_USER_ADDRESS)
|| ((PVOID)(Base + Limit) > MM_HIGHEST_USER_ADDRESS))
{
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
//
// If type and DPL are 0, this is an invalid selector, otherwise,
// we have to check the type (invalid from the standpoint of a
// selector created to generate a GP fault)
//
if (Entry1Hi & DESCRIPTOR_TYPEDPL) {
// No system selectors (system descriptors have system bit = 0)
if (!(Entry1Hi & DESCRIPTOR_SYSTEM)) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
// No conforming code
if ((Entry1Hi & DESCRIPTOR_CONFORM) == DESCRIPTOR_CONFORM) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
// Dpl must be 3
if ((Entry1Hi & DESCRIPTOR_DPL) != DESCRIPTOR_DPL) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
}
//
// Acquire the LDT mutex.
//
Status = KeWaitForSingleObject(
&LdtMutex,
Executive,
KernelMode,
FALSE,
NULL
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// Figure out how large the LDT needs to be
//
if (Selector0 > Selector1) {
LdtSize = Selector0 + sizeof(LDT_ENTRY);
} else {
LdtSize = Selector1 + sizeof(LDT_ENTRY);
}
Process = PsGetCurrentProcess();
ProcessLdtInformation = Process->LdtInformation;
//
// Most of the time, the process will already have an LDT, and it
// will be large enough. for this, we just set the selectors and
// return
//
if (ProcessLdtInformation) {
//
// If the LDT selector does not have to be modified.
//
if (ProcessLdtInformation->Size >= LdtSize) {
if (Selector0) {
*((PULONG)(&Descriptor)) = Entry0Low;
*(((PULONG)(&Descriptor)) + 1) = Entry0Hi;
Ke386SetDescriptorProcess(
&(Process->Pcb),
Selector0,
Descriptor
);
}
if (Selector1) {
*((PULONG)(&Descriptor)) = Entry1Low;
*(((PULONG)(&Descriptor)) + 1) = Entry1Hi;
Ke386SetDescriptorProcess(
&(Process->Pcb),
Selector1,
Descriptor
);
}
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexState == 0 ));
return STATUS_SUCCESS;
//
// Else if the Ldt will fit in the memory currently allocated
//
} else if (ProcessLdtInformation->AllocatedSize >= LdtSize) {
//
// First remove the LDT. This will allow us to edit the memory.
// We will then put the LDT back. Since we have to change the
// limit anyway, it would take two calls to the kernel ldt
// management minimum to set the descriptors. Each of those calls
// would stall all of the processors in an MP system. If we
// didn't remove the ldt first, and we were setting two descriptors,
// we would have to call the LDT management 3 times (once per
// descriptor, and once to change the limit of the LDT).
//
Ke386SetLdtProcess(
&(Process->Pcb),
NULL,
0L
);
//
// Set the Descriptors in the LDT
//
if (Selector0) {
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)]))) = Entry0Low;
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)])) + 1) =
Entry0Hi;
}
if (Selector1) {
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)]))) = Entry1Low;
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)])) + 1) =
Entry1Hi;
}
//
// Set the LDT for the process
//
ProcessLdtInformation->Size = LdtSize;
Ke386SetLdtProcess(
&(Process->Pcb),
ProcessLdtInformation->Ldt,
ProcessLdtInformation->Size
);
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexState == 0 ));
return STATUS_SUCCESS;
//
// Otherwise, we have to grow the LDT allocation
//
}
}
//
// If the process does not yet have an LDT information structure,
// allocate and attach one.
//
OldLdt = NULL;
if (!Process->LdtInformation) {
ProcessLdtInformation = ExAllocatePool(
PagedPool,
sizeof(LDTINFORMATION)
);
if (ProcessLdtInformation == NULL) {
goto SetLdtEntriesCleanup;
}
Process->LdtInformation = ProcessLdtInformation;
ProcessLdtInformation->Size = 0L;
ProcessLdtInformation->AllocatedSize = 0L;
ProcessLdtInformation->Ldt = NULL;
}
//
// Now, we either need to create or grow an LDT, so allocate some
// memory, and copy as necessary
//
AllocatedSize = (LdtSize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
Ldt = ExAllocatePool(
PagedPool,
AllocatedSize
);
if (Ldt) {
RtlZeroMemory(
Ldt,
AllocatedSize
);
} else {
goto SetLdtEntriesCleanup;
}
if (ProcessLdtInformation->Ldt) {
//
// copy the contents of the old ldt
//
RtlMoveMemory(
Ldt,
ProcessLdtInformation->Ldt,
ProcessLdtInformation->Size
);
try {
PsChargePoolQuota(
Process,
PagedPool,
AllocatedSize - ProcessLdtInformation->AllocatedSize
);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
ExFreePool(Ldt);
Ldt = NULL;
}
if (Ldt == NULL) {
goto SetLdtEntriesCleanup;
}
} else {
try {
PsChargePoolQuota(
Process,
PagedPool,
AllocatedSize
);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
ExFreePool(Ldt);
Ldt = NULL;
}
if (Ldt == NULL) {
goto SetLdtEntriesCleanup;
}
}
OldLdt = ProcessLdtInformation->Ldt;
ProcessLdtInformation->Size = LdtSize;
ProcessLdtInformation->AllocatedSize = AllocatedSize;
ProcessLdtInformation->Ldt = Ldt;
//
// Set the selectors in the LDT
//
if (Selector0) {
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)]))) = Entry0Low;
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)])) + 1) =
Entry0Hi;
}
if (Selector1) {
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)]))) = Entry1Low;
*((PULONG)(&(ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)])) + 1) =
Entry1Hi;
}
//
// Set the LDT for the process
//
Ke386SetLdtProcess(
&(Process->Pcb),
ProcessLdtInformation->Ldt,
ProcessLdtInformation->Size
);
//
// Cleanup and exit
//
Status = STATUS_SUCCESS;
SetLdtEntriesCleanup:
if (OldLdt) {
ExFreePool(OldLdt);
}
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT(( MutexState == 0 ));
return Status;
}