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 https://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_SameBinary_h
8 #define mozilla_SameBinary_h
10 #include "mozilla/WinHeaderOnlyUtils.h"
11 #include "mozilla/NativeNt.h"
12 #include "nsWindowsHelpers.h"
16 class ProcessImagePath final
{
18 LauncherVoidResult mLastError
;
20 // Using a larger buffer because an NT path may exceed MAX_PATH.
21 WCHAR mPathBuffer
[(MAX_PATH
* 2) + 1];
24 // Initialize with an NT path string of a given process handle
25 explicit ProcessImagePath(const nsAutoHandle
& aProcess
)
26 : mType(PathType::eNtPath
), mLastError(Ok()) {
27 DWORD len
= std::size(mPathBuffer
);
28 if (!::QueryFullProcessImageNameW(aProcess
.get(), PROCESS_NAME_NATIVE
,
30 mLastError
= LAUNCHER_ERROR_FROM_LAST();
35 // Initizlize with a DOS path string of a given imagebase address
36 explicit ProcessImagePath(HMODULE aImageBase
)
37 : mType(PathType::eDosPath
), mLastError(Ok()) {
39 ::GetModuleFileNameW(aImageBase
, mPathBuffer
, std::size(mPathBuffer
));
40 if (!len
|| len
== std::size(mPathBuffer
)) {
41 mLastError
= LAUNCHER_ERROR_FROM_LAST();
46 bool IsError() const { return mLastError
.isErr(); }
48 const WindowsErrorType
& GetError() const { return mLastError
.inspectErr(); }
50 FileUniqueId
GetId() const { return FileUniqueId(mPathBuffer
, mType
); }
52 bool CompareNtPaths(const ProcessImagePath
& aOther
) const {
53 if (mLastError
.isErr() || aOther
.mLastError
.isErr() ||
54 mType
!= PathType::eNtPath
|| aOther
.mType
!= PathType::eNtPath
) {
58 UNICODE_STRING path1
, path2
;
59 ::RtlInitUnicodeString(&path1
, mPathBuffer
);
60 ::RtlInitUnicodeString(&path2
, aOther
.mPathBuffer
);
61 return !!::RtlEqualUnicodeString(&path1
, &path2
, TRUE
);
65 enum class ImageFileCompareOption
{
70 static inline mozilla::LauncherResult
<bool> IsSameBinaryAsParentProcess(
71 ImageFileCompareOption aOption
= ImageFileCompareOption::Default
) {
72 mozilla::LauncherResult
<DWORD
> parentPid
= mozilla::nt::GetParentProcessId();
73 if (parentPid
.isErr()) {
74 return parentPid
.propagateErr();
77 nsAutoHandle
parentProcess(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION
,
78 FALSE
, parentPid
.unwrap()));
79 if (!parentProcess
.get()) {
80 DWORD err
= ::GetLastError();
81 if (err
== ERROR_INVALID_PARAMETER
|| err
== ERROR_ACCESS_DENIED
) {
82 // In the ERROR_INVALID_PARAMETER case, the process identified by
83 // parentPid has already exited. This is a common case when the parent
84 // process is not Firefox, thus we should return false instead of erroring
86 // The ERROR_ACCESS_DENIED case can happen when the parent process is
87 // something that we don't have permission to query. For example, we may
88 // encounter this when Firefox is launched by the Windows Task Scheduler.
92 return LAUNCHER_ERROR_FROM_WIN32(err
);
95 ProcessImagePath
parentExe(parentProcess
);
96 if (parentExe
.IsError()) {
97 return ::mozilla::Err(parentExe
.GetError());
100 if (aOption
== ImageFileCompareOption::Default
) {
101 bool skipFileIdComparison
= false;
103 FileUniqueId id1
= parentExe
.GetId();
105 // We saw a number of Win7 users failed to call NtOpenFile with
106 // STATUS_OBJECT_PATH_NOT_FOUND for an unknown reason. In this
107 // particular case, we fall back to the logic to compare NT path
108 // strings instead of a file id which will not fail because we don't
109 // need to open a file handle.
110 #if !defined(STATUS_OBJECT_PATH_NOT_FOUND)
111 constexpr NTSTATUS STATUS_OBJECT_PATH_NOT_FOUND
= 0xc000003a;
113 const LauncherError
& err
= id1
.GetError();
115 WindowsError::FromNtStatus(STATUS_OBJECT_PATH_NOT_FOUND
)) {
116 return ::mozilla::Err(err
);
119 skipFileIdComparison
= true;
122 if (!skipFileIdComparison
) {
123 ProcessImagePath
ourExe(nullptr);
124 if (ourExe
.IsError()) {
125 return ::mozilla::Err(ourExe
.GetError());
128 FileUniqueId id2
= ourExe
.GetId();
130 return ::mozilla::Err(id2
.GetError());
136 nsAutoHandle
ourProcess(::GetCurrentProcess());
137 ProcessImagePath
ourExeNt(ourProcess
);
138 if (ourExeNt
.IsError()) {
139 return ::mozilla::Err(ourExeNt
.GetError());
141 return parentExe
.CompareNtPaths(ourExeNt
);
144 } // namespace mozilla
146 #endif // mozilla_SameBinary_h