|
|
/*++
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 nonpaged 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.
Note that the LDT must be kept in nonpaged memory because the EXIT_ALL macros that return from traps and interrupts pop ds (which may be an LDT selector) and then other registers. With interrupts disabled.
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 user's 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=0, Start=0; LONG MutexStatus; PLDTINFORMATION ProcessLdtInfo;
PAGED_CODE();
//
// Verify the parameters
//
if (LdtInformationLength < 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) { return GetExceptionCode (); }
//
// 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) { RtlCopyMemory( &(LdtInformation->LdtEntries), (PCHAR)ProcessLdtInfo->Ldt + Start, CopyLength ); }
} except(EXCEPTION_EXECUTE_HANDLER) { MutexStatus = KeReleaseMutex( &LdtMutex, FALSE ); ASSERT(( MutexStatus == 0 )); return GetExceptionCode (); }
} else {
//
// There is no Ldt
//
CopyLength = 0; try { LdtInformation->Length = 0; } except(EXCEPTION_EXECUTE_HANDLER) { MutexStatus = KeReleaseMutex( &LdtMutex, FALSE ); ASSERT(( MutexStatus == 0 )); return GetExceptionCode (); }
}
//
// 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){ MutexStatus = KeReleaseMutex( &LdtMutex, FALSE ); ASSERT(( MutexStatus == 0 )); return GetExceptionCode (); } }
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, NewSize; LONG MutexState; ULONG Length=0; PLDT_ENTRY OldLdt = NULL; NTSTATUS Status; PLDTINFORMATION ProcessLdtInfo; PLDT_ENTRY Ldt;
PAGED_CODE();
//
// Verify the parameters
//
if (LdtSizeLength != 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){ return GetExceptionCode (); }
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 );
NewSize = ProcessLdtInfo->AllocatedSize;
MutexState = KeReleaseMutex( &LdtMutex, FALSE );
ASSERT((MutexState == 0));
//
// If we resized the Ldt, free the old one and reduce the quota charge
//
if (OldLdt) { ExFreePool( OldLdt );
PsReturnProcessNonPagedPoolQuota (Process, OldSize - NewSize); }
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=NULL; PLDTINFORMATION ProcessLdtInfo; PLDT_ENTRY Ldt;
PAGED_CODE();
if (LdtInformationLength < sizeof (PROCESS_LDT_INFORMATION)) { return STATUS_INFO_LENGTH_MISMATCH; }
Status = STATUS_SUCCESS; LdtInfo = ExAllocatePoolWithTag (NonPagedPool, LdtInformationLength, 'dLsP');
if (LdtInfo == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
//
// alocate a local buffer to capture the ldt information to
//
try { //
// Copy the information the user is supplying
//
RtlCopyMemory (LdtInfo, LdtInformation, LdtInformationLength);
} except (EXCEPTION_EXECUTE_HANDLER) { ExFreePool (LdtInfo); Status = GetExceptionCode (); return Status; }
//
// Verify that the Start and Length are plausible
//
if (LdtInfo->Start & 0xFFFF0000) { ExFreePool (LdtInfo); return STATUS_INVALID_LDT_OFFSET; }
if (LdtInfo->Length & 0xFFFF0000) { ExFreePool (LdtInfo); 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)) { ExFreePool (LdtInfo); 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 = ExAllocatePoolWithTag (NonPagedPool, sizeof(LDTINFORMATION), 'dLsP'); 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);
PsReturnProcessNonPagedPoolQuota (Process, 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; }
Status = PsChargeProcessNonPagedPoolQuota (Process, AllocatedSize);
if (!NT_SUCCESS (Status)) { ExFreePool (Ldt); Ldt = NULL; 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; }
Status = PsChargeProcessNonPagedPoolQuota (Process, AllocatedSize);
if (!NT_SUCCESS (Status)) { ExFreePool (Ldt); Ldt = NULL; goto SetInfoCleanup; } PsReturnProcessNonPagedPoolQuota (Process, OldSize);
//
// Swap Ldt information
//
OldLdt = ProcessLdtInfo->Ldt; ProcessLdtInfo->Ldt = Ldt; ProcessLdtInfo->Size = Size; ProcessLdtInfo->AllocatedSize = AllocatedSize;
//
// Put new selectors into the new ldt
//
RtlCopyMemory ((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 nonpaged 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 = ExAllocatePoolWithTag (NonPagedPool, AllocationSize, 'dLsP'); if (NewLdt != NULL) { RtlZeroMemory (NewLdt, AllocationSize); RtlCopyMemory ((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 ((Descriptor->HighWord.Bits.Type&0x14) == 0x14) { if (Descriptor->HighWord.Bits.Default_Big == 1) { ActualLimit = 0xFFFFFFFF; } else { ActualLimit = 0xFFFF; } } else if (Descriptor->HighWord.Bits.Granularity == 0) { ActualLimit = Limit; } else { ActualLimit = (Limit<<12) + 0xFFF; }
//
// See if the segment extends into the kernel address space.
//
if (Base > Base + ActualLimit || ((PVOID)(Base + ActualLimit) > MM_HIGHEST_USER_ADDRESS)) { return FALSE; }
//
// Don't let the reserved field be set.
//
if (Descriptor->HighWord.Bits.Reserved_0 != 0) { 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={0}; PEPROCESS Process; 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){ return GetExceptionCode (); }
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) { return GetExceptionCode (); } } 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
RtlCopyMemory (&(((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) { Status = GetExceptionCode (); }
MutexState = KeReleaseMutex (&LdtMutex, FALSE); ASSERT ((MutexState == 0)); }
return Status; }
VOID PspDeleteLdt( IN PEPROCESS Process ) /*++
Routine Description:
This routine frees the nonpaged 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) { PsReturnProcessNonPagedPoolQuota (Process, LdtInformation->AllocatedSize); 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 LdtSize, AllocatedSize; NTSTATUS Status; PEPROCESS Process; LDT_ENTRY Descriptor[2]; PLDT_ENTRY Ldt, OldLdt; PLDTINFORMATION ProcessLdtInformation; LONG MutexState; ULONG Selector1Index;
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
//
Selector1Index = 0; if (Selector0) {
Selector1Index = 1;
*((PULONG)(&Descriptor[0])) = Entry0Low; *(((PULONG)(&Descriptor[0])) + 1) = Entry0Hi;
//
// Validate the descriptor
//
if (!PspIsDescriptorValid (&Descriptor[0])) { return STATUS_INVALID_LDT_DESCRIPTOR; } }
//
// Verify descriptor 1
//
if (Selector1) { *((PULONG)(&Descriptor[Selector1Index])) = Entry1Low; *(((PULONG)(&Descriptor[Selector1Index])) + 1) = Entry1Hi;
//
// Validate the descriptor
//
if (!PspIsDescriptorValid (&Descriptor[Selector1Index])) { return STATUS_INVALID_LDT_DESCRIPTOR; } }
//
// 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();
//
// Acquire the LDT mutex.
//
Status = KeWaitForSingleObject( &LdtMutex, Executive, KernelMode, FALSE, NULL ); if (!NT_SUCCESS (Status)) { return Status; }
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) {
Ke386SetDescriptorProcess( &(Process->Pcb), Selector0, Descriptor[0] ); }
if (Selector1) {
Ke386SetDescriptorProcess( &(Process->Pcb), Selector1, Descriptor[Selector1Index] ); }
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) { *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)]) = Descriptor[0]; }
if (Selector1) { *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)]) = Descriptor[Selector1Index]; }
//
// 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 = ExAllocatePoolWithTag (NonPagedPool, sizeof(LDTINFORMATION), 'dLsP'); 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 = ExAllocatePoolWithTag (NonPagedPool, AllocatedSize, 'dLsP');
if (Ldt) { RtlZeroMemory (Ldt, AllocatedSize); } else { goto SetLdtEntriesCleanup; }
if (ProcessLdtInformation->Ldt) { //
// copy the contents of the old ldt
//
RtlCopyMemory (Ldt, ProcessLdtInformation->Ldt, ProcessLdtInformation->Size);
Status = PsChargeProcessNonPagedPoolQuota (Process, AllocatedSize); if (!NT_SUCCESS (Status)) { ExFreePool (Ldt); Ldt = NULL; } else { PsReturnProcessNonPagedPoolQuota (Process, ProcessLdtInformation->AllocatedSize); }
if (Ldt == NULL) { goto SetLdtEntriesCleanup; }
} else { Status = PsChargeProcessNonPagedPoolQuota (Process, AllocatedSize); if (!NT_SUCCESS (Status)) { 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) { *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector0/sizeof(LDT_ENTRY)]) = Descriptor[0]; }
if (Selector1) { *((PLDT_ENTRY) &ProcessLdtInformation->Ldt[Selector1/sizeof(LDT_ENTRY)]) = Descriptor[Selector1Index]; }
//
// 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; }
|