Merge pull request #17531 from BruceForstall/SpmiProtectJitStartup
[platform/upstream/coreclr.git] / src / debug / di / publish.cpp
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 // File: publish.cpp
6 // 
7
8 //
9 //*****************************************************************************
10
11
12 #include "stdafx.h"
13 #ifdef FEATURE_DBG_PUBLISH
14
15 #include "check.h"
16
17 #include <tlhelp32.h>
18 #include "wtsapi32.h"
19
20 #ifndef SM_REMOTESESSION
21 #define SM_REMOTESESSION 0x1000
22 #endif
23
24 #include "corpriv.h"
25 #include "../../dlls/mscorrc/resource.h"
26 #include <limits.h>
27
28 // Publish shares header files with the rest of ICorDebug.
29 // ICorDebug should not call ReadProcessMemory & other APIs directly, it should instead go through
30 // the Data-target. ICD headers #define these APIs to help enforce this. 
31 // Since Publish is separate and doesn't use data-targets, it can access the APIs directly. 
32 // see code:RSDebuggingInfo#UseDataTarget
33 #undef ReadProcessMemory
34
35 //****************************************************************************
36 //************ App Domain Publishing Service API Implementation **************
37 //****************************************************************************
38
39 // This function enumerates all the process in the system and returns
40 // their PIDs
41 BOOL GetAllProcessesInSystem(DWORD *ProcessId,
42                              DWORD dwArraySize,
43                              DWORD *pdwNumEntries)
44 {    
45     HandleHolder hSnapshotHolder;
46
47 #if !defined(FEATURE_CORESYSTEM)
48     // Load the dll "kernel32.dll".
49     HModuleHolder hDll = WszLoadLibrary(W("kernel32"));
50     _ASSERTE(hDll != NULL);
51
52     if (hDll == NULL)
53     {
54         LOG((LF_CORDB, LL_INFO1000,
55                 "Unable to load the dll for enumerating processes. "
56                 "LoadLibrary (kernel32.dll) failed.\n"));
57         return FALSE;
58     }
59 #else
60         // Load the dll "api-ms-win-obsolete-kernel32-l1-1-0.dll".
61     HModuleHolder hDll = WszLoadLibrary(W("api-ms-win-obsolete-kernel32-l1-1-0.dll"));
62     _ASSERTE(hDll != NULL);
63
64     if (hDll == NULL)
65     {
66         LOG((LF_CORDB, LL_INFO1000,
67                 "Unable to load the dll for enumerating processes. "
68                 "LoadLibrary (api-ms-win-obsolete-kernel32-l1-1-0.dll) failed.\n"));
69         return FALSE;
70     }
71 #endif
72   
73     
74     // Create the Process' Snapshot
75     // Get the pointer to the requested function
76     FARPROC pProcAddr = GetProcAddress(hDll, "CreateToolhelp32Snapshot");
77
78     // If the proc address was not found, return error
79     if (pProcAddr == NULL)
80     {
81         LOG((LF_CORDB, LL_INFO1000,
82                 "Unable to enumerate processes in the system. "
83                 "GetProcAddr (CreateToolhelp32Snapshot) failed.\n"));
84         return FALSE;
85     }
86
87     
88
89     // Handle from CreateToolHelp32Snapshot must be freed via CloseHandle().    
90     typedef HANDLE CREATETOOLHELP32SNAPSHOT(DWORD, DWORD);
91
92     HANDLE hSnapshot =
93             ((CREATETOOLHELP32SNAPSHOT *)pProcAddr)(TH32CS_SNAPPROCESS, NULL);
94
95     if (hSnapshot == INVALID_HANDLE_VALUE)
96     {
97         LOG((LF_CORDB, LL_INFO1000,
98                 "Unable to create snapshot of processes in the system. "
99                 "CreateToolhelp32Snapshot() failed.\n"));
100         return FALSE;
101     }
102     // HandleHolder doesn't deal with INVALID_HANDLE_VALUE, so we only assign if we have a legal value.
103     hSnapshotHolder.Assign(hSnapshot);
104
105     // Get the first process in the process list
106     // Get the pointer to the requested function
107     pProcAddr = GetProcAddress(hDll, "Process32First");
108
109     // If the proc address was not found, return error
110     if (pProcAddr == NULL)
111     {
112         LOG((LF_CORDB, LL_INFO1000,
113                 "Unable to enumerate processes in the system. "
114                 "GetProcAddr (Process32First) failed.\n"));
115         return FALSE;
116     }
117
118     PROCESSENTRY32  PE32;
119
120     // need to initialize the dwSize field before calling Process32First
121     PE32.dwSize = sizeof (PROCESSENTRY32);
122
123     typedef BOOL PROCESS32FIRST(HANDLE, LPPROCESSENTRY32);
124
125     BOOL succ =
126             ((PROCESS32FIRST *)pProcAddr)(hSnapshot, &PE32);
127
128     if (succ != TRUE)
129     {
130         LOG((LF_CORDB, LL_INFO1000,
131                 "Unable to create snapshot of processes in the system. "
132                 "Process32First() returned FALSE.\n"));
133         return FALSE;
134     }
135
136
137     // Loop over and get all the remaining processes
138     // Get the pointer to the requested function
139     pProcAddr = GetProcAddress(hDll, "Process32Next");
140
141     // If the proc address was not found, return error
142     if (pProcAddr == NULL)
143     {
144         LOG((LF_CORDB, LL_INFO1000,
145                 "Unable to enumerate processes in the system. "
146                 "GetProcAddr (Process32Next) failed.\n"));
147         return FALSE;
148     }
149
150     typedef BOOL PROCESS32NEXT(HANDLE, LPPROCESSENTRY32);
151
152     int iIndex = 0;
153
154     do
155     {
156         ProcessId [iIndex++] = PE32.th32ProcessID;
157
158         succ = ((PROCESS32NEXT *)pProcAddr)(hSnapshot, &PE32);
159
160     } while ((succ == TRUE) && (iIndex < (int)dwArraySize));
161
162     // I would like to know if we're running more than 512 processes on Win95!!
163     _ASSERTE (iIndex < (int)dwArraySize);
164
165     *pdwNumEntries = iIndex;
166
167     // If we made it this far, we succeeded
168     return TRUE;
169 }
170
171
172 // We never want to wait infinite on an object that we can't verify.
173 // Wait with a timeout.
174 const DWORD SAFETY_TIMEOUT = 2000;
175
176 // ******************************************
177 // CorpubPublish
178 // ******************************************
179
180 CorpubPublish::CorpubPublish()
181     : CordbCommonBase(0),
182     m_fpGetModuleFileNameEx(NULL)
183 {
184     // Try to get psapi!GetModuleFileNameExW once, and then every process object can use it.
185     // If we can't get it, then we'll fallback to getting information from the IPC block.
186 #if !defined(FEATURE_CORESYSTEM)
187     m_hPSAPIdll = WszLoadLibrary(W("psapi.dll"));
188 #else
189         m_hPSAPIdll = WszLoadLibrary(W("api-ms-win-obsolete-psapi-l1-1-0.dll"));
190 #endif
191
192     if (m_hPSAPIdll != NULL)
193     {
194         m_fpGetModuleFileNameEx = (FPGetModuleFileNameEx*) GetProcAddress(m_hPSAPIdll, "GetModuleFileNameExW");
195     }
196
197     CordbCommonBase::InitializeCommon();
198 }
199
200 CorpubPublish::~CorpubPublish()
201 {
202     // m_hPSAPIdll is a module holder, so the dtor will free it automatically for us.
203 }
204
205
206 COM_METHOD CorpubPublish::QueryInterface(REFIID id, void **ppInterface)
207 {
208     if (id == IID_ICorPublish)
209         *ppInterface = (ICorPublish*)this;
210     else if (id == IID_IUnknown)
211         *ppInterface = (IUnknown*)(ICorPublish*)this;
212     else
213     {
214         *ppInterface = NULL;
215         return E_NOINTERFACE;
216     }
217
218     ExternalAddRef();
219     return S_OK;
220 }
221
222
223 COM_METHOD CorpubPublish::EnumProcesses(COR_PUB_ENUMPROCESS Type,
224                                         ICorPublishProcessEnum **ppIEnum)
225 {
226     HRESULT hr = E_FAIL;
227     CorpubProcess* pProcessList = NULL  ;
228     CorpubProcessEnum* pProcEnum = NULL;
229     *ppIEnum = NULL;
230
231     if( Type != COR_PUB_MANAGEDONLY )
232     {
233         hr = E_INVALIDARG;
234         goto exit;
235     }
236
237     // call function to get PIDs for all processes in the system
238 #define MAX_PROCESSES  512
239
240     DWORD ProcessId[MAX_PROCESSES];
241     DWORD dwNumProcesses = 0;
242     if( !GetAllProcessesInSystem(ProcessId, MAX_PROCESSES, &dwNumProcesses) )
243     {
244         hr = E_FAIL;
245         goto exit;
246     }
247
248     // iterate over all the processes to fetch all the managed processes
249     for (int i = 0; i < (int)dwNumProcesses; i++)
250     {
251         CorpubProcess *pProcess = NULL;
252         hr = GetProcessInternal( ProcessId[i], &pProcess );
253         if( FAILED(hr) )
254         {
255             _ASSERTE( pProcess == NULL );
256             goto exit;      // a serious error has occurred, abort
257         }
258
259         if( hr == S_OK )
260         {
261             // Success, Add the process to the list.
262             _ASSERTE( pProcess != NULL );
263             pProcess->SetNext( pProcessList );
264             pProcessList = pProcess;
265         }
266         else
267         {
268             // Ignore this process (isn't managed, or shut down, etc.)
269             _ASSERTE( pProcess == NULL );
270         }
271     }
272
273     // create and return the ICorPublishProcessEnum
274     pProcEnum = new (nothrow) CorpubProcessEnum(pProcessList);
275     if (pProcEnum == NULL)
276     {
277         hr = E_OUTOFMEMORY;
278         goto exit;
279     }
280     pProcEnum->AddRef();
281
282     hr = pProcEnum->QueryInterface(IID_ICorPublishProcessEnum, (void**)ppIEnum);
283     if( FAILED(hr) )
284     {
285         goto exit;
286     }
287
288     hr = S_OK;
289
290 exit:
291     // release our handle on the process objects
292     while (pProcessList != NULL)
293     {
294         CorpubProcess *pTmp = pProcessList;
295         pProcessList = pProcessList->GetNextProcess();
296         pTmp->Release();
297     }
298     if( pProcEnum != NULL )
299     {
300         pProcEnum->Release();
301         pProcEnum = NULL;
302     }
303
304     return hr;
305 }
306
307
308 HRESULT CorpubPublish::GetProcess(unsigned pid,
309                                   ICorPublishProcess **ppProcess)
310 {
311     *ppProcess = NULL;
312
313     // Query for this specific process (even if we've already handed out a
314     // now-stale process object for this pid)
315     CorpubProcess * pProcess = NULL;
316     HRESULT hr = GetProcessInternal( pid, &pProcess );
317     if( hr != S_OK )
318     {
319         // Couldn't get this process (doesn't exist, or isn't managed)
320         _ASSERTE( pProcess == NULL );
321         if( FAILED(hr) )
322         {
323             return hr;      // there was a serious error trying to get this process info
324         }
325         return E_INVALIDARG;  // this process doesn't exist, isn't managed or is shutting down
326     }
327
328     // QI to ICorPublishProcess and return it
329     _ASSERTE( pProcess != NULL );
330     hr = pProcess->QueryInterface(IID_ICorPublishProcess, (void**)ppProcess);
331     pProcess->Release();
332     return hr;
333 }
334
335
336 // Attempts to create a CorpubProcess object for a specific managed process
337 // On success returns S_OK and sets ppProcess to a new AddRef'd CorpubProcess
338 // object.  Otherwise, returns S_FALSE if the process isn't managed or if it has
339 // terminated (i.e. it should be ignored), or and error code on a serious failure.
340 HRESULT CorpubPublish::GetProcessInternal(
341                                     unsigned pid,
342                                     CorpubProcess **ppProcess )
343 {
344 #if defined(FEATURE_DBGIPC_TRANSPORT_DI)
345     return E_NOTIMPL;
346
347 #else // !FEATURE_DBGIPC_TRANSPORT_DI
348     HRESULT hr = S_OK;
349     *ppProcess = NULL;
350
351     NewHolder<IPCReaderInterface> pIPCReader( new (nothrow) IPCReaderInterface() );
352     if (pIPCReader == NULL)
353     {
354         LOG((LF_CORDB, LL_INFO100, "CP::EP: Failed to allocate memory for IPCReaderInterface.\n"));
355         return E_OUTOFMEMORY;
356     }
357
358     // See if it is a managed process by trying to open the shared
359     // memory block.
360     hr = pIPCReader->OpenLegacyPrivateBlockTempV4OnPid(pid);
361     if (FAILED(hr))
362     {
363         return S_FALSE;     // Not a managed process
364     }
365
366     // Get the AppDomainIPCBlock
367     AppDomainEnumerationIPCBlock *pAppDomainCB = pIPCReader->GetAppDomainBlock();
368     if (pAppDomainCB == NULL)
369     {
370         LOG((LF_CORDB, LL_INFO1000, "CP::EP: Failed to obtain AppDomainIPCBlock.\n"));
371         return S_FALSE;
372     }
373
374     // Get the process handle.
375     HANDLE hProcess = OpenProcess((PROCESS_VM_READ | 
376                                    PROCESS_QUERY_INFORMATION | 
377                                    PROCESS_DUP_HANDLE | 
378                                    SYNCHRONIZE),
379                                   FALSE, pid);
380     if (hProcess == NULL)
381     {
382         LOG((LF_CORDB, LL_INFO1000, "CP::EP: OpenProcess() returned NULL handle.\n"));
383         return S_FALSE;
384     }
385
386     // If the mutex isn't filled in, the CLR is either starting up or shutting down
387     if (pAppDomainCB->m_hMutex == NULL)
388     {
389         LOG((LF_CORDB, LL_INFO1000, "CP::EP: IPC block isn't properly filled in.\n"));
390         return S_FALSE;
391     }
392
393     // Dup the valid mutex handle into this process.
394     HANDLE hMutex;
395     if( !pAppDomainCB->m_hMutex.DuplicateToLocalProcess(hProcess, &hMutex) )
396     {
397         return S_FALSE;
398     }
399
400     // Acquire the mutex, only waiting two seconds.
401     // We can't actually gaurantee that the target put a mutex object in here.
402     DWORD dwRetVal = WaitForSingleObject(hMutex, SAFETY_TIMEOUT);
403
404     if (dwRetVal == WAIT_OBJECT_0)
405     {
406         // Make sure the mutex handle is still valid. If
407         // its not, then we lost a shutdown race.
408         if (pAppDomainCB->m_hMutex == NULL)
409         {
410             LOG((LF_CORDB, LL_INFO1000, "CP::EP: lost shutdown race, skipping...\n"));
411
412             ReleaseMutex(hMutex);
413             CloseHandle(hMutex);
414             return S_FALSE;
415         }
416     }
417     else
418     {
419         // Again, landing here is most probably a shutdown race. Its okay, though...
420         LOG((LF_CORDB, LL_INFO1000, "CP::EP: failed to get IPC mutex.\n"));
421
422         if (dwRetVal == WAIT_ABANDONED)
423         {
424             ReleaseMutex(hMutex);
425         }
426         CloseHandle(hMutex);
427         return S_FALSE;
428     }
429     // Beware: if the target pid is not properly honoring the mutex, the data in the
430     // IPC block may still shift underneath us.
431
432     // If we get here, then hMutex is held by this process.
433
434     // Now create the CorpubProcess object for the ProcessID
435     CorpubProcess *pProc = new (nothrow) CorpubProcess(pid,
436                                            true,
437                                            hProcess,
438                                            hMutex,
439                                            pAppDomainCB,
440                                            pIPCReader,
441                                            m_fpGetModuleFileNameEx);
442
443     // Release our lock on the IPC block.
444     ReleaseMutex(hMutex);
445
446     if (pProc == NULL)
447     {
448         return E_OUTOFMEMORY;
449     }
450     pIPCReader.SuppressRelease();
451
452     // Success, return the Process object
453     pProc->AddRef();
454     *ppProcess = pProc;
455     return S_OK;
456
457 #endif // FEATURE_DBGIPC_TRANSPORT_DI
458 }
459
460
461
462 // ******************************************
463 // CorpubProcess
464 // ******************************************
465
466 // Constructor
467 CorpubProcess::CorpubProcess(DWORD dwProcessId,
468                              bool fManaged,
469                              HANDLE hProcess,
470                              HANDLE hMutex,
471                              AppDomainEnumerationIPCBlock *pAD,
472 #if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
473                              IPCReaderInterface *pIPCReader,
474 #endif // !FEATURE_DBGIPC_TRANSPORT_DI
475                              FPGetModuleFileNameEx * fpGetModuleFileNameEx)
476     : CordbCommonBase(0, enumCorpubProcess),
477       m_dwProcessId(dwProcessId),
478       m_fIsManaged(fManaged),
479       m_hProcess(hProcess),
480       m_hMutex(hMutex),
481       m_AppDomainCB(pAD),
482 #if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
483       m_pIPCReader(pIPCReader),
484 #endif // !FEATURE_DBGIPC_TRANSPORT_DI
485       m_pNext(NULL)
486 {
487     {
488         // First try to get the process name from the OS. That can't be spoofed by badly formed IPC block.
489         // psapi!GetModuleFileNameExW can get that, but it's not available on all platforms so we
490         // need to load it dynamically.
491         if (fpGetModuleFileNameEx != NULL)
492         {
493             // MSDN is very confused about whether the lenght is in bytes (MSDN 2002) or chars (MSDN 2004).
494             // We err on the safe side by having buffer that's twice as large, and ignoring
495             // the units on the return value.
496             WCHAR szName[MAX_LONGPATH * sizeof(WCHAR)];
497
498             DWORD lenInCharsOrBytes = MAX_LONGPATH*sizeof(WCHAR);
499
500             // Pass NULL module handle to get "Main Module", which will give us the process name.
501             DWORD ret = (*fpGetModuleFileNameEx) (hProcess, NULL, szName, lenInCharsOrBytes);
502             if (ret > 0)
503             {
504                 // Recompute string length because we don't know if 'ret' is in bytes or char.
505                 SIZE_T len = wcslen(szName) + 1;
506                 m_szProcessName = new (nothrow) WCHAR[len];
507                 if (m_szProcessName != NULL)
508                 {
509                     wcscpy_s(m_szProcessName, len, szName);
510                     goto exit;
511                 }
512             }
513         }
514
515         // This is a security feature on WinXp + above, so make sure it worked there.
516         CONSISTENCY_CHECK_MSGF(FALSE, ("On XP/2k03 OSes + above, we should have been able to get\n"
517             "the module name from psapi!GetModuleFileNameEx. fp=0x%p\n.", fpGetModuleFileNameEx));
518     }
519     // We couldn't get it from the OS, so fallthrough to getting it from the IPC block.
520
521     // Fetch the process name from the AppDomainIPCBlock
522     _ASSERTE (pAD->m_szProcessName != NULL);
523
524     if (pAD->m_szProcessName == NULL)
525         m_szProcessName = NULL;
526     else
527     {
528         SIZE_T nBytesRead;
529
530         _ASSERTE(pAD->m_iProcessNameLengthInBytes > 0);
531
532         // Note: this assumes we're reading the null terminator from
533         // the IPC block.
534         m_szProcessName = (WCHAR*) new (nothrow) char[pAD->m_iProcessNameLengthInBytes];
535
536         if (m_szProcessName == NULL)
537         {
538             LOG((LF_CORDB, LL_INFO1000,
539              "CP::CP: Failed to allocate memory for ProcessName.\n"));
540
541             goto exit;
542         }
543
544         BOOL bSucc = ReadProcessMemory(hProcess,
545                                         pAD->m_szProcessName,
546                                         m_szProcessName,
547                                         pAD->m_iProcessNameLengthInBytes,
548                                         &nBytesRead);
549
550         if ((bSucc == 0) ||
551             (nBytesRead != (SIZE_T)pAD->m_iProcessNameLengthInBytes))
552         {
553             // The EE may have done a rude exit
554             LOG((LF_CORDB, LL_INFO1000,
555              "CP::EAD: ReadProcessMemory (ProcessName) failed.\n"));
556         }
557     }
558
559 exit:
560     ;
561 }
562
563 CorpubProcess::~CorpubProcess()
564 {
565     delete [] m_szProcessName;
566 #if !defined(FEATURE_DBGIPC_TRANSPORT_DI)
567     delete m_pIPCReader;
568 #endif // !FEATURE_DBGIPC_TRANSPORT_DI
569     CloseHandle(m_hProcess);
570     CloseHandle(m_hMutex);
571 }
572
573
574 HRESULT CorpubProcess::QueryInterface(REFIID id, void **ppInterface)
575 {
576     if (id == IID_ICorPublishProcess)
577         *ppInterface = (ICorPublishProcess*)this;
578     else if (id == IID_IUnknown)
579         *ppInterface = (IUnknown*)(ICorPublishProcess*)this;
580     else
581     {
582         *ppInterface = NULL;
583         return E_NOINTERFACE;
584     }
585
586     AddRef();
587     return S_OK;
588 }
589
590
591 // Helper to tell if this process has exited.
592 bool CorpubProcess::IsExited()
593 {
594     DWORD res = WaitForSingleObject(this->m_hProcess, 0);
595     return (res == WAIT_OBJECT_0);
596 }
597
598
599 HRESULT CorpubProcess::IsManaged(BOOL *pbManaged)
600 {
601     *pbManaged = (m_fIsManaged == true) ? TRUE : FALSE;
602
603     return S_OK;
604 }
605
606 // Helper.
607 // Allocates a local buffer (using 'new') and fills it by copying it from remote memory.
608 // Returns:
609 // - on success, S_OK, *ppNewLocalBuffer points to a newly allocated buffer containing
610 //               the full copy from remote memoy. Caller must use 'delete []' to free this.
611 // - on failure, a failing HR. No memory is allocated.
612 HRESULT AllocateAndReadRemoteBuffer(
613     HANDLE hProcess,
614     void * pRemotePtr,
615     SIZE_T cbSize, // size of buffer to allocate + copy.
616     BYTE * * ppNewLocalBuffer
617 )
618 {
619     _ASSERTE(ppNewLocalBuffer != NULL);
620     *ppNewLocalBuffer = NULL;
621
622
623     if (pRemotePtr == NULL)
624     {
625         return E_INVALIDARG;
626     }
627
628     BYTE *pLocalBuffer = new (nothrow) BYTE[cbSize];
629
630     if (pLocalBuffer == NULL)
631     {
632         _ASSERTE(!"Failed to alloc memory. Likely size is bogusly large, perhaps from an attacker.");
633         return E_OUTOFMEMORY;
634     }
635
636     SIZE_T nBytesRead;
637
638     // Need to read in the remote process' memory
639     BOOL bSucc = ReadProcessMemory(hProcess,
640                                     pRemotePtr,
641                                     pLocalBuffer, cbSize,
642                                     &nBytesRead);
643
644     if ((bSucc == 0) || (nBytesRead != cbSize))
645     {
646         // The EE may have done a rude exit
647         delete [] pLocalBuffer;
648         return E_FAIL;
649     }
650
651     *ppNewLocalBuffer = pLocalBuffer;
652     return S_OK;
653 }
654
655 // Wrapper around AllocateAndReadRemoteBuffer,
656 // to ensure that we're reading an remote-null terminated string.
657 // Ensures that string is null-terminated.
658 HRESULT AllocateAndReadRemoteString(
659     HANDLE hProcess,
660     void * pRemotePtr,
661     SIZE_T cbSize, // size of buffer to allocate + copy.
662     __deref_out_bcount(cbSize) WCHAR * * ppNewLocalBuffer
663     )
664 {
665     // Make sure buffer has right geometry.
666     if (cbSize < 0)
667     {
668         return E_INVALIDARG;
669     }
670
671     // If it's not on a WCHAR boundary, then we may have a 1-byte buffer-overflow.
672     SIZE_T ceSize = cbSize / sizeof(WCHAR);
673     if ((ceSize * sizeof(WCHAR)) != cbSize)
674     {
675         return E_INVALIDARG;
676     }
677
678     // It should at least have 1 char for the null terminator.
679     if (ceSize < 1)
680     {
681         return E_INVALIDARG;
682     }
683
684
685     HRESULT hr = AllocateAndReadRemoteBuffer(hProcess, pRemotePtr, cbSize, (BYTE**) ppNewLocalBuffer);
686     if (SUCCEEDED(hr))
687     {
688         // Ensure that the string we just read is actually null terminated.
689         // We can't call wcslen() on it yet, since that may AV on a non-null terminated string.
690         WCHAR * pString = *ppNewLocalBuffer;
691
692         if (pString[ceSize - 1] == W('\0'))
693         {
694             // String is null terminated.
695             return S_OK;
696         }
697         pString[ceSize - 1] = W('\0');
698
699         SIZE_T ceTestLen = wcslen(pString);
700         if (ceTestLen == ceSize - 1)
701         {
702             // String was not previously null-terminated.
703             delete [] ppNewLocalBuffer;
704             return E_INVALIDARG;
705         }
706     }
707     return S_OK;
708 }
709
710 //
711 // Enumerate the list of known application domains in the target process.
712 //
713 HRESULT CorpubProcess::EnumAppDomains(ICorPublishAppDomainEnum **ppIEnum)
714 {
715     VALIDATE_POINTER_TO_OBJECT(ppIEnum, ICorPublishAppDomainEnum **);
716     *ppIEnum = NULL;
717
718     int i;
719
720     HRESULT hr = S_OK;
721     WCHAR *pAppDomainName = NULL;
722     CorpubAppDomain *pAppDomainHead = NULL;
723
724     // Lock the IPC block:
725     // We can't trust any of the data in the IPC block (including our own mutex handle),
726     // because we don't want bugs in the debuggee escalating into bugs in the debugger.
727     DWORD res = WaitForSingleObject(m_hMutex, SAFETY_TIMEOUT);
728
729     if (res == WAIT_TIMEOUT)
730     {
731         // This should only happen if the target process is illbehaved.
732         return CORDBG_E_TIMEOUT;
733     }
734
735     // If the process has gone away, or if it has cleared out its control block, then
736     // we've lost the race to access this process before it is terminated.
737     // Note that if the EE does a rude process exit, it won't have cleared the control block so there
738     // will be a small race window.
739     if (this->IsExited() || this->m_AppDomainCB->m_hMutex == NULL )
740     {
741         // This is the common case. A process holding the mutex shouldn't normally exit,
742         // but once it releases the mutex, it may exit asynchronously.
743         return CORDBG_E_PROCESS_TERMINATED;
744     }
745
746     if (res == WAIT_FAILED)
747     {
748         // This should be the next most common failure case
749         return HRESULT_FROM_GetLastError();
750     }
751
752     if (res != WAIT_OBJECT_0)
753     {
754         // Catch all other possible failures
755         return E_FAIL;
756     }
757
758     int iAppDomainCount = 0;
759     AppDomainInfo *pADI = NULL;
760
761     // Make a copy of the IPC block so that we can gaurantee that it's not changing on us.
762     AppDomainEnumerationIPCBlock tempBlock;
763     memcpy(&tempBlock, m_AppDomainCB, sizeof(tempBlock));
764
765     // Allocate memory to read the remote process' memory into
766     const SIZE_T cbADI = tempBlock.m_iSizeInBytes;
767
768     // It's possible the process will not have any appdomains.
769     if ((tempBlock.m_rgListOfAppDomains == NULL) != (tempBlock.m_iSizeInBytes == 0))
770     {
771         _ASSERTE(!"Inconsistent IPC block in publish.");
772         hr = E_FAIL;
773         goto exit;
774     }
775
776     // All the data in the IPC block is signed integers. They should never be negative,
777     // so check that now.
778     if ((tempBlock.m_iTotalSlots < 0) ||
779         (tempBlock.m_iNumOfUsedSlots < 0) ||
780         (tempBlock.m_iLastFreedSlot < 0) ||
781         (tempBlock.m_iSizeInBytes < 0) ||
782         (tempBlock.m_iProcessNameLengthInBytes < 0))
783     {
784         hr = E_FAIL;
785         goto exit;
786     }
787
788     // Check other invariants.
789     if (cbADI != tempBlock.m_iTotalSlots * sizeof(AppDomainInfo))
790     {
791         _ASSERTE(!"Size mismatch");
792         hr = E_FAIL;
793         goto exit;
794     }
795
796     hr = AllocateAndReadRemoteBuffer(m_hProcess, tempBlock.m_rgListOfAppDomains, cbADI, (BYTE**) &pADI);
797     if (FAILED(hr))
798     {
799         goto exit;
800     }
801     _ASSERTE(pADI != NULL);
802
803     // Collect all the AppDomain info info a list of CorpubAppDomains
804     for (i = 0; i < tempBlock.m_iTotalSlots; i++)
805     {
806         if (!pADI[i].IsEmpty())
807         {
808             // Should be positive, and at least have a null-terminator character.
809             if (pADI[i].m_iNameLengthInBytes <= 1)
810             {
811                 hr = E_INVALIDARG;
812                 goto exit;
813             }
814             hr = AllocateAndReadRemoteString(m_hProcess,
815                 (void*) pADI[i].m_szAppDomainName, pADI[i].m_iNameLengthInBytes, // remote string + size in bytes
816                 &pAppDomainName);
817             if (FAILED(hr))
818             {
819                 goto exit;
820             }
821
822             // create a new AppDomainObject. This will take ownership of pAppDomainName.
823             // We know the string is a well-formed null-terminated string,
824             // but beyond that, we can't verify that the data is actually truthful.
825             CorpubAppDomain *pCurrentAppDomain = new (nothrow) CorpubAppDomain(pAppDomainName,
826                                                             pADI[i].m_id);
827
828             if (pCurrentAppDomain == NULL)
829             {
830                 LOG((LF_CORDB, LL_INFO1000,
831                  "CP::EAD: Failed to allocate memory for CorpubAppDomain.\n"));
832
833                 hr = E_OUTOFMEMORY;
834                 goto exit;
835             }
836
837             // Since CorpubAppDomain now owns pAppDomain's memory, we don't worry about freeing it.
838             pAppDomainName = NULL;
839
840             // Add the appdomain to the list.
841             pCurrentAppDomain->SetNext(pAppDomainHead);
842             pAppDomainHead = pCurrentAppDomain;
843
844             // Shortcut to opt out of reading the rest of the array if it's empty.
845             if (++iAppDomainCount >= tempBlock.m_iNumOfUsedSlots)
846                 break;
847         }
848     }
849
850     {
851         _ASSERTE ((iAppDomainCount >= tempBlock.m_iNumOfUsedSlots)
852                   && (i <= tempBlock.m_iTotalSlots));
853
854         // create and return the ICorPublishAppDomainEnum object, handing off the AppDomain list to it
855         CorpubAppDomainEnum *pTemp = new (nothrow) CorpubAppDomainEnum(pAppDomainHead);
856
857         if (pTemp == NULL)
858         {
859             hr = E_OUTOFMEMORY;
860             goto exit;
861         }
862
863         pAppDomainHead = NULL;      // handed off AppDomain list to enum, don't delete below
864
865         hr = pTemp->QueryInterface(IID_ICorPublishAppDomainEnum,
866                                    (void **)ppIEnum);
867     }
868
869 exit:
870     ReleaseMutex(m_hMutex);
871
872     // If we didn't hand off the AppDomain objects, delete them
873     while( pAppDomainHead != NULL )
874     {
875         CorpubAppDomain *pTemp = pAppDomainHead;
876         pAppDomainHead = pAppDomainHead->GetNextAppDomain();
877         delete pTemp;
878     }
879
880     if (pADI != NULL)
881         delete[] pADI;
882
883     if (pAppDomainName != NULL)
884         delete [] pAppDomainName;
885
886     // Either we succeeded && provided an enumerator; or we failed and didn't provide an enum.
887     _ASSERTE(SUCCEEDED(hr) == (*ppIEnum != NULL));
888     return hr;
889 }
890
891 /*
892  * Returns the OS ID for the process in question.
893  */
894 HRESULT CorpubProcess::GetProcessID(unsigned *pid)
895 {
896     *pid = m_dwProcessId;
897
898     return S_OK;
899 }
900
901 /*
902  * Get the display name for a process.
903  */
904 HRESULT CorpubProcess::GetDisplayName(ULONG32 cchName,
905                                       ULONG32 *pcchName,
906                                       __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
907 {
908     VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(szName, WCHAR, cchName, true, true);
909     VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *);
910
911     // Reasonable defaults
912     if (szName)
913         *szName = 0;
914
915     if (pcchName)
916         *pcchName = 0;
917
918     const WCHAR *szTempName = m_szProcessName;
919
920     // In case we didn't get the name (most likely out of memory on ctor).
921     if (!szTempName)
922         szTempName = W("<unknown>");
923
924     return CopyOutString(szTempName, cchName, pcchName, szName);
925 }
926
927
928 // ******************************************
929 // CorpubAppDomain
930 // ******************************************
931
932 CorpubAppDomain::CorpubAppDomain (__in LPWSTR szAppDomainName, ULONG Id)
933     : CordbCommonBase (0, enumCorpubAppDomain),
934     m_pNext (NULL),
935     m_szAppDomainName (szAppDomainName),
936     m_id (Id)
937 {
938     _ASSERTE(m_szAppDomainName != NULL);
939 }
940
941 CorpubAppDomain::~CorpubAppDomain()
942 {
943     delete [] m_szAppDomainName;
944 }
945
946 HRESULT CorpubAppDomain::QueryInterface (REFIID id, void **ppInterface)
947 {
948     if (id == IID_ICorPublishAppDomain)
949         *ppInterface = (ICorPublishAppDomain*)this;
950     else if (id == IID_IUnknown)
951         *ppInterface = (IUnknown*)(ICorPublishAppDomain*)this;
952     else
953     {
954         *ppInterface = NULL;
955         return E_NOINTERFACE;
956     }
957
958     AddRef();
959     return S_OK;
960 }
961
962
963 /*
964  * Get the name and ID for an application domain.
965  */
966 HRESULT CorpubAppDomain::GetID (ULONG32 *pId)
967 {
968     VALIDATE_POINTER_TO_OBJECT(pId, ULONG32 *);
969
970     *pId = m_id;
971
972     return S_OK;
973 }
974
975 /*
976  * Get the name for an application domain.
977  */
978 HRESULT CorpubAppDomain::GetName(ULONG32 cchName,
979                                 ULONG32 *pcchName,
980                                 __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
981 {
982     VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(szName, WCHAR, cchName, true, true);
983     VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *);
984
985     const WCHAR *szTempName = m_szAppDomainName;
986
987     // In case we didn't get the name (most likely out of memory on ctor).
988     if (!szTempName)
989         szTempName = W("<unknown>");
990
991     return CopyOutString(szTempName, cchName, pcchName, szName);
992 }
993
994
995
996 // ******************************************
997 // CorpubProcessEnum
998 // ******************************************
999
1000 CorpubProcessEnum::CorpubProcessEnum (CorpubProcess *pFirst)
1001     : CordbCommonBase (0, enumCorpubProcessEnum),
1002     m_pFirst (pFirst),
1003     m_pCurrent (pFirst)
1004 {
1005     // Increment the ref count on each process, we own the list
1006     CorpubProcess * cur = pFirst;
1007     while( cur != NULL )
1008     {
1009         cur->AddRef();
1010         cur = cur->GetNextProcess();
1011     }
1012 }
1013
1014 CorpubProcessEnum::~CorpubProcessEnum()
1015 {
1016     // Release each process in the list (our client may still have a reference
1017     // to some of them)
1018     while (m_pFirst != NULL)
1019     {
1020         CorpubProcess *pTmp = m_pFirst;
1021         m_pFirst = m_pFirst->GetNextProcess();
1022         pTmp->Release();
1023     }
1024 }
1025
1026 HRESULT CorpubProcessEnum::QueryInterface (REFIID id, void **ppInterface)
1027 {
1028     if (id == IID_ICorPublishProcessEnum)
1029         *ppInterface = (ICorPublishProcessEnum*)this;
1030     else if (id == IID_IUnknown)
1031         *ppInterface = (IUnknown*)(ICorPublishProcessEnum*)this;
1032     else
1033     {
1034         *ppInterface = NULL;
1035         return E_NOINTERFACE;
1036     }
1037
1038     AddRef();
1039     return S_OK;
1040 }
1041
1042
1043 HRESULT CorpubProcessEnum::Skip(ULONG celt)
1044 {
1045     while ((m_pCurrent != NULL) && (celt-- > 0))
1046     {
1047         m_pCurrent = m_pCurrent->GetNextProcess();
1048     }
1049
1050     return S_OK;
1051 }
1052
1053 HRESULT CorpubProcessEnum::Reset()
1054 {
1055     m_pCurrent = m_pFirst;
1056
1057     return S_OK;
1058 }
1059
1060 HRESULT CorpubProcessEnum::Clone(ICorPublishEnum **ppEnum)
1061 {
1062     VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorPublishEnum **);
1063     return E_NOTIMPL;
1064 }
1065
1066 HRESULT CorpubProcessEnum::GetCount(ULONG *pcelt)
1067 {
1068     VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *);
1069
1070     CorpubProcess *pTemp = m_pFirst;
1071
1072     *pcelt = 0;
1073
1074     while (pTemp != NULL)
1075     {
1076         (*pcelt)++;
1077         pTemp = pTemp->GetNextProcess();
1078     }
1079
1080     return S_OK;
1081 }
1082
1083 HRESULT CorpubProcessEnum::Next(ULONG celt,
1084                 ICorPublishProcess *objects[],
1085                 ULONG *pceltFetched)
1086 {
1087     VALIDATE_POINTER_TO_OBJECT_ARRAY(objects, ICorPublishProcess *,
1088         celt, true, true);
1089     VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *);
1090
1091     if ((pceltFetched == NULL) && (celt != 1))
1092     {
1093         return E_INVALIDARG;
1094     }
1095
1096     if (celt == 0)
1097     {
1098         if (pceltFetched != NULL)
1099         {
1100             *pceltFetched = 0;
1101         }
1102         return S_OK;
1103     }
1104
1105     HRESULT hr = S_OK;
1106
1107     ULONG count = 0;
1108
1109     while ((m_pCurrent != NULL) && (count < celt))
1110     {
1111         hr = m_pCurrent->QueryInterface (IID_ICorPublishProcess,
1112                                         (void**)&objects[count]);
1113
1114         if (hr != S_OK)
1115         {
1116             break;
1117         }
1118
1119         count++;
1120         m_pCurrent = m_pCurrent->GetNextProcess();
1121     }
1122
1123     if (pceltFetched != NULL)
1124     {
1125         *pceltFetched = count;
1126     }
1127
1128     //
1129     // If we reached the end of the enumeration, but not the end
1130     // of the number of requested items, we return S_FALSE.
1131     //
1132     if (count < celt)
1133     {
1134         return S_FALSE;
1135     }
1136
1137     return hr;
1138 }
1139
1140 // ******************************************
1141 // CorpubAppDomainEnum
1142 // ******************************************
1143 CorpubAppDomainEnum::CorpubAppDomainEnum (CorpubAppDomain *pFirst)
1144     : CordbCommonBase (0, enumCorpubAppDomainEnum),
1145     m_pFirst (pFirst),
1146     m_pCurrent (pFirst)
1147 {
1148     CorpubAppDomain *pCur = pFirst;
1149     while( pCur != NULL )
1150     {
1151         pCur->AddRef();
1152         pCur = pCur->GetNextAppDomain();
1153     }
1154 }
1155
1156 CorpubAppDomainEnum::~CorpubAppDomainEnum()
1157 {
1158     // Delete all the app domains
1159     while (m_pFirst != NULL )
1160     {
1161         CorpubAppDomain *pTemp = m_pFirst;
1162         m_pFirst = m_pFirst->GetNextAppDomain();
1163         pTemp->Release();
1164     }
1165 }
1166
1167 HRESULT CorpubAppDomainEnum::QueryInterface (REFIID id, void **ppInterface)
1168 {
1169     if (id == IID_ICorPublishAppDomainEnum)
1170         *ppInterface = (ICorPublishAppDomainEnum*)this;
1171     else if (id == IID_IUnknown)
1172         *ppInterface = (IUnknown*)(ICorPublishAppDomainEnum*)this;
1173     else
1174     {
1175         *ppInterface = NULL;
1176         return E_NOINTERFACE;
1177     }
1178
1179     AddRef();
1180     return S_OK;
1181 }
1182
1183
1184 HRESULT CorpubAppDomainEnum::Skip(ULONG celt)
1185 {
1186     while ((m_pCurrent != NULL) && (celt-- > 0))
1187     {
1188         m_pCurrent = m_pCurrent->GetNextAppDomain();
1189     }
1190
1191     return S_OK;
1192 }
1193
1194 HRESULT CorpubAppDomainEnum::Reset()
1195 {
1196     m_pCurrent = m_pFirst;
1197
1198     return S_OK;
1199 }
1200
1201 HRESULT CorpubAppDomainEnum::Clone(ICorPublishEnum **ppEnum)
1202 {
1203     VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorPublishEnum **);
1204     return E_NOTIMPL;
1205 }
1206
1207 HRESULT CorpubAppDomainEnum::GetCount(ULONG *pcelt)
1208 {
1209     VALIDATE_POINTER_TO_OBJECT(pcelt, ULONG *);
1210
1211     CorpubAppDomain *pTemp = m_pFirst;
1212
1213     *pcelt = 0;
1214
1215     while (pTemp != NULL)
1216     {
1217         (*pcelt)++;
1218         pTemp = pTemp->GetNextAppDomain();
1219     }
1220
1221     return S_OK;
1222 }
1223
1224 HRESULT CorpubAppDomainEnum::Next(ULONG celt,
1225                 ICorPublishAppDomain *objects[],
1226                 ULONG *pceltFetched)
1227 {
1228     VALIDATE_POINTER_TO_OBJECT_ARRAY(objects, ICorPublishProcess *,
1229         celt, true, true);
1230     VALIDATE_POINTER_TO_OBJECT_OR_NULL(pceltFetched, ULONG *);
1231
1232     if ((pceltFetched == NULL) && (celt != 1))
1233     {
1234         return E_INVALIDARG;
1235     }
1236
1237     if (celt == 0)
1238     {
1239         if (pceltFetched != NULL)
1240         {
1241     *pceltFetched = 0;
1242         }
1243         return S_OK;
1244     }
1245
1246     HRESULT hr = S_OK;
1247
1248     ULONG count = 0;
1249
1250     while ((m_pCurrent != NULL) && (count < celt))
1251     {
1252         hr = m_pCurrent->QueryInterface (IID_ICorPublishAppDomain,
1253                                         (void **)&objects[count]);
1254
1255         if (hr != S_OK)
1256         {
1257             break;
1258         }
1259
1260         count++;
1261         m_pCurrent = m_pCurrent->GetNextAppDomain();
1262     }
1263
1264
1265     if (pceltFetched != NULL)
1266     {
1267         *pceltFetched = count;
1268     }
1269
1270     //
1271     // If we reached the end of the enumeration, but not the end
1272     // of the number of requested items, we return S_FALSE.
1273     //
1274     if (count < celt)
1275     {
1276         return S_FALSE;
1277     }
1278
1279     return hr;
1280 }
1281
1282 #endif // defined(FEATURE_DBG_PUBLISH)