0

Allow child processes to inherit logging file handle on Windows

Child processes on Windows cannot always open their log file
(for instance if they cannot determine the user-data-dir) making
configuration of logging complicated. Child processes also need
a sandbox exception to open their log files.

This CL replaces logging related flags to pass a handle to children
in cases where a file would have been passed, allowing a later CL
to remove the sandbox rule allowing direct access to the log file.

If the browser is run with `--enable-logging --log-file=file`,
children will now be run with `--enable-logging=handle --log-file=N`.
It is not possible to mix stderr logging with file logging (it will
still work for the browser process), but this already does not work
in official builds as std handles are not passed into children.

This also allows the CHROME_LOG_FILE to be useful again for renderer
processes if Chrome is run using:

 $env:CHROME_LOG_FILE="d:\temp\env-log.log"
 chrome.exe --enable-logging

the browser will inspect the env var and provide a handle to that
file when starting renderer processes.

Running the browser with --enable-logging=handle directly is not
supported, these flags should only be provided by the browser when
starting child processes.

Tests: Manually tested results https://docs.google.com/document/d/1Hq37YReGM91fmcCcqkRbjtjwP62FVH_zpZtfpSPlLeY/edit?usp=sharing
Bug: 328285906, 40270808
Change-Id: Id79731b2d35ab3ee58f6c1612990bdec1485da68
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5337202
Commit-Queue: Alex Gough <ajgo@chromium.org>
Reviewed-by: Greg Thompson <grt@chromium.org>
Reviewed-by: Peter Boström <pbos@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1272538}
This commit is contained in:
Alex Gough
2024-03-14 02:16:44 +00:00
committed by Chromium LUCI CQ
parent a3baf9dbfc
commit b80a478f9f
10 changed files with 301 additions and 51 deletions

@ -15,9 +15,8 @@
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/win/windows_types.h"
#endif // BUILDFLAG(IS_WIN)
#if defined(COMPONENT_BUILD) && BUILDFLAG(IS_WIN)
@ -72,15 +71,11 @@ void AsanService::Initialize() {
if (!is_initialized_) {
#if BUILDFLAG(IS_WIN)
if (logging::IsLoggingToFileEnabled()) {
// This path is allowed by the sandbox when `--enable-logging
// --log-file={path}` are both specified when launching Chromium.
auto log_file = base::File(
base::FilePath(logging::GetLogFileFullPath()),
base::File::Flags::FLAG_OPEN_ALWAYS | base::File::Flags::FLAG_APPEND);
if (log_file.IsValid()) {
// Sandboxed processes cannot open files but are provided a HANDLE.
HANDLE log_handle = logging::DuplicateLogFileHandle();
if (log_handle) {
// Sanitizer APIs need a HANDLE cast to void*.
__sanitizer_set_report_fd(
reinterpret_cast<void*>(log_file.TakePlatformFile()));
__sanitizer_set_report_fd(reinterpret_cast<void*>(log_handle));
}
}
#endif // BUILDFLAG(IS_WIN)

@ -23,6 +23,7 @@
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/functional/callback.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
@ -33,11 +34,28 @@
#include "base/posix/eintr_wrapper.h"
#endif
namespace content::internal {
class ChildProcessLauncherHelper;
} // namespace content::internal
namespace base {
class Environment;
class Time;
#if BUILDFLAG(IS_WIN)
class PreventExecuteMappingClasses {
public:
using PassKey = base::PassKey<PreventExecuteMappingClasses>;
private:
static PassKey GetPassKey() { return PassKey(); }
// Allowed to open log files in arbitrary locations.
friend class content::internal::ChildProcessLauncherHelper;
};
#endif
//-----------------------------------------------------------------------------
// Functions that involve filesystem access or modification:
@ -128,6 +146,13 @@ BASE_EXPORT bool DeleteFileAfterReboot(const FilePath& path);
// untrusted process. See also `File::FLAG_WIN_NO_EXECUTE`.
BASE_EXPORT bool PreventExecuteMapping(const FilePath& path);
// Same as PreventExecuteMapping but DCHECK for known allowed paths is omitted.
// Only call this if you know the path you are providing is safe to mark as
// non-executable, such as log files.
BASE_EXPORT bool PreventExecuteMappingUnchecked(
const FilePath& path,
base::PassKey<PreventExecuteMappingClasses> passkey);
// Set `path_key` to the second of two valid paths that support safely marking a
// file as non-execute. The first allowed path is always PATH_TEMP. This is
// needed to avoid layering violations, as the user data dir is an embedder

@ -379,6 +379,7 @@ OnceClosure GetDeleteFileCallbackInternal(
// might cause the browser or operating system to fail in unexpected ways.
bool IsPathSafeToSetAclOn(const FilePath& path) {
#if BUILDFLAG(CLANG_PROFILING)
// TODO(crbug.com/329482479) Use PreventExecuteMappingUnchecked for .profraw.
// Ignore .profraw profiling files, as they can occur anywhere, and only occur
// during testing.
if (path.Extension() == FILE_PATH_LITERAL(".profraw")) {
@ -1152,13 +1153,13 @@ bool PreReadFile(const FilePath& file_path,
/*Flags=*/0);
}
bool PreventExecuteMapping(const FilePath& path) {
bool PreventExecuteMappingInternal(const FilePath& path, bool skip_path_check) {
if (!base::FeatureList::IsEnabled(
features::kEnforceNoExecutableFileHandles)) {
return true;
}
bool is_path_safe = IsPathSafeToSetAclOn(path);
bool is_path_safe = skip_path_check || IsPathSafeToSetAclOn(path);
if (!is_path_safe) {
// To mitigate the effect of past OS bugs where attackers are able to use
@ -1207,6 +1208,16 @@ bool PreventExecuteMapping(const FilePath& path) {
/*recursive=*/false);
}
bool PreventExecuteMapping(const FilePath& path) {
return PreventExecuteMappingInternal(path, false);
}
bool PreventExecuteMappingUnchecked(
const FilePath& path,
base::PassKey<PreventExecuteMappingClasses> passkey) {
return PreventExecuteMappingInternal(path, true);
}
void SetExtraNoExecuteAllowedPath(int path_key) {
DCHECK(!g_extra_allowed_path_for_no_execute ||
g_extra_allowed_path_for_no_execute == path_key);

@ -536,7 +536,7 @@ bool BaseInitLoggingImpl(const LoggingSettings& settings) {
}
#endif
// ignore file options unless logging to file is set.
// Ignore file options unless logging to file is set.
if ((g_logging_destination & LOG_TO_FILE) == 0)
return true;
@ -548,13 +548,13 @@ bool BaseInitLoggingImpl(const LoggingSettings& settings) {
// default log file will re-initialize to the new options.
CloseLogFileUnlocked();
#if BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
if (settings.log_file) {
DCHECK(!settings.log_file_path);
g_log_file = settings.log_file;
return true;
}
#endif
#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_WIN)
DCHECK(settings.log_file_path) << "LOG_TO_FILE set but no log_file_path!";
@ -1155,6 +1155,24 @@ FILE* DuplicateLogFILE() {
}
#endif
#if BUILDFLAG(IS_WIN)
HANDLE DuplicateLogFileHandle() {
// `g_log_file` should only be valid, or nullptr, but be very careful that we
// do not duplicate INVALID_HANDLE_VALUE as it aliases the process handle.
if (!(g_logging_destination & LOG_TO_FILE) || !g_log_file ||
g_log_file == INVALID_HANDLE_VALUE) {
return nullptr;
}
HANDLE duplicate = nullptr;
if (!::DuplicateHandle(::GetCurrentProcess(), g_log_file,
::GetCurrentProcess(), &duplicate, 0,
/*bInheritHandle=*/TRUE, DUPLICATE_SAME_ACCESS)) {
return nullptr;
}
return duplicate;
}
#endif
// Used for testing. Declared in test/scoped_logging_settings.h.
ScopedLoggingSettings::ScopedLoggingSettings()
: min_log_level_(g_min_log_level),

@ -28,6 +28,10 @@
#include <cstdio>
#endif
#if BUILDFLAG(IS_WIN)
#include "base/win/windows_types.h"
#endif
//
// Optional message capabilities
// -----------------------------
@ -188,6 +192,7 @@ using LoggingDestination = uint32_t;
// with bitwise OR.
// Unless destination is LOG_NONE, all logs with severity ERROR and above will
// be written to stderr in addition to the specified destination.
// LOG_TO_FILE includes logging to externally-provided file handles.
enum : uint32_t {
LOG_NONE = 0,
LOG_TO_FILE = 1 << 0,
@ -248,6 +253,13 @@ struct BASE_EXPORT LoggingSettings {
// ChromeOS uses the syslog log format by default.
LogFormat log_format = LogFormat::LOG_FORMAT_SYSLOG;
#endif
#if BUILDFLAG(IS_WIN)
// Contains an optional file that logs should be written to. If present,
// `log_file_path` will be ignored, and the logging system will take ownership
// of the HANDLE. If there's an error writing to this file, no fallback paths
// will be opened.
HANDLE log_file = nullptr;
#endif
};
// Define different names for the BaseInitLoggingImpl() function depending on
@ -755,6 +767,9 @@ BASE_EXPORT bool IsLoggingToFileEnabled();
// Returns the default log file path.
BASE_EXPORT std::wstring GetLogFileFullPath();
// Duplicates the log file handle to send into a child process.
BASE_EXPORT HANDLE DuplicateLogFileHandle();
#endif
} // namespace logging

@ -70,8 +70,11 @@
#include "base/logging_win.h"
#include "base/process/process_info.h"
#include "base/syslog_logging.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "chrome/common/win/eventlog_messages.h"
#include "chrome/install_static/install_details.h"
#include "sandbox/policy/switches.h"
#endif
namespace logging {
@ -138,10 +141,35 @@ void SuppressDialogs() {
dialogs_are_suppressed_ = true;
}
} // anonymous namespace
LoggingDestination DetermineLoggingDestination(
#if BUILDFLAG(IS_WIN)
base::win::ScopedHandle GetLogInheritedHandle(
const base::CommandLine& command_line) {
auto handle_str = command_line.GetSwitchValueNative(switches::kLogFile);
uint32_t handle_value = 0;
if (!base::StringToUint(handle_str, &handle_value)) {
return base::win::ScopedHandle();
}
// Duplicate the handle from the command line so that different things can
// init logging. This means the handle from the parent is never closed, but
// there will only be one of these in the process.
HANDLE log_handle = nullptr;
if (!::DuplicateHandle(GetCurrentProcess(),
base::win::Uint32ToHandle(handle_value),
GetCurrentProcess(), &log_handle, 0,
/*bInheritHandle=*/FALSE, DUPLICATE_SAME_ACCESS)) {
return base::win::ScopedHandle();
}
// Transfer ownership to the caller.
return base::win::ScopedHandle(log_handle);
}
#endif
// `filename_is_handle`, will be set to `true` if the log-file switch contains
// an inherited handle value rather than a filepath, and `false` otherwise.
LoggingDestination LoggingDestFromCommandLine(
const base::CommandLine& command_line,
bool& filename_is_handle) {
filename_is_handle = false;
#if BUILDFLAG(IS_FUCHSIA)
// Fuchsia provides a system log that can be filtered for logs from specific
// components (e.g. Chrome), and which is easier to access than logs in a
@ -176,20 +204,41 @@ LoggingDestination DetermineLoggingDestination(
command_line.GetSwitchValueASCII(switches::kEnableLogging);
if (logging_destination == "stderr") {
return LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR;
} else if (logging_destination != "") {
PLOG(ERROR) << "Invalid logging destination: " << logging_destination;
#if BUILDFLAG(IS_WIN)
} else if (base::IsCurrentProcessInAppContainer() &&
!command_line.HasSwitch(switches::kLogFile)) {
// Sandboxed appcontainer processes are unable to resolve the default log
// file path without asserting.
return kDefaultLoggingMode & ~LOG_TO_FILE;
#endif
}
#if BUILDFLAG(IS_WIN)
if (logging_destination == "handle" &&
command_line.HasSwitch(switches::kProcessType) &&
command_line.HasSwitch(switches::kLogFile)) {
// Child processes can log to a handle duplicated from the parent, and
// provided in the log-file switch value.
filename_is_handle = true;
return kDefaultLoggingMode | LOG_TO_FILE;
}
#endif // BUILDFLAG(IS_WIN)
if (logging_destination != "") {
// The browser process should not be called with --enable-logging=handle.
LOG(ERROR) << "Invalid logging destination: " << logging_destination;
return kDefaultLoggingMode;
}
#if BUILDFLAG(IS_WIN)
if (command_line.HasSwitch(switches::kProcessType) &&
!command_line.HasSwitch(sandbox::policy::switches::kNoSandbox)) {
// Sandboxed processes cannot open log files so skip if provided.
return kDefaultLoggingMode & ~LOG_TO_FILE;
}
#endif
}
return kDefaultLoggingMode;
}
} // anonymous namespace
LoggingDestination DetermineLoggingDestination(
const base::CommandLine& command_line) {
bool unused = false;
return LoggingDestFromCommandLine(command_line, unused);
}
#if BUILDFLAG(IS_CHROMEOS)
bool RotateLogFile(const base::FilePath& target_path) {
DCHECK(!target_path.empty());
@ -367,43 +416,69 @@ void InitChromeLogging(const base::CommandLine& command_line,
OldFileDeletionState delete_old_log_file) {
DCHECK(!chrome_logging_initialized_)
<< "Attempted to initialize logging when it was already initialized.";
LoggingDestination logging_dest = DetermineLoggingDestination(command_line);
bool filename_is_handle = false;
LoggingDestination logging_dest =
LoggingDestFromCommandLine(command_line, filename_is_handle);
LogLockingState log_locking_state = LOCK_LOG_FILE;
base::FilePath log_path;
#if BUILDFLAG(IS_CHROMEOS_ASH)
base::FilePath target_path;
#endif
#if BUILDFLAG(IS_WIN)
base::win::ScopedHandle log_handle;
#endif
// Don't resolve the log path unless we need to. Otherwise we leave an open
// ALPC handle after sandbox lockdown on Windows.
if ((logging_dest & LOG_TO_FILE) != 0) {
log_path = GetLogFileName(command_line);
if (logging_dest & LOG_TO_FILE) {
if (filename_is_handle) {
#if BUILDFLAG(IS_WIN)
// Child processes on Windows are provided a file handle if logging is
// enabled as sandboxed processes cannot open files.
log_handle = GetLogInheritedHandle(command_line);
if (!log_handle.is_valid()) {
DLOG(ERROR) << "Unable to initialize logging from handle.";
chrome_logging_failed_ = true;
return;
}
#endif
} else {
log_path = GetLogFileName(command_line);
#if BUILDFLAG(IS_CHROMEOS_ASH)
// For BWSI (Incognito) logins, we want to put the logs in the user
// profile directory that is created for the temporary session instead
// of in the system log directory, for privacy reasons.
if (command_line.HasSwitch(ash::switches::kGuestSession))
log_path = GetSessionLogFile(command_line);
// For BWSI (Incognito) logins, we want to put the logs in the user
// profile directory that is created for the temporary session instead
// of in the system log directory, for privacy reasons.
if (command_line.HasSwitch(ash::switches::kGuestSession)) {
log_path = GetSessionLogFile(command_line);
}
// Prepares a log file. We rotate the previous log file and prepare a new
// log file if we've been asked to delete the old log, since that
// indicates the start of a new session.
target_path =
SetUpLogFile(log_path, delete_old_log_file == DELETE_OLD_LOG_FILE);
// Prepares a log file. We rotate the previous log file and prepare a new
// log file if we've been asked to delete the old log, since that
// indicates the start of a new session.
target_path =
SetUpLogFile(log_path, delete_old_log_file == DELETE_OLD_LOG_FILE);
// Because ChromeOS manages the move to a new session by redirecting
// the link, it shouldn't remove the old file in the logging code,
// since that will remove the newly created link instead.
delete_old_log_file = APPEND_TO_OLD_LOG_FILE;
// Because ChromeOS manages the move to a new session by redirecting
// the link, it shouldn't remove the old file in the logging code,
// since that will remove the newly created link instead.
delete_old_log_file = APPEND_TO_OLD_LOG_FILE;
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
} else {
log_locking_state = DONT_LOCK_LOG_FILE;
}
LoggingSettings settings;
settings.logging_dest = logging_dest;
settings.log_file_path = log_path.value().c_str();
if (!log_path.empty()) {
settings.log_file_path = log_path.value().c_str();
}
#if BUILDFLAG(IS_WIN)
// Avoid initializing with INVALID_HANDLE_VALUE.
// This handle is owned by the logging framework and is closed when the
// process exits.
// TODO(crbug.com/328285906) Use a ScopedHandle in logging settings.
settings.log_file = log_handle.is_valid() ? log_handle.release() : nullptr;
#endif
settings.lock_log = log_locking_state;
settings.delete_old = delete_old_log_file;
bool success = InitLogging(settings);

@ -390,12 +390,13 @@ void ChildProcessLauncherHelper::ForceNormalProcessTerminationAsync(
std::move(process)));
}
#if !BUILDFLAG(IS_WIN)
void ChildProcessLauncherHelper::PassLoggingSwitches(
base::LaunchOptions* launch_options,
base::CommandLine* cmd_line) {
const base::CommandLine& browser_command_line =
*base::CommandLine::ForCurrentProcess();
static const char* const kForwardSwitches[] = {
constexpr const char* kForwardSwitches[] = {
switches::kDisableLogging,
switches::kEnableLogging,
switches::kLogFile,
@ -405,6 +406,7 @@ void ChildProcessLauncherHelper::PassLoggingSwitches(
};
cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches);
}
#endif // !BUILDFLAG(IS_WIN)
} // namespace internal

@ -35,6 +35,7 @@
#endif
#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_handle.h"
#include "base/win/windows_types.h"
#include "content/public/common/prefetch_type_win.h"
#include "sandbox/win/src/sandbox_types.h"
@ -341,6 +342,11 @@ class ChildProcessLauncherHelper
std::unique_ptr<sandbox::policy::SandboxPolicyFuchsia> sandbox_policy_;
#endif
#if BUILDFLAG(IS_WIN)
// Only valid if the host process has logging enabled.
base::win::ScopedHandle log_handle_;
#endif
// Histogram shared memory region metadata.
base::UnsafeSharedMemoryRegion histogram_memory_region_;
};

@ -2,27 +2,42 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/child_process_launcher_helper.h"
#include "base/base_switches.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/process/process.h"
#include "base/strings/string_number_conversions.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "build/build_config.h"
#include "content/browser/child_process_launcher.h"
#include "content/browser/child_process_launcher_helper.h"
#include "content/public/browser/child_process_launcher_utils.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/sandbox_init_win.h"
#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "sandbox/policy/win/sandbox_win.h"
#include "sandbox/win/src/sandbox_types.h"
namespace {
// Helper to avoid marking the log file as non-executable every time we launch a
// process.
bool ShouldMarkLogfileAsNonExecute() {
static bool first_time = true;
if (!first_time) {
return false;
}
first_time = false;
return true;
}
// /prefetch:# arguments to use when launching various process types. It has
// been observed that when file reads are consistent for 3 process launches with
// the same /prefetch:# argument, the Windows prefetcher starts issuing reads in
@ -93,6 +108,7 @@ ChildProcessLauncherHelper::CreateNamedPlatformChannelOnLauncherThread() {
std::unique_ptr<FileMappedForLaunch>
ChildProcessLauncherHelper::GetFilesToMap() {
// Windows uses LaunchOptions to pass filehandles to children.
return nullptr;
}
@ -176,6 +192,60 @@ std::string_view ChildProcessLauncherHelper::GetPrefetchSwitch(
}
}
void ChildProcessLauncherHelper::PassLoggingSwitches(
base::LaunchOptions* launch_options,
base::CommandLine* cmd_line) {
const base::CommandLine& browser_command_line =
*base::CommandLine::ForCurrentProcess();
// Sandboxed processes on Windows cannot open files, and can't always figure
// out default paths, so we directly pass a handle if logging is enabled.
if (logging::IsLoggingToFileEnabled()) {
// Make sure we're in charge of these flags.
CHECK(!cmd_line->HasSwitch(switches::kEnableLogging));
CHECK(!cmd_line->HasSwitch(switches::kLogFile));
// Make best efforts attempt to mark the logfile as no-execute the first
// time a process is started.
if (ShouldMarkLogfileAsNonExecute()) {
// Failure here means we pass in a writeable handle to a file that could
// be marked executable and chained into a sandbox escape - but failure
// should be rare and providing a logfile is already optional.
std::ignore = base::PreventExecuteMappingUnchecked(
base::FilePath(logging::GetLogFileFullPath()),
base::PreventExecuteMappingClasses::GetPassKey());
}
log_handle_.Set(logging::DuplicateLogFileHandle());
if (log_handle_.is_valid()) {
// Override `--enable-logging --log-file=` switches so the child can log.
cmd_line->AppendSwitchASCII(switches::kEnableLogging, "handle");
auto handle_str =
base::NumberToString(base::win::HandleToUint32(log_handle_.get()));
cmd_line->AppendSwitchASCII(switches::kLogFile, handle_str);
launch_options->handles_to_inherit.push_back(log_handle_.get());
}
}
#if !defined(OFFICIAL_BUILD)
// Official builds do not send std handles to children so there is no point
// in passing --enable-logging by itself. Debug builds might need to know if
// stderr is being forced or not.
else if (browser_command_line.HasSwitch(switches::kEnableLogging)) {
std::string logging_destination =
browser_command_line.GetSwitchValueASCII(switches::kEnableLogging);
cmd_line->AppendSwitchASCII(switches::kEnableLogging, logging_destination);
}
#endif
// Forward other switches like other platforms.
constexpr const char* kForwardSwitches[] = {
switches::kDisableLogging,
switches::kLoggingLevel,
switches::kV,
switches::kVModule,
};
cmd_line->CopySwitchesFrom(browser_command_line, kForwardSwitches);
}
bool ChildProcessLauncherHelper::IsUsingLaunchOptions() {
return true;
}

@ -79,6 +79,8 @@
#include <initguid.h>
#include "base/logging_win.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "content/shell/common/v8_crashpad_support_win.h"
#endif
@ -115,6 +117,26 @@ const GUID kContentShellProviderName = {
#endif
void InitLogging(const base::CommandLine& command_line) {
#if BUILDFLAG(IS_WIN)
// On Windows child process may be given a handle in the --log-file switch.
base::win::ScopedHandle log_handle;
if (command_line.GetSwitchValueASCII(switches::kEnableLogging) == "handle") {
auto handle_str = command_line.GetSwitchValueNative(switches::kLogFile);
uint32_t handle_value = 0;
if (base::StringToUint(handle_str, &handle_value)) {
// This handle is owned by the logging framework and is closed when the
// process exits.
HANDLE duplicate = nullptr;
if (::DuplicateHandle(GetCurrentProcess(),
base::win::Uint32ToHandle(handle_value),
GetCurrentProcess(), &duplicate, 0, FALSE,
DUPLICATE_SAME_ACCESS)) {
log_handle.Set(duplicate);
}
}
}
#endif // BUILDFLAG(IS_WIN)
base::FilePath log_filename =
command_line.GetSwitchValuePath(switches::kLogFile);
if (log_filename.empty()) {
@ -128,7 +150,18 @@ void InitLogging(const base::CommandLine& command_line) {
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_ALL;
#if BUILDFLAG(IS_WIN)
if (log_handle.is_valid()) {
settings.log_file_path = nullptr;
// TODO(crbug.com/328285906) Use a ScopedHandle in logging settings.
settings.log_file = log_handle.release();
} else {
settings.log_file_path = log_filename.value().c_str();
settings.log_file = nullptr;
}
#else
settings.log_file_path = log_filename.value().c_str();
#endif
settings.delete_old = logging::DELETE_OLD_LOG_FILE;
logging::InitLogging(settings);
logging::SetLogItems(true /* Process ID */, true /* Thread ID */,