Backed out 2 changesets (bug 1943998) for causing wd failures @ phases.py CLOSED...
[gecko.git] / ipc / chromium / src / base / set_process_title_linux.cc
blobc108460e1795efc51a46f865c2bf0209493d99d4
1 // Copyright 2009 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file implements BSD-style setproctitle() for Linux.
6 // It is written such that it can easily be compiled outside Chromium.
7 //
8 // (This copy has been modified for use in the Mozilla codebase.)
9 //
10 // The Linux kernel sets up two locations in memory to pass arguments and
11 // environment variables to processes. First, there are two char* arrays stored
12 // one after another: argv and environ. A pointer to argv is passed to main(),
13 // while glibc sets the global variable |environ| to point at the latter. Both
14 // of these arrays are terminated by a null pointer; the environment array is
15 // also followed by some empty space to allow additional variables to be added.
17 // These arrays contain pointers to a second location in memory, where the
18 // strings themselves are stored one after another: first all the arguments,
19 // then the environment variables.
21 // When the kernel reads the command line arguments for a process, it looks at
22 // the range of memory that it initially used for the argument list. If the
23 // terminating '\0' character is still where it expects, nothing further is
24 // done. If it has been overwritten, the kernel will scan up to the size of
25 // a page looking for another.
27 // Thus to change the process title, we must move any arguments and environment
28 // variables out of the way to make room for a potentially longer title, and
29 // then overwrite the memory pointed to by argv[0] with a single replacement
30 // string, making sure its size does not exceed the available space.
32 // See the following kernel commit for the details of the contract between
33 // kernel and setproctitle:
34 // https://github.com/torvalds/linux/commit/2954152298c37804dab49d630aa959625b50cf64
36 // It is perhaps worth noting that patches to add a system call to Linux for
37 // this, like in BSD, have never made it in: this is the "official" way to do
38 // this on Linux. Presumably it is not in glibc due to some disagreement over
39 // this position within the glibc project, leaving applications caught in the
40 // middle. (Also, only a very few applications need or want this anyway.)
42 #include "base/set_process_title_linux.h"
44 #include "mozilla/UniquePtrExtensions.h"
46 #include <fcntl.h>
47 #include <stdarg.h>
48 #include <stddef.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <unistd.h>
54 #include <string>
55 #include <vector>
57 extern char** environ;
59 // g_orig_argv0 is the original process name found in argv[0].
60 // It is set to a copy of argv[0] in setproctitle_init. It is nullptr if
61 // setproctitle_init was unsuccessful or not called.
62 static const char* g_orig_argv0 = nullptr;
64 // Following pointers hold the initial argv/envp memory range.
65 // They are initialized in setproctitle_init and are used to overwrite the
66 // argv/envp memory range with a new process title to be read by the kernel.
67 // They are nullptr if setproctitle_init was unsuccessful or not called.
68 // Note that g_envp_start is not necessary because it is the same as g_argv_end.
69 static char* g_argv_start = nullptr;
70 static char* g_argv_end = nullptr;
71 static char* g_envp_end = nullptr;
73 void setproctitle(const char* fmt, ...) {
74 va_list ap;
76 // Sanity check before we try and set the process title.
77 // The BSD version allows a null fmt to restore the original title.
78 if (!g_orig_argv0 || !fmt) {
79 return;
82 // The title can be up to the end of envp.
83 const size_t avail_size = g_envp_end - g_argv_start - 1;
85 // Linux 4.18--5.2 have a bug where we can never set a process title
86 // shorter than the initial argv. Check if the bug exists in the current
87 // kernel on the first call of setproctitle.
88 static const bool buggy_kernel = [avail_size]() {
89 // Attempt to set an empty title. This will set cmdline to:
90 // "" (on Linux --4.17)
91 // "\0\0\0...\0\0\0.\0" (on Linux 4.18--5.2)
92 // "\0" (on Linux 5.3--)
93 memset(g_argv_start, 0, avail_size + 1);
94 g_argv_end[-1] = '.';
96 mozilla::UniqueFileHandle fd(
97 open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC));
98 if (!fd) {
99 return false;
102 // We just want to see if there are at least 2 bytes in the file;
103 // we don't need to read the whole contents. Short reads probably
104 // aren't possible given how this procfs node is implemented, but
105 // it's not much more code to handle it anyway.
106 char buf[2];
107 ssize_t total_read = 0;
108 while (total_read < 2) {
109 ssize_t rd = read(fd.get(), buf, 2);
110 if (rd <= 0) {
111 return false;
113 total_read += rd;
115 return true;
116 }();
118 memset(g_argv_start, 0, avail_size + 1);
120 size_t size;
121 va_start(ap, fmt);
122 if (fmt[0] == '-') {
123 size = vsnprintf(g_argv_start, avail_size, &fmt[1], ap);
124 } else {
125 size = snprintf(g_argv_start, avail_size, "%s ", g_orig_argv0);
126 if (size < avail_size) {
127 size += vsnprintf(&g_argv_start[size], avail_size - size, fmt, ap);
130 va_end(ap);
132 // Kernel looks for a null terminator instead of the initial argv space
133 // when the end of the space is not terminated with a null.
134 // https://github.com/torvalds/linux/commit/d26d0cd97c88eb1a5704b42e41ab443406807810
136 // If the length of the new title is shorter than the original argv space,
137 // set the last byte of the space to an arbitrary non-null character to tell
138 // the kernel that setproctitle was called.
140 // On buggy kernels we can never make the process title shorter than the
141 // initial argv. In that case, just leave the remaining bytes filled with
142 // null characters.
143 const size_t argv_size = g_argv_end - g_argv_start - 1;
144 if (!buggy_kernel && size < argv_size) {
145 g_argv_end[-1] = '.';
148 const size_t previous_size = g_argv_end - g_argv_start - 1;
149 ssize_t need_to_save = static_cast<ssize_t>(size - previous_size);
151 // The argv part has grown so there is less room for the environ part.
152 // Selectively removing a few environment variables so this can fit.
154 // The goal is trying to make sure that the environment in the
155 // /proc/PID/environ content for crashes is useful
156 const char* kEnvSkip[] = {"HOME=", "LS_COLORS=", "PATH=", "XDG_DATA_DIRS="};
157 const size_t kEnvElems = sizeof(kEnvSkip) / sizeof(kEnvSkip[0]);
159 size_t environ_size = 0;
160 for (size_t i = 0; environ[i]; ++i) {
161 bool skip = false;
162 const size_t var_size = strlen(environ[i]) + 1;
164 for (size_t remI = 0; need_to_save > 0 && remI < kEnvElems; ++remI) {
165 const char* thisEnv = kEnvSkip[remI];
166 int diff = strncmp(environ[i], thisEnv, strlen(thisEnv));
167 if (diff == 0) {
168 need_to_save -= static_cast<ssize_t>(var_size);
169 skip = true;
170 break;
174 if (skip) {
175 continue;
178 char* env_start = g_argv_start + size + 1 + environ_size;
179 if ((env_start + var_size) < g_envp_end) {
180 const size_t var_size_copied =
181 snprintf(env_start, var_size, "%s", environ[i]);
182 environ_size += var_size_copied + 1 /* account for null */;
187 // A version of this built into glibc would not need this function, since
188 // it could stash the argv pointer in __libc_start_main(). But we need it.
189 void setproctitle_init(char** main_argv) {
190 static bool init_called = false;
191 if (init_called) {
192 return;
194 init_called = true;
196 if (!main_argv) {
197 return;
200 // Verify that the memory layout matches expectation.
201 char** const argv = main_argv;
202 char* argv_start = argv[0];
203 char* p = argv_start;
204 for (size_t i = 0; argv[i]; ++i) {
205 if (p != argv[i]) {
206 return;
208 p += strlen(p) + 1;
210 char* argv_end = p;
211 size_t environ_size = 0;
212 for (size_t i = 0; environ[i]; ++i, ++environ_size) {
213 if (p != environ[i]) {
214 return;
216 p += strlen(p) + 1;
218 char* envp_end = p;
220 // Copy the arg and env strings into the heap. Leak Sanitizer
221 // doesn't seem to object to these strdup()s; if it ever does, we
222 // can always ensure the pointers are reachable from globals or add
223 // a suppresion for this function.
225 // Note that Chromium's version of this code didn't copy the
226 // arguments; this is probably because they access args via the
227 // CommandLine class, which copies into a std::vector<std::string>,
228 // but in general that's not a safe assumption for Gecko.
229 for (size_t i = 0; argv[i]; ++i) {
230 argv[i] = strdup(argv[i]);
232 for (size_t i = 0; environ[i]; ++i) {
233 environ[i] = strdup(environ[i]);
236 if (!argv[0]) {
237 return;
240 g_orig_argv0 = argv[0];
241 g_argv_start = argv_start;
242 g_argv_end = argv_end;
243 g_envp_end = envp_end;