Windows2000/private/ntos/ps/i386/psldt.c
2020-09-30 17:12:32 +02:00

1037 lines
35 KiB
C

/*++
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.
--*/
#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
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) + sizeof(LDT_ENTRY) < Length) {
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) {
CopyEnd = Start;
} else 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;
}
// Verify that the Start and Length are plausible
if (LdtInfo->Start & 0xFFFF0000) {
return STATUS_INVALID_LDT_OFFSET;
}
if (LdtInfo->Length & 0xFFFF0000) {
return STATUS_INVALID_LDT_SIZE;
}
// Insure that the buffer it large enough to contain the specified number of selectors.
if (LdtInformationLength - sizeof(PROCESS_LDT_INFORMATION) + sizeof(LDT_ENTRY) < LdtInfo->Length) {
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 system descriptor (system bit is 1 == application)
This rules out all gates, etc
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) {
ULONG ActualLimit;
if ( (PVOID)Base > MM_HIGHEST_USER_ADDRESS ) {
return FALSE;
}
ActualLimit = (Limit << (Descriptor->HighWord.Bits.Granularity * 12)) + 0xFFF * Descriptor->HighWord.Bits.Granularity;
if ( (Base > (Base + ActualLimit)) || ((PVOID)(Base + ActualLimit) > MM_HIGHEST_USER_ADDRESS)) {
return FALSE;
}
}
// if descriptor is a system descriptor (which includes gates)
// if bit 4 of the Type field is 0, then it's a system descriptor, and we don't like it.
if ( ! (Descriptor->HighWord.Bits.Type & 0x10)) {
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
--*/
{
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
// Verify the selectors
if ((Selector0 & 0xFFFF0000) || (Selector1 & 0xFFFF0000)) {
return STATUS_INVALID_LDT_DESCRIPTOR;
}
// Change the selector values to indexes into the LDT
Selector0 = Selector0 & ~(RPL_MASK | SELECTOR_TABLE_INDEX);
Selector1 = Selector1 & ~(RPL_MASK | SELECTOR_TABLE_INDEX);
// Verify descriptor 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 descriptor
if (Entry0Hi & DESCRIPTOR_GRAN) {
Limit = (Limit << 12) | 0xFFF;
}
// Base and limit don't matter for NP descriptors, so only check for present descriptors
if (Entry0Hi & DESCRIPTOR_NP) {
// Check descriptor base and limit
if (((PVOID)Base > MM_HIGHEST_USER_ADDRESS) || ((PVOID)Base > (PVOID) (Base + Limit)) || ((PVOID)(Base + Limit) > MM_HIGHEST_USER_ADDRESS))
{
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
// If type and DPL are 0, this is an invalid descriptor, otherwise, we have to check the type (invalid from the standpoint of a descriptor created to generate a GP fault)
if (Entry0Hi & DESCRIPTOR_TYPEDPL) {
// No system descriptors (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 descriptor 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 descriptor
if (Entry1Hi & DESCRIPTOR_GRAN) {
Limit = (Limit << 12) | 0xFFF;
}
// Base and limit don't matter for NP descriptor, so only check for present descriptors
if (Entry1Hi & DESCRIPTOR_NP) {
// Check descriptor base and limit
if (((PVOID)Base > MM_HIGHEST_USER_ADDRESS) || ((PVOID)Base > (PVOID) (Base + Limit)) || ((PVOID)(Base + Limit) > MM_HIGHEST_USER_ADDRESS))
{
return STATUS_INVALID_LDT_DESCRIPTOR;
}
}
// If type and DPL are 0, this is an invalid descriptor, otherwise, we have to check the type (invalid from the standpoint of a descriptor created to generate a GP fault)
if (Entry1Hi & DESCRIPTOR_TYPEDPL) {
// No system descriptors (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 descriptors and return
if (ProcessLdtInformation) {
// If the LDT descriptor 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 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
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;
}