Skip to content

Commit d394dd7

Browse files
bnoordhuisaddaleax
authored andcommitted
src: fix OOB reads in process.title getter
The getter passed a stack-allocated, fixed-size buffer to uv_get_process_title() but neglected to check the return value. When the total length of the command line arguments exceeds the size of the buffer, libuv returns UV_ENOBUFS and doesn't modify the contents of the buffer. The getter then proceeded to return whatever garbage was on the stack at the time of the call, quite possibly reading beyond the end of the buffer. Add a GetProcessTitle() helper that reads the process title into a dynamically allocated buffer that is resized when necessary. Fixes: #31631 PR-URL: #31633 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 7df4298 commit d394dd7

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed

src/node_internals.h

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ void ResetStdio(); // Safe to call more than once and from signal handlers.
9595
void SignalExit(int signal, siginfo_t* info, void* ucontext);
9696
#endif
9797

98+
std::string GetProcessTitle(const char* default_title);
9899
std::string GetHumanReadableProcessName();
99100
void GetHumanReadableProcessName(char (*name)[1024]);
100101

src/node_process_object.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ using v8::Value;
3232

3333
static void ProcessTitleGetter(Local<Name> property,
3434
const PropertyCallbackInfo<Value>& info) {
35-
char buffer[512];
36-
uv_get_process_title(buffer, sizeof(buffer));
35+
std::string title = GetProcessTitle("node");
3736
info.GetReturnValue().Set(
38-
String::NewFromUtf8(info.GetIsolate(), buffer, NewStringType::kNormal)
39-
.ToLocalChecked());
37+
String::NewFromUtf8(info.GetIsolate(), title.data(),
38+
NewStringType::kNormal, title.size())
39+
.ToLocalChecked());
4040
}
4141

4242
static void ProcessTitleSetter(Local<Name> property,

src/node_v8_platform-inl.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ class NodeTraceStateObserver
2121
: public v8::TracingController::TraceStateObserver {
2222
public:
2323
inline void OnTraceEnabled() override {
24-
char name_buffer[512];
25-
if (uv_get_process_title(name_buffer, sizeof(name_buffer)) == 0) {
24+
std::string title = GetProcessTitle("");
25+
if (!title.empty()) {
2626
// Only emit the metadata event if the title can be retrieved
2727
// successfully. Ignore it otherwise.
2828
TRACE_EVENT_METADATA1(
29-
"__metadata", "process_name", "name", TRACE_STR_COPY(name_buffer));
29+
"__metadata", "process_name", "name", TRACE_STR_COPY(title.c_str()));
3030
}
3131
TRACE_EVENT_METADATA1("__metadata",
3232
"version",

src/util.cc

+25-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "util.h" // NOLINT(build/include_inline)
2323
#include "util-inl.h"
2424

25+
#include "debug_utils-inl.h"
2526
#include "env-inl.h"
2627
#include "node_buffer.h"
2728
#include "node_errors.h"
@@ -45,6 +46,7 @@
4546

4647
#include <atomic>
4748
#include <cstdio>
49+
#include <cstring>
4850
#include <iomanip>
4951
#include <sstream>
5052

@@ -133,10 +135,30 @@ void LowMemoryNotification() {
133135
}
134136
}
135137

138+
std::string GetProcessTitle(const char* default_title) {
139+
std::string buf(16, '\0');
140+
141+
for (;;) {
142+
const int rc = uv_get_process_title(&buf[0], buf.size());
143+
144+
if (rc == 0)
145+
break;
146+
147+
if (rc != UV_ENOBUFS)
148+
return default_title;
149+
150+
buf.resize(2 * buf.size());
151+
}
152+
153+
// Strip excess trailing nul bytes. Using strlen() here is safe,
154+
// uv_get_process_title() always zero-terminates the result.
155+
buf.resize(strlen(&buf[0]));
156+
157+
return buf;
158+
}
159+
136160
std::string GetHumanReadableProcessName() {
137-
char name[1024];
138-
GetHumanReadableProcessName(&name);
139-
return name;
161+
return SPrintF("%s[%d]", GetProcessTitle("Node.js"), uv_os_getpid());
140162
}
141163

142164
void GetHumanReadableProcessName(char (*name)[1024]) {

test/parallel/test-process-title.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
const common = require('../common');
3+
const { spawnSync } = require('child_process');
4+
const { strictEqual } = require('assert');
5+
6+
// FIXME add sunos support
7+
if (common.isSunOS)
8+
common.skip(`Unsupported platform [${process.platform}]`);
9+
// FIXME add IBMi support
10+
if (common.isIBMi)
11+
common.skip('Unsupported platform IBMi');
12+
13+
// Explicitly assigning to process.title before starting the child process
14+
// is necessary otherwise *its* process.title is whatever the last
15+
// SetConsoleTitle() call in our process tree set it to.
16+
// Can be removed when https://github.com/libuv/libuv/issues/2667 is fixed.
17+
if (common.isWindows)
18+
process.title = process.execPath;
19+
20+
const xs = 'x'.repeat(1024);
21+
const proc = spawnSync(process.execPath, ['-p', 'process.title', xs]);
22+
strictEqual(proc.stdout.toString().trim(), process.execPath);

0 commit comments

Comments
 (0)