Utils.cpp (8966B)
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #if defined(MOZILLA_INTERNAL_API) 8 # include "MainThreadUtils.h" 9 # include "mozilla/dom/ContentChild.h" 10 #endif 11 12 #include "mozilla/mscom/COMWrappers.h" 13 #include "mozilla/DebugOnly.h" 14 #include "mozilla/mscom/Utils.h" 15 #include "mozilla/RefPtr.h" 16 17 #include <objidl.h> 18 #include <winnt.h> 19 20 #include <utility> 21 22 #if defined(_MSC_VER) 23 extern "C" IMAGE_DOS_HEADER __ImageBase; 24 #endif 25 26 namespace mozilla { 27 namespace mscom { 28 29 bool IsCOMInitializedOnCurrentThread() { 30 APTTYPE aptType; 31 APTTYPEQUALIFIER aptTypeQualifier; 32 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier); 33 return hr != CO_E_NOTINITIALIZED; 34 } 35 36 bool IsCurrentThreadMTA() { 37 APTTYPE aptType; 38 APTTYPEQUALIFIER aptTypeQualifier; 39 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier); 40 if (FAILED(hr)) { 41 return false; 42 } 43 44 return aptType == APTTYPE_MTA; 45 } 46 47 bool IsCurrentThreadExplicitMTA() { 48 APTTYPE aptType; 49 APTTYPEQUALIFIER aptTypeQualifier; 50 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier); 51 if (FAILED(hr)) { 52 return false; 53 } 54 55 return aptType == APTTYPE_MTA && 56 aptTypeQualifier != APTTYPEQUALIFIER_IMPLICIT_MTA; 57 } 58 59 bool IsCurrentThreadImplicitMTA() { 60 APTTYPE aptType; 61 APTTYPEQUALIFIER aptTypeQualifier; 62 HRESULT hr = wrapped::CoGetApartmentType(&aptType, &aptTypeQualifier); 63 if (FAILED(hr)) { 64 return false; 65 } 66 67 return aptType == APTTYPE_MTA && 68 aptTypeQualifier == APTTYPEQUALIFIER_IMPLICIT_MTA; 69 } 70 71 #if defined(MOZILLA_INTERNAL_API) 72 bool IsCurrentThreadNonMainMTA() { 73 if (NS_IsMainThread()) { 74 return false; 75 } 76 77 return IsCurrentThreadMTA(); 78 } 79 #endif // defined(MOZILLA_INTERNAL_API) 80 81 bool IsProxy(IUnknown* aUnknown) { 82 if (!aUnknown) { 83 return false; 84 } 85 86 // Only proxies implement this interface, so if it is present then we must 87 // be dealing with a proxied object. 88 RefPtr<IClientSecurity> clientSecurity; 89 HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity, 90 (void**)getter_AddRefs(clientSecurity)); 91 if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) { 92 return true; 93 } 94 return false; 95 } 96 97 bool IsValidGUID(REFGUID aCheckGuid) { 98 // This function determines whether or not aCheckGuid conforms to RFC4122 99 // as it applies to Microsoft COM. 100 101 BYTE variant = aCheckGuid.Data4[0]; 102 if (!(variant & 0x80)) { 103 // NCS Reserved 104 return false; 105 } 106 if ((variant & 0xE0) == 0xE0) { 107 // Reserved for future use 108 return false; 109 } 110 if ((variant & 0xC0) == 0xC0) { 111 // Microsoft Reserved. 112 return true; 113 } 114 115 BYTE version = HIBYTE(aCheckGuid.Data3) >> 4; 116 // Other versions are specified in RFC4122 but these are the two used by COM. 117 return version == 1 || version == 4; 118 } 119 120 uintptr_t GetContainingModuleHandle() { 121 HMODULE thisModule = nullptr; 122 #if defined(_MSC_VER) 123 thisModule = reinterpret_cast<HMODULE>(&__ImageBase); 124 #else 125 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 126 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 127 reinterpret_cast<LPCTSTR>(&GetContainingModuleHandle), 128 &thisModule)) { 129 return 0; 130 } 131 #endif 132 return reinterpret_cast<uintptr_t>(thisModule); 133 } 134 135 namespace detail { 136 137 long BuildRegGuidPath(REFGUID aGuid, const GuidType aGuidType, wchar_t* aBuf, 138 const size_t aBufLen) { 139 constexpr wchar_t kClsid[] = L"CLSID\\"; 140 constexpr wchar_t kAppid[] = L"AppID\\"; 141 constexpr wchar_t kSubkeyBase[] = L"SOFTWARE\\Classes\\"; 142 143 // We exclude null terminators in these length calculations because we include 144 // the stringified GUID's null terminator at the end. Since kClsid and kAppid 145 // have identical lengths, we just choose one to compute this length. 146 constexpr size_t kSubkeyBaseLen = std::size(kSubkeyBase) - 1; 147 constexpr size_t kSubkeyLen = kSubkeyBaseLen + std::size(kClsid) - 1; 148 // Guid length as formatted for the registry (including curlies and dashes), 149 // but excluding null terminator. 150 constexpr size_t kGuidLen = kGuidRegFormatCharLenInclNul - 1; 151 constexpr size_t kExpectedPathLenInclNul = kSubkeyLen + kGuidLen + 1; 152 153 if (aBufLen < kExpectedPathLenInclNul) { 154 // Buffer is too short 155 return E_INVALIDARG; 156 } 157 158 if (wcscpy_s(aBuf, aBufLen, kSubkeyBase)) { 159 return E_INVALIDARG; 160 } 161 162 const wchar_t* strGuidType = aGuidType == GuidType::CLSID ? kClsid : kAppid; 163 if (wcscat_s(aBuf, aBufLen, strGuidType)) { 164 return E_INVALIDARG; 165 } 166 167 int guidConversionResult = 168 ::StringFromGUID2(aGuid, &aBuf[kSubkeyLen], aBufLen - kSubkeyLen); 169 if (!guidConversionResult) { 170 return E_INVALIDARG; 171 } 172 173 return S_OK; 174 } 175 176 } // namespace detail 177 178 long CreateStream(const uint8_t* aInitBuf, const uint32_t aInitBufSize, 179 IStream** aOutStream) { 180 if (!aInitBufSize || !aOutStream) { 181 return E_INVALIDARG; 182 } 183 184 *aOutStream = nullptr; 185 186 HRESULT hr; 187 RefPtr<IStream> stream; 188 189 // If aInitBuf is null then initSize must be 0. 190 UINT initSize = aInitBuf ? aInitBufSize : 0; 191 stream = already_AddRefed<IStream>(::SHCreateMemStream(aInitBuf, initSize)); 192 if (!stream) { 193 return E_OUTOFMEMORY; 194 } 195 196 if (!aInitBuf) { 197 // Now we'll set the required size 198 ULARGE_INTEGER newSize; 199 newSize.QuadPart = aInitBufSize; 200 hr = stream->SetSize(newSize); 201 if (FAILED(hr)) { 202 return hr; 203 } 204 } 205 206 // Ensure that the stream is rewound 207 LARGE_INTEGER streamOffset; 208 streamOffset.QuadPart = 0LL; 209 hr = stream->Seek(streamOffset, STREAM_SEEK_SET, nullptr); 210 if (FAILED(hr)) { 211 return hr; 212 } 213 214 stream.forget(aOutStream); 215 return S_OK; 216 } 217 218 #if defined(MOZILLA_INTERNAL_API) 219 220 void GUIDToString(REFGUID aGuid, nsAString& aOutString) { 221 // This buffer length is long enough to hold a GUID string that is formatted 222 // to include curly braces and dashes. 223 const int kBufLenWithNul = 39; 224 aOutString.SetLength(kBufLenWithNul); 225 int result = StringFromGUID2(aGuid, char16ptr_t(aOutString.BeginWriting()), 226 kBufLenWithNul); 227 MOZ_ASSERT(result); 228 if (result) { 229 // Truncate the terminator 230 aOutString.SetLength(result - 1); 231 } 232 } 233 234 // Undocumented IIDs that are relevant for diagnostic purposes 235 static const IID IID_ISCMLocalActivator = { 236 0x00000136, 237 0x0000, 238 0x0000, 239 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; 240 static const IID IID_IRundown = { 241 0x00000134, 242 0x0000, 243 0x0000, 244 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; 245 static const IID IID_IRemUnknown = { 246 0x00000131, 247 0x0000, 248 0x0000, 249 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; 250 static const IID IID_IRemUnknown2 = { 251 0x00000143, 252 0x0000, 253 0x0000, 254 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; 255 256 struct IIDToLiteralMapEntry { 257 constexpr IIDToLiteralMapEntry(REFIID aIid, nsLiteralCString&& aStr) 258 : mIid(aIid), mStr(std::forward<nsLiteralCString>(aStr)) {} 259 260 REFIID mIid; 261 const nsLiteralCString mStr; 262 }; 263 264 /** 265 * Given the name of an interface, the IID_ENTRY macro generates a pair 266 * containing a reference to the interface ID and a stringified version of 267 * the interface name. 268 * 269 * For example: 270 * 271 * {IID_ENTRY(IUnknown)} 272 * is expanded to: 273 * {IID_IUnknown, "IUnknown"_ns} 274 * 275 */ 276 // clang-format off 277 # define IID_ENTRY_STRINGIFY(iface) #iface##_ns 278 # define IID_ENTRY(iface) IID_##iface, IID_ENTRY_STRINGIFY(iface) 279 // clang-format on 280 281 // Mapping of selected IIDs to friendly, human readable descriptions for each 282 // interface. 283 static constexpr IIDToLiteralMapEntry sIidDiagStrs[] = { 284 {IID_ENTRY(IUnknown)}, 285 {IID_IRemUnknown, "cross-apartment IUnknown"_ns}, 286 {IID_IRundown, "cross-apartment object management"_ns}, 287 {IID_ISCMLocalActivator, "out-of-process object instantiation"_ns}, 288 {IID_IRemUnknown2, "cross-apartment IUnknown"_ns}}; 289 290 # undef IID_ENTRY 291 # undef IID_ENTRY_STRINGIFY 292 293 void DiagnosticNameForIID(REFIID aIid, nsACString& aOutString) { 294 // If the IID matches something in sIidDiagStrs, output its string. 295 for (const auto& curEntry : sIidDiagStrs) { 296 if (curEntry.mIid == aIid) { 297 aOutString.Assign(curEntry.mStr); 298 return; 299 } 300 } 301 302 // Otherwise just convert the IID to string form and output that. 303 nsAutoString strIid; 304 GUIDToString(aIid, strIid); 305 306 aOutString.AssignLiteral("IID "); 307 AppendUTF16toUTF8(strIid, aOutString); 308 } 309 310 #else 311 312 void GUIDToString(REFGUID aGuid, 313 wchar_t (&aOutBuf)[kGuidRegFormatCharLenInclNul]) { 314 DebugOnly<int> result = ::StringFromGUID2(aGuid, aOutBuf, std::size(aOutBuf)); 315 MOZ_ASSERT(result); 316 } 317 318 #endif // defined(MOZILLA_INTERNAL_API) 319 320 } // namespace mscom 321 } // namespace mozilla