summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/base/set_process_title_linux.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /ipc/chromium/src/base/set_process_title_linux.cc
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/chromium/src/base/set_process_title_linux.cc')
-rw-r--r--ipc/chromium/src/base/set_process_title_linux.cc206
1 files changed, 206 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/set_process_title_linux.cc b/ipc/chromium/src/base/set_process_title_linux.cc
new file mode 100644
index 0000000000..fed166a524
--- /dev/null
+++ b/ipc/chromium/src/base/set_process_title_linux.cc
@@ -0,0 +1,206 @@
+// Copyright 2009 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file implements BSD-style setproctitle() for Linux.
+// It is written such that it can easily be compiled outside Chromium.
+//
+// (This copy has been modified for use in the Mozilla codebase.)
+//
+// The Linux kernel sets up two locations in memory to pass arguments and
+// environment variables to processes. First, there are two char* arrays stored
+// one after another: argv and environ. A pointer to argv is passed to main(),
+// while glibc sets the global variable |environ| to point at the latter. Both
+// of these arrays are terminated by a null pointer; the environment array is
+// also followed by some empty space to allow additional variables to be added.
+//
+// These arrays contain pointers to a second location in memory, where the
+// strings themselves are stored one after another: first all the arguments,
+// then the environment variables.
+//
+// When the kernel reads the command line arguments for a process, it looks at
+// the range of memory that it initially used for the argument list. If the
+// terminating '\0' character is still where it expects, nothing further is
+// done. If it has been overwritten, the kernel will scan up to the size of
+// a page looking for another.
+//
+// Thus to change the process title, we must move any arguments and environment
+// variables out of the way to make room for a potentially longer title, and
+// then overwrite the memory pointed to by argv[0] with a single replacement
+// string, making sure its size does not exceed the available space.
+//
+// See the following kernel commit for the details of the contract between
+// kernel and setproctitle:
+// https://github.com/torvalds/linux/commit/2954152298c37804dab49d630aa959625b50cf64
+//
+// It is perhaps worth noting that patches to add a system call to Linux for
+// this, like in BSD, have never made it in: this is the "official" way to do
+// this on Linux. Presumably it is not in glibc due to some disagreement over
+// this position within the glibc project, leaving applications caught in the
+// middle. (Also, only a very few applications need or want this anyway.)
+
+#include "base/set_process_title_linux.h"
+
+#include "mozilla/UniquePtrExtensions.h"
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+extern char** environ;
+
+// g_orig_argv0 is the original process name found in argv[0].
+// It is set to a copy of argv[0] in setproctitle_init. It is nullptr if
+// setproctitle_init was unsuccessful or not called.
+static const char* g_orig_argv0 = nullptr;
+
+// Following pointers hold the initial argv/envp memory range.
+// They are initialized in setproctitle_init and are used to overwrite the
+// argv/envp memory range with a new process title to be read by the kernel.
+// They are nullptr if setproctitle_init was unsuccessful or not called.
+// Note that g_envp_start is not necessary because it is the same as g_argv_end.
+static char* g_argv_start = nullptr;
+static char* g_argv_end = nullptr;
+static char* g_envp_end = nullptr;
+
+void setproctitle(const char* fmt, ...) {
+ va_list ap;
+
+ // Sanity check before we try and set the process title.
+ // The BSD version allows a null fmt to restore the original title.
+ if (!g_orig_argv0 || !fmt) {
+ return;
+ }
+
+ // The title can be up to the end of envp.
+ const size_t avail_size = g_envp_end - g_argv_start - 1;
+
+ // Linux 4.18--5.2 have a bug where we can never set a process title
+ // shorter than the initial argv. Check if the bug exists in the current
+ // kernel on the first call of setproctitle.
+ static const bool buggy_kernel = [avail_size]() {
+ // Attempt to set an empty title. This will set cmdline to:
+ // "" (on Linux --4.17)
+ // "\0\0\0...\0\0\0.\0" (on Linux 4.18--5.2)
+ // "\0" (on Linux 5.3--)
+ memset(g_argv_start, 0, avail_size + 1);
+ g_argv_end[-1] = '.';
+
+ mozilla::UniqueFileHandle fd(
+ open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC));
+ if (!fd) {
+ return false;
+ }
+
+ // We just want to see if there are at least 2 bytes in the file;
+ // we don't need to read the whole contents. Short reads probably
+ // aren't possible given how this procfs node is implemented, but
+ // it's not much more code to handle it anyway.
+ char buf[2];
+ ssize_t total_read = 0;
+ while (total_read < 2) {
+ ssize_t rd = read(fd.get(), buf, 2);
+ if (rd <= 0) {
+ return false;
+ }
+ total_read += rd;
+ }
+ return true;
+ }();
+
+ memset(g_argv_start, 0, avail_size + 1);
+
+ size_t size;
+ va_start(ap, fmt);
+ if (fmt[0] == '-') {
+ size = vsnprintf(g_argv_start, avail_size, &fmt[1], ap);
+ } else {
+ size = snprintf(g_argv_start, avail_size, "%s ", g_orig_argv0);
+ if (size < avail_size) {
+ size += vsnprintf(&g_argv_start[size], avail_size - size, fmt, ap);
+ }
+ }
+ va_end(ap);
+
+ // Kernel looks for a null terminator instead of the initial argv space
+ // when the end of the space is not terminated with a null.
+ // https://github.com/torvalds/linux/commit/d26d0cd97c88eb1a5704b42e41ab443406807810
+ //
+ // If the length of the new title is shorter than the original argv space,
+ // set the last byte of the space to an arbitrary non-null character to tell
+ // the kernel that setproctitle was called.
+ //
+ // On buggy kernels we can never make the process title shorter than the
+ // initial argv. In that case, just leave the remaining bytes filled with
+ // null characters.
+ const size_t argv_size = g_argv_end - g_argv_start - 1;
+ if (!buggy_kernel && size < argv_size) {
+ g_argv_end[-1] = '.';
+ }
+}
+
+// A version of this built into glibc would not need this function, since
+// it could stash the argv pointer in __libc_start_main(). But we need it.
+void setproctitle_init(char** main_argv) {
+ static bool init_called = false;
+ if (init_called) {
+ return;
+ }
+ init_called = true;
+
+ if (!main_argv) {
+ return;
+ }
+
+ // Verify that the memory layout matches expectation.
+ char** const argv = main_argv;
+ char* argv_start = argv[0];
+ char* p = argv_start;
+ for (size_t i = 0; argv[i]; ++i) {
+ if (p != argv[i]) {
+ return;
+ }
+ p += strlen(p) + 1;
+ }
+ char* argv_end = p;
+ size_t environ_size = 0;
+ for (size_t i = 0; environ[i]; ++i, ++environ_size) {
+ if (p != environ[i]) {
+ return;
+ }
+ p += strlen(p) + 1;
+ }
+ char* envp_end = p;
+
+ // Copy the arg and env strings into the heap. Leak Sanitizer
+ // doesn't seem to object to these strdup()s; if it ever does, we
+ // can always ensure the pointers are reachable from globals or add
+ // a suppresion for this function.
+ //
+ // Note that Chromium's version of this code didn't copy the
+ // arguments; this is probably because they access args via the
+ // CommandLine class, which copies into a std::vector<std::string>,
+ // but in general that's not a safe assumption for Gecko.
+ for (size_t i = 0; argv[i]; ++i) {
+ argv[i] = strdup(argv[i]);
+ }
+ for (size_t i = 0; environ[i]; ++i) {
+ environ[i] = strdup(environ[i]);
+ }
+
+ if (!argv[0]) {
+ return;
+ }
+
+ g_orig_argv0 = argv[0];
+ g_argv_start = argv_start;
+ g_argv_end = argv_end;
+ g_envp_end = envp_end;
+}