Bug 1352893: Handle non-UTF-8 data in Unix environment variables. r=mstange

MozReview-Commit-ID: 5aRVYQICc7O

--HG--
extra : rebase_source : 6244a8ba08bad6da90496f27e9bb4eaace5e6fb9
extra : amend_source : f997353c8b075c989ace2790ae73330fd375b558
This commit is contained in:
Kris Maglione 2017-04-02 18:46:10 -07:00
Родитель 1fd72486a0
Коммит 0638745d18
4 изменённых файлов: 89 добавлений и 9 удалений

Просмотреть файл

@ -18,6 +18,8 @@ let EXPORTED_SYMBOLS = ["Subprocess"];
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.importGlobalProperties(["TextEncoder"]);
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/subprocess/subprocess_common.jsm");
@ -30,6 +32,19 @@ if (AppConstants.platform == "win") {
"resource://gre/modules/subprocess/subprocess_unix.jsm");
}
function encodeEnvVar(name, value) {
if (typeof name === "string" && typeof value === "string") {
return `${name}=${value}`;
}
let encoder = new TextEncoder("utf-8");
function encode(val) {
return typeof val === "string" ? encoder.encode(val) : val;
}
return Uint8Array.of(...encode(name), ...encode("="), ...encode(value), 0);
}
/**
* Allows for creation of and communication with OS-level sub-processes.
* @namespace
@ -102,8 +117,8 @@ var Subprocess = {
Object.assign(environment, options.environment);
}
options.environment = Object.keys(environment)
.map(key => `${key}=${environment[key]}`);
options.environment = Object.entries(environment)
.map(([key, val]) => encodeEnvVar(key, val));
options.arguments = Array.from(options.arguments || []);

Просмотреть файл

@ -41,6 +41,14 @@ var libc = new Library("libc", LIBC_CHOICES, {
ctypes.char.ptr.ptr.ptr,
],
setenv: [
ctypes.default_abi,
ctypes.int,
ctypes.char.ptr,
ctypes.char.ptr,
ctypes.int,
],
chdir: [
ctypes.default_abi,
ctypes.int,

Просмотреть файл

@ -13,6 +13,8 @@
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.importGlobalProperties(["TextDecoder"]);
var EXPORTED_SYMBOLS = ["SubprocessImpl"];
Cu.import("resource://gre/modules/ctypes.jsm");
@ -76,6 +78,21 @@ class Process extends BaseProcess {
}
}
// Convert a null-terminated char pointer into a sized char array, and then
// convert that into a JS typed array.
// The resulting array will not be null-terminated.
function ptrToUint8Array(input) {
let {cast, uint8_t} = ctypes;
let len = 0;
for (let ptr = cast(input, uint8_t.ptr); ptr.contents; ptr = ptr.increment()) {
len++;
}
let aryPtr = cast(input, uint8_t.array(len).ptr);
return new Uint8Array(aryPtr.contents);
}
var SubprocessUnix = {
Process,
@ -91,13 +108,26 @@ var SubprocessUnix = {
environ = libc.environ;
}
for (let envp = environ; !envp.contents.isNull(); envp = envp.increment()) {
let str = envp.contents.readString();
const EQUAL = "=".charCodeAt(0);
let decoder = new TextDecoder("utf-8", {fatal: true});
let idx = str.indexOf("=");
if (idx >= 0) {
yield [str.slice(0, idx),
str.slice(idx + 1)];
function decode(array) {
try {
return decoder.decode(array);
} catch (e) {
return array;
}
}
for (let envp = environ; !envp.contents.isNull(); envp = envp.increment()) {
let buf = ptrToUint8Array(envp.contents);
for (let i = 0; i < buf.length; i++) {
if (buf[i] == EQUAL) {
yield [decode(buf.subarray(0, i)),
decode(buf.subarray(i + 1))];
break;
}
}
}
},
@ -146,7 +176,7 @@ var SubprocessUnix = {
}
let dirs = [];
if (environment.PATH) {
if (typeof environment.PATH === "string") {
dirs = environment.PATH.split(":");
}

Просмотреть файл

@ -716,6 +716,33 @@ add_task(function* test_subprocess_environmentAppend() {
equal(exitCode, 0, "Got expected exit code");
});
if (AppConstants.platform !== "win") {
add_task(function* test_subprocess_nonASCII() {
const {libc} = Cu.import("resource://gre/modules/subprocess/subprocess_unix.jsm", {});
// Use TextDecoder rather than a string with a \xff escape, since
// the latter will automatically be normalized to valid UTF-8.
let val = new TextDecoder().decode(Uint8Array.of(1, 255));
libc.setenv("FOO", Uint8Array.from(val + "\0", c => c.charCodeAt(0)), 1);
let proc = yield Subprocess.call({
command: PYTHON,
arguments: ["-u", TEST_SCRIPT, "env", "FOO"],
});
let foo = yield read(proc.stdout);
equal(foo, val, "Got expected $FOO value");
env.set("FOO", "");
let {exitCode} = yield proc.wait();
equal(exitCode, 0, "Got expected exit code");
});
}
add_task(function* test_bad_executable() {
// Test with a non-executable file.