KeStackAttach: Acquire lock before getting thread
[reactos.git] / reactos / ntoskrnl / ke / process.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: process.c,v 1.30 2004/10/17 03:43:26 ion Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/ke/process.c
23 * PURPOSE: Microkernel process management
24 * PROGRAMMER: David Welch (welch@cwcom.net)
25 * PORTABILITY: No.
26 * UPDATE HISTORY:
27 * Created 22/05/98
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ntoskrnl.h>
33 #define NDEBUG
34 #include <internal/debug.h>
35
36 /* FUNCTIONS *****************************************************************/
37
38 /*
39 * @implemented
40 */
41 VOID STDCALL
42 KeAttachProcess (PEPROCESS Process)
43 {
44 KIRQL oldlvl;
45 PETHREAD CurrentThread;
46 ULONG PageDir;
47
48 DPRINT("KeAttachProcess(Process %x)\n",Process);
49
50 CurrentThread = PsGetCurrentThread();
51
52 if (&CurrentThread->ThreadsProcess->Pcb != CurrentThread->Tcb.ApcState.Process)
53 {
54 DPRINT1("Invalid attach (thread is already attached)\n");
55 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
56 }
57 if (&Process->Pcb == CurrentThread->Tcb.ApcState.Process)
58 {
59 DPRINT1("Invalid attach (process is the same)\n");
60 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
61 }
62
63
64 /* The stack and the thread structure of the current process may be
65 located in a page which is not present in the page directory of
66 the process we're attaching to. That would lead to a page fault
67 when this function returns. However, since the processor can't
68 call the page fault handler 'cause it can't push EIP on the stack,
69 this will show up as a stack fault which will crash the entire system.
70 To prevent this, make sure the page directory of the process we're
71 attaching to is up-to-date. */
72
73 MmUpdatePageDir(Process, (PVOID)CurrentThread->Tcb.StackLimit, MM_STACK_SIZE);
74 MmUpdatePageDir(Process, (PVOID)CurrentThread, sizeof(ETHREAD));
75
76 KeRaiseIrql(DISPATCH_LEVEL, &oldlvl);
77
78 KiSwapApcEnvironment(&CurrentThread->Tcb, &Process->Pcb);
79
80 CurrentThread->Tcb.ApcState.Process = &Process->Pcb;
81 PageDir = Process->Pcb.DirectoryTableBase.u.LowPart;
82 DPRINT("Switching process context to %x\n",PageDir);
83 Ke386SetPageTableDirectory(PageDir);
84 KeLowerIrql(oldlvl);
85 }
86
87 /*
88 * @implemented
89 */
90 VOID STDCALL
91 KeDetachProcess (VOID)
92 {
93 KIRQL oldlvl;
94 PETHREAD CurrentThread;
95 ULONG PageDir;
96
97 DPRINT("KeDetachProcess()\n");
98
99 CurrentThread = PsGetCurrentThread();
100
101 if (&CurrentThread->ThreadsProcess->Pcb == CurrentThread->Tcb.ApcState.Process)
102 {
103 DPRINT1("Invalid detach (thread was not attached)\n");
104 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
105 }
106
107 KeRaiseIrql(DISPATCH_LEVEL, &oldlvl);
108
109 KiSwapApcEnvironment(&CurrentThread->Tcb, CurrentThread->Tcb.SavedApcState.Process);
110 PageDir = CurrentThread->Tcb.ApcState.Process->DirectoryTableBase.u.LowPart;
111 Ke386SetPageTableDirectory(PageDir);
112
113 KeLowerIrql(oldlvl);
114 }
115
116 /*
117 * @implemented
118 */
119 BOOLEAN
120 STDCALL
121 KeIsAttachedProcess(
122 VOID
123 )
124 {
125 return KeGetCurrentThread()->ApcStateIndex;
126 }
127
128 /*
129 * @implemented
130 */
131 VOID
132 STDCALL
133 KeStackAttachProcess (
134 IN PKPROCESS Process,
135 OUT PRKAPC_STATE ApcState
136 )
137 {
138 KIRQL OldIrql;
139 PKTHREAD Thread;
140
141 OldIrql = KeAcquireDispatcherDatabaseLock();
142 Thread = KeGetCurrentThread();
143
144 /* Crash system if DPC is being executed! */
145 if (KeIsExecutingDpc()) {
146 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
147 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
148 }
149
150 /* Check if the Target Process is already attached */
151 if (Thread->ApcState.Process == Process) {
152 ApcState->Process = (PKPROCESS)1; /* Meaning already attached to the same Process */
153 } else {
154 /* Check if the Current Thread is already attached */
155 if (Thread->ApcStateIndex != 0) {
156 KeAttachProcess((PEPROCESS)Process); /* FIXME: Re-write function to support stackability and fix it not to use EPROCESS */
157 } else {
158 KeAttachProcess((PEPROCESS)Process);
159 ApcState->Process = NULL; /* FIXME: Re-write function to support stackability and fix it not to use EPROCESS */
160 }
161 }
162
163 /* Return to old IRQL*/
164 KeReleaseDispatcherDatabaseLock(OldIrql);
165 }
166
167 /*
168 * @implemented
169 */
170 VOID
171 STDCALL
172 KeUnstackDetachProcess (
173 IN PRKAPC_STATE ApcState
174 )
175 {
176 KIRQL OldIrql;
177 PKTHREAD Thread;
178 ULONG PageDir;
179
180 /* If the special "We tried to attach to the process already being attached to" flag is there, don't do anything */
181 if (ApcState->Process == (PKPROCESS)1) return;
182
183 Thread = KeGetCurrentThread();
184 OldIrql = KeAcquireDispatcherDatabaseLock();
185
186 /* Sorry Buddy, can't help you if you've got APCs or just aren't attached */
187 if ((Thread->ApcStateIndex == 0) || (Thread->ApcState.KernelApcInProgress)) {
188 DPRINT1("Invalid detach (Thread not Attached, or Kernel APC in Progress!)\n");
189 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
190 }
191
192 /* Restore the Old APC State if a Process was present */
193 if (ApcState->Process) {
194 RtlMoveMemory(ApcState, &Thread->ApcState, sizeof(KAPC_STATE));
195 } else {
196 /* The ApcState parameter is useless, so use the saved data and reset it */
197 RtlMoveMemory(&Thread->SavedApcState, &Thread->ApcState, sizeof(KAPC_STATE));
198 Thread->SavedApcState.Process = NULL;
199 Thread->ApcStateIndex = 0;
200 Thread->ApcStatePointer[0] = &Thread->ApcState;
201 Thread->ApcStatePointer[1] = &Thread->SavedApcState;
202 }
203
204 /* Do the Actual Swap */
205 KiSwapApcEnvironment(Thread, Thread->SavedApcState.Process);
206 PageDir = Thread->ApcState.Process->DirectoryTableBase.u.LowPart;
207 Ke386SetPageTableDirectory(PageDir);
208
209 /* Return to old IRQL*/
210 KeReleaseDispatcherDatabaseLock(OldIrql);
211 }
212
213 /* EOF */