Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUncontrolled search path element in CredentialProvider
DescriptionUncontrolled search path element in CredentialProvider
ComponentCredentialProvider
Bug ClassLogic Error
Tracker518081914
Fix commit5a583ca6b6a2 (chromium/src) +65/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-09-08

Files Changed

  • chrome/credential_provider/setup/BUILD.gn
  • chrome/credential_provider/setup/setup.cc
  • chrome/credential_provider/setup/setup_lib.cc
  • chrome/credential_provider/setup/setup_utils.cc
  • chrome/credential_provider/setup/setup_utils.h
  • chrome/credential_provider/test/gcp_setup_unittest.cc
From 5a583ca6b6a2fac3b481b1cd7744534531965d6c Mon Sep 17 00:00:00 2001
From: Gabriel Charette <[email protected]>
Date: Mon, 17 Aug 2026 00:56:59 -0700
Subject: [PATCH] Reland "[gcpw] Relaunch the uninstaller from the system temp directory"

This reverts commit 16832512e03a6e690ef18007c56dfc9538b049e7.

Reason for reland: Fix test under component build.

Original change's description:
> Revert "[gcpw] Relaunch the uninstaller from the system temp directory"
>
> This reverts commit af7d71b118cb2b0f8a623258ac777726e05217e8.
>
> Reason for revert: Failing on Win10
>
> Failure Link: https://ci.chromium.org/ui/p/chromium/builders/ci/Win10%20Tests%20x64%20(dbg)/54781/overview
>
> Original change's description:
> > [gcpw] Relaunch the uninstaller from the system temp directory
> >
> > RelaunchUninstaller() copies gcp_setup.exe into a fresh temp directory
> > and relaunches it from there with that directory as both the
> > application directory and working directory of the child. The
> > directory was obtained via base::CreateNewTempDirectory(), which for
> > an elevated admin with a split token resolves to the per-user %TEMP%.
> >
> > This function only runs after the IsUserAnAdmin() gate, so stage the
> > relaunched copy under base::DIR_SYSTEM_TEMP instead, matching what
> > chrome/installer/setup, chrome/updater, and
> > chrome/enterprise_companion already do for the same operation.
> >
> > Additionally, this change:
> > - Restricts DLL loading to full paths or %SYSTEM32% by calling
> >   EnableSecureDllLoading() (SetDefaultDllDirectories(
> >   LOAD_LIBRARY_SEARCH_SYSTEM32) or LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
> >   in component builds) in setup.cc.
> > - Adds /DEPENDENTLOADFLAG:0x800 to gcp_setup ldflags in BUILD.gn to
> >   enforce secure dependent DLL loading at the PE header level
> >   (matching gcp_sfx).
> > - Fails early with E_FAIL in RelaunchUninstaller() for component
> >   builds since copying only gcp_setup.exe into an isolated temp
> >   directory does not copy dependent component DLLs.
> > - Adds a regression test
> >   (GcpSetupTest.RelaunchUninstallerStagesUnderSystemTemp) that
> >   overrides DIR_SYSTEM_TEMP and verifies the staging directory is
> >   created beneath it.
> >
> > [email protected]
> >
> > TAG=agy
> > CONV=9b81bd43-a335-43ec-81e4-e60832a65aed
> >
> > Bug: 518081914
> > Change-Id: I76501070ac866647c48ea9612720b72da8e4580c
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8240372
> > Reviewed-by: Greg Thompson <[email protected]>
> > Auto-Submit: Gabriel Charette <[email protected]>
> > Commit-Queue: Gabriel Charette <[email protected]>
> > Cr-Commit-Position: refs/heads/main@{#1678221}
>
> Bug: 518081914
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Change-Id: I08ee9a9b2a8079a87733c48b151d7c103b361f14
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8247942
> Auto-Submit: Kenichi Ishibashi <[email protected]>
> Owners-Override: Kenichi Ishibashi <[email protected]>
> Commit-Queue: [email protected] <[email protected]>
> Bot-Commit: [email protected] <[email protected]>
> Cr-Commit-Position: refs/heads/main@{#1678376}

Fixed: 518081914
Change-Id: Ie25b253c88dc28efcbf7d92efd8307c8adee82c7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8256641
Auto-Submit: Gabriel Charette <[email protected]>
Commit-Queue: Greg Thompson <[email protected]>
Reviewed-by: Greg Thompson <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1680390}
---

diff --git a/chrome/credential_provider/setup/BUILD.gn b/chrome/credential_provider/setup/BUILD.gn
index 063669a..4325145 100644
--- a/chrome/credential_provider/setup/BUILD.gn
+++ b/chrome/credential_provider/setup/BUILD.gn
@@ -98,4 +98,5 @@
     "//content/public/common:static_switches",
   ]
   configs += [ "//build/config/win:windowed" ]
+  ldflags = [ "/DEPENDENTLOADFLAG:0x800" ]
 }
diff --git a/chrome/credential_provider/setup/setup.cc b/chrome/credential_provider/setup/setup.cc
index 3abe46d9..ab45c25 100644
--- a/chrome/credential_provider/setup/setup.cc
+++ b/chrome/credential_provider/setup/setup.cc
@@ -62,6 +62,8 @@
                       HINSTANCE /*hPrevInstance*/,
                       wchar_t* lpCmdLine,
                       int /*nCmdShow*/) {
+  credential_provider::EnableSecureDllLoading();
+
   HRESULT hr = S_OK;
 
   // Initialize base.  Command line will be set from GetCommandLineW().
diff --git a/chrome/credential_provider/setup/setup_lib.cc b/chrome/credential_provider/setup/setup_lib.cc
index 407d412..7f01b57 100644
--- a/chrome/credential_provider/setup/setup_lib.cc
+++ b/chrome/credential_provider/setup/setup_lib.cc
@@ -9,6 +9,7 @@
 #include <iomanip>
 #include <string>
 
+#include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/compiler_specific.h"
 #include "base/file_version_info.h"
@@ -274,10 +275,24 @@
 }
 
 HRESULT RelaunchUninstaller(const base::FilePath& installer_path) {
+#if defined(COMPONENT_BUILD)
+  // In component builds, dependent DLLs are not copied to the temporary
+  // directory, so the executable cannot launch.
+  return E_FAIL;
+#else
+  // This function only runs elevated, so stage and relaunch the copy of the
+  // installer from the system temp directory rather than the per-user one.
+  base::FilePath system_temp;
+  if (!base::PathService::Get(base::DIR_SYSTEM_TEMP, &system_temp)) {
+    LOGFN(ERROR) << "PathService::Get(DIR_SYSTEM_TEMP) failed";
+    return E_FAIL;
+  }
+
   base::FilePath temp_path;
-  if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("gcp"), &temp_path)) {
+  if (!base::CreateTemporaryDirInDir(system_temp, FILE_PATH_LITERAL("gcp"),
+                                     &temp_path)) {
     HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
-    LOGFN(ERROR) << "CreateNewTempDirectory hr=" << putHR(hr);
+    LOGFN(ERROR) << "CreateTemporaryDirInDir hr=" << putHR(hr);
     return hr;
   }
 
@@ -317,6 +332,7 @@
   base::Process process(base::LaunchProcess(cmdline, options));
 
   return process.IsValid() ? S_OK : E_FAIL;
+#endif  // defined(COMPONENT_BUILD)
 }
 
 int EnableStatsCollection(const base::CommandLine& cmdline) {
diff --git a/chrome/credential_provider/setup/setup_utils.cc b/chrome/credential_provider/setup/setup_utils.cc
index 8b9df89c..32d28bd5 100644
--- a/chrome/credential_provider/setup/setup_utils.cc
+++ b/chrome/credential_provider/setup/setup_utils.cc
@@ -323,4 +323,14 @@
   return true;
 }
 
+bool EnableSecureDllLoading() {
+#if defined(COMPONENT_BUILD)
+  const DWORD directory_flags = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
+#else
+  const DWORD directory_flags = LOAD_LIBRARY_SEARCH_SYSTEM32;
+#endif
+
+  return ::SetDefaultDllDirectories(directory_flags);
+}
+
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/setup/setup_utils.h b/chrome/credential_provider/setup/setup_utils.h
index a619b01..269e847 100644
--- a/chrome/credential_provider/setup/setup_utils.h
+++ b/chrome/credential_provider/setup/setup_utils.h
@@ -84,6 +84,9 @@
   base::DictValue installer_data_dictionary_;
 };
 
+// Restricts DLL loads to either full paths or %SYSTEM32%.
+bool EnableSecureDllLoading();
+
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_SETUP_SETUP_UTILS_H_
diff --git a/chrome/credential_provider/test/gcp_setup_unittest.cc b/chrome/credential_provider/test/gcp_setup_unittest.cc
index bba356f..59823bb 100644
--- a/chrome/credential_provider/test/gcp_setup_unittest.cc
+++ b/chrome/credential_provider/test/gcp_setup_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/environment.h"
 #include "base/file_version_info.h"
 #include "base/files/file.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/process/launch.h"
@@ -327,6 +328,10 @@
 }
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/credential_provider/test/gcp_setup_unittest.cc b/chrome/credential_provider/test/gcp_setup_unittest.cc
index bba356f..59823bb 100644
--- a/chrome/credential_provider/test/gcp_setup_unittest.cc
+++ b/chrome/credential_provider/test/gcp_setup_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/environment.h"
 #include "base/file_version_info.h"
 #include "base/files/file.h"
+#include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/process/launch.h"
@@ -327,6 +328,10 @@
 }
 
 void GcpSetupTest::SetUp() {
+  if (!::IsUserAnAdmin()) {
+    GTEST_SKIP() << "Test requires administrative privileges.";
+  }
+
   // Get the path to the setup exe (this exe during unit tests) and the
   // chrome version.
   GetModulePathAndProductVersion(&module_path_, &product_version_);
@@ -355,10 +360,6 @@
   programdata_override_ = std::make_unique<base::ScopedPathOverride>(
       base::DIR_COMMON_APP_DATA, scoped_temp_progdata_dir_.GetPath());
 
-  if (!::IsUserAnAdmin()) {
-    GTEST_SKIP() << "Test requires administrative privileges.";
-  }
-
   ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
 
   // In non-component builds, base::FILE_MODULE will always return the path
@@ -705,6 +706,32 @@
                   .empty());
 }
 
+TEST_F(GcpSetupTest, RelaunchUninstallerStagesUnderSystemTemp) {
+#if defined(COMPONENT_BUILD)
+  GTEST_SKIP() << "RelaunchUninstaller is not supported in component builds.";
+#else
+  base::ScopedTempDir system_temp;
+  EXPECT_TRUE(system_temp.CreateUniqueTempDir());
+  base::ScopedPathOverride system_temp_override(base::DIR_SYSTEM_TEMP,
+                                                system_temp.GetPath());
+
+  base::ScopedTempDir source_dir;
+  EXPECT_TRUE(source_dir.CreateUniqueTempDir());
+  base::FilePath installer_path =
+      source_dir.GetPath().Append(FILE_PATH_LITERAL("gcp_setup.exe"));
+  EXPECT_TRUE(base::WriteFile(installer_path, "stub"));
+
+  RelaunchUninstaller(installer_path);
+
+  base::FileEnumerator enumerator(system_temp.GetPath(), /*recursive=*/false,
+                                  base::FileEnumerator::DIRECTORIES,
+                                  FILE_PATH_LITERAL("gcp*"));
+  base::FilePath staged_dir = enumerator.Next();
+  EXPECT_FALSE(staged_dir.empty());
+  EXPECT_TRUE(base::PathExists(staged_dir.Append(installer_path.BaseName())));
+#endif  // defined(COMPONENT_BUILD)
+}
+
 TEST_F(GcpSetupTest, ValidLsaWithNoExistingUser) {
   logging::ResetEventSourceForTesting();
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.