1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*++ |
6 | |
7 | |
8 | |
9 | Module Name: |
10 | |
11 | handleapi.cpp |
12 | |
13 | Abstract: |
14 | |
15 | Implementation of the handle management APIs |
16 | |
17 | |
18 | |
19 | --*/ |
20 | |
21 | #include "pal/handleapi.hpp" |
22 | #include "pal/handlemgr.hpp" |
23 | #include "pal/thread.hpp" |
24 | #include "pal/procobj.hpp" |
25 | #include "pal/dbgmsg.h" |
26 | #include "pal/process.h" |
27 | |
28 | using namespace CorUnix; |
29 | |
30 | SET_DEFAULT_DEBUG_CHANNEL(HANDLE); |
31 | |
32 | CAllowedObjectTypes aotDuplicateHandle(TRUE); |
33 | |
34 | PAL_ERROR |
35 | CloseSpecialHandle( |
36 | HANDLE hObject |
37 | ); |
38 | |
39 | /*++ |
40 | Function: |
41 | DuplicateHandle |
42 | |
43 | See MSDN doc. |
44 | |
45 | PAL-specific behavior : |
46 | -Source and Target process needs to be the current process. |
47 | -lpTargetHandle must be non-NULL |
48 | -dwDesiredAccess is ignored |
49 | -bInheritHandle must be FALSE |
50 | -dwOptions must be a combo of DUPLICATE_SAME_ACCESS and |
51 | DUPLICATE_CLOSE_SOURCE |
52 | |
53 | --*/ |
54 | BOOL |
55 | PALAPI |
56 | DuplicateHandle( |
57 | IN HANDLE hSourceProcessHandle, |
58 | IN HANDLE hSourceHandle, |
59 | IN HANDLE hTargetProcessHandle, |
60 | OUT LPHANDLE lpTargetHandle, |
61 | IN DWORD dwDesiredAccess, |
62 | IN BOOL bInheritHandle, |
63 | IN DWORD dwOptions) |
64 | { |
65 | PAL_ERROR palError; |
66 | CPalThread *pThread; |
67 | |
68 | PERF_ENTRY(DuplicateHandle); |
69 | ENTRY("DuplicateHandle( hSrcProcHandle=%p, hSrcHandle=%p, " |
70 | "hTargetProcHandle=%p, lpTargetHandle=%p, dwAccess=%#x, " |
71 | "bInheritHandle=%d, dwOptions=%#x) \n" , hSourceProcessHandle, |
72 | hSourceHandle, hTargetProcessHandle, lpTargetHandle, |
73 | dwDesiredAccess, bInheritHandle, dwOptions); |
74 | |
75 | pThread = InternalGetCurrentThread(); |
76 | |
77 | palError = InternalDuplicateHandle( |
78 | pThread, |
79 | hSourceProcessHandle, |
80 | hSourceHandle, |
81 | hTargetProcessHandle, |
82 | lpTargetHandle, |
83 | dwDesiredAccess, |
84 | bInheritHandle, |
85 | dwOptions |
86 | ); |
87 | |
88 | if (NO_ERROR != palError) |
89 | { |
90 | pThread->SetLastError(palError); |
91 | } |
92 | |
93 | LOGEXIT("DuplicateHandle returns BOOL %d\n" , (NO_ERROR == palError)); |
94 | PERF_EXIT(DuplicateHandle); |
95 | return (NO_ERROR == palError); |
96 | } |
97 | |
98 | PAL_ERROR |
99 | CorUnix::InternalDuplicateHandle( |
100 | CPalThread *pThread, |
101 | HANDLE hSourceProcess, |
102 | HANDLE hSource, |
103 | HANDLE hTargetProcess, |
104 | LPHANDLE phDuplicate, |
105 | DWORD dwDesiredAccess, |
106 | BOOL bInheritHandle, |
107 | DWORD dwOptions |
108 | ) |
109 | { |
110 | PAL_ERROR palError = NO_ERROR; |
111 | IPalObject *pobjSource = NULL; |
112 | |
113 | DWORD source_process_id; |
114 | DWORD target_process_id; |
115 | DWORD cur_process_id; |
116 | |
117 | cur_process_id = GetCurrentProcessId(); |
118 | source_process_id = PROCGetProcessIDFromHandle(hSourceProcess); |
119 | target_process_id = PROCGetProcessIDFromHandle(hTargetProcess); |
120 | |
121 | /* Check validity of process handles */ |
122 | if (0 == source_process_id || 0 == target_process_id) |
123 | { |
124 | ASSERT("Can't duplicate handle: invalid source or destination process" ); |
125 | palError = ERROR_INVALID_PARAMETER; |
126 | goto InternalDuplicateHandleExit; |
127 | } |
128 | |
129 | /* At least source or target process should be the current process. */ |
130 | if (source_process_id != cur_process_id |
131 | && target_process_id != cur_process_id) |
132 | { |
133 | ASSERT("Can't duplicate handle : neither source or destination" |
134 | "processes are from current process" ); |
135 | palError = ERROR_INVALID_PARAMETER; |
136 | goto InternalDuplicateHandleExit; |
137 | } |
138 | |
139 | if (FALSE != bInheritHandle) |
140 | { |
141 | ASSERT("Can't duplicate handle : bInheritHandle is not FALSE.\n" ); |
142 | palError = ERROR_INVALID_PARAMETER; |
143 | goto InternalDuplicateHandleExit; |
144 | } |
145 | |
146 | if (dwOptions & ~(DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) |
147 | { |
148 | ASSERT( |
149 | "Can't duplicate handle : dwOptions is %#x which is not " |
150 | "a subset of (DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE) " |
151 | "(%#x).\n" , |
152 | dwOptions, |
153 | DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); |
154 | palError = ERROR_INVALID_PARAMETER; |
155 | goto InternalDuplicateHandleExit; |
156 | } |
157 | |
158 | if (0 == (dwOptions & DUPLICATE_SAME_ACCESS)) |
159 | { |
160 | ASSERT( |
161 | "Can't duplicate handle : dwOptions is %#x which does not " |
162 | "include DUPLICATE_SAME_ACCESS (%#x).\n" , |
163 | dwOptions, |
164 | DUPLICATE_SAME_ACCESS); |
165 | palError = ERROR_INVALID_PARAMETER; |
166 | goto InternalDuplicateHandleExit; |
167 | } |
168 | |
169 | if (NULL == phDuplicate) |
170 | { |
171 | ASSERT("Can't duplicate handle : lpTargetHandle is NULL.\n" ); |
172 | goto InternalDuplicateHandleExit; |
173 | } |
174 | |
175 | /* Since handles can be remoted to others processes using PAL_LocalHsndleToRemote |
176 | and PAL_RemoteHandleToLocal, DuplicateHandle needs some special handling |
177 | when this scenario occurs. |
178 | |
179 | if hSourceProcessHandle is from another process OR |
180 | hTargetProcessHandle is from another process but both aren't |
181 | ( handled above ) return hSourceHandle. |
182 | */ |
183 | if (source_process_id != cur_process_id |
184 | || target_process_id != cur_process_id) |
185 | { |
186 | *phDuplicate = hSource; |
187 | palError = NO_ERROR; |
188 | goto InternalDuplicateHandleExit; |
189 | } |
190 | |
191 | // |
192 | // Obtain the source IPalObject |
193 | // |
194 | |
195 | if (!HandleIsSpecial(hSource)) |
196 | { |
197 | palError = g_pObjectManager->ReferenceObjectByHandle( |
198 | pThread, |
199 | hSource, |
200 | &aotDuplicateHandle, |
201 | dwDesiredAccess, |
202 | &pobjSource |
203 | ); |
204 | |
205 | if (NO_ERROR != palError) |
206 | { |
207 | ERROR("Unable to get object for source handle %p (%i)\n" , hSource, palError); |
208 | goto InternalDuplicateHandleExit; |
209 | } |
210 | } |
211 | else if (hPseudoCurrentProcess == hSource) |
212 | { |
213 | TRACE("Duplicating process pseudo handle(%p)\n" , hSource); |
214 | |
215 | pobjSource = g_pobjProcess; |
216 | pobjSource->AddReference(); |
217 | } |
218 | else if (hPseudoCurrentThread == hSource) |
219 | { |
220 | TRACE("Duplicating thread pseudo handle(%p)\n" , hSource); |
221 | |
222 | pobjSource = pThread->GetThreadObject(); |
223 | pobjSource->AddReference(); |
224 | } |
225 | else |
226 | { |
227 | ASSERT("Duplication not supported for this special handle (%p)\n" , hSource); |
228 | palError = ERROR_INVALID_HANDLE; |
229 | goto InternalDuplicateHandleExit; |
230 | } |
231 | |
232 | palError = g_pObjectManager->ObtainHandleForObject( |
233 | pThread, |
234 | pobjSource, |
235 | dwDesiredAccess, |
236 | bInheritHandle, |
237 | NULL, |
238 | phDuplicate |
239 | ); |
240 | |
241 | InternalDuplicateHandleExit: |
242 | |
243 | if (NULL != pobjSource) |
244 | { |
245 | pobjSource->ReleaseReference(pThread); |
246 | } |
247 | |
248 | if (dwOptions & DUPLICATE_CLOSE_SOURCE) |
249 | { |
250 | // |
251 | // Since DUPLICATE_CLOSE_SOURCE was specified the source handle |
252 | // MUST be closed, even if an error occurred during the duplication |
253 | // process |
254 | // |
255 | |
256 | TRACE("DuplicateHandle closing source handle %p\n" , hSource); |
257 | InternalCloseHandle(pThread, hSource); |
258 | } |
259 | |
260 | return palError; |
261 | } |
262 | |
263 | /*++ |
264 | Function: |
265 | CloseHandle |
266 | |
267 | See MSDN doc. |
268 | |
269 | Note : according to MSDN, FALSE is returned in case of error. But also |
270 | according to MSDN, closing an invalid handle raises an exception when running a |
271 | debugger [or, alternately, if a special registry key is set]. This behavior is |
272 | not required in the PAL, so we'll always return FALSE. |
273 | --*/ |
274 | BOOL |
275 | PALAPI |
276 | CloseHandle( |
277 | IN OUT HANDLE hObject) |
278 | { |
279 | CPalThread *pThread; |
280 | PAL_ERROR palError; |
281 | |
282 | PERF_ENTRY(CloseHandle); |
283 | ENTRY("CloseHandle (hObject=%p) \n" , hObject); |
284 | |
285 | pThread = InternalGetCurrentThread(); |
286 | |
287 | palError = InternalCloseHandle( |
288 | pThread, |
289 | hObject |
290 | ); |
291 | |
292 | if (NO_ERROR != palError) |
293 | { |
294 | pThread->SetLastError(palError); |
295 | } |
296 | |
297 | LOGEXIT("CloseHandle returns BOOL %d\n" , (NO_ERROR == palError)); |
298 | PERF_EXIT(CloseHandle); |
299 | return (NO_ERROR == palError); |
300 | } |
301 | |
302 | PAL_ERROR |
303 | CorUnix::InternalCloseHandle( |
304 | CPalThread * pThread, |
305 | HANDLE hObject |
306 | ) |
307 | { |
308 | PAL_ERROR palError = NO_ERROR; |
309 | |
310 | if (!HandleIsSpecial(hObject)) |
311 | { |
312 | palError = g_pObjectManager->RevokeHandle( |
313 | pThread, |
314 | hObject |
315 | ); |
316 | } |
317 | else |
318 | { |
319 | palError = CloseSpecialHandle(hObject); |
320 | } |
321 | |
322 | return palError; |
323 | } |
324 | |
325 | PAL_ERROR |
326 | CloseSpecialHandle( |
327 | HANDLE hObject |
328 | ) |
329 | { |
330 | if ((hObject == hPseudoCurrentThread) || |
331 | (hObject == hPseudoCurrentProcess)) |
332 | { |
333 | return NO_ERROR; |
334 | } |
335 | |
336 | return ERROR_INVALID_HANDLE; |
337 | } |
338 | |
339 | |