-
Notifications
You must be signed in to change notification settings - Fork 6
/
0005-Provide-ENV-controls-to-bypass-some-sb2-calls-betwee.patch
394 lines (368 loc) · 15.3 KB
/
0005-Provide-ENV-controls-to-bypass-some-sb2-calls-betwee.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: David Greaves <david.greaves@jolla.com>
Date: Wed, 20 Jan 2021 09:40:34 +0000
Subject: [PATCH] Provide ENV controls to bypass some sb2 calls between fork
exec
In threaded systems there can be deadlocks caused by calling malloc
after fork and before exec. Rust manages this but sb2 does not.
The following ENV variables are available:
SB2_RUST_EXECVP_SHIM
This executable is passed to the execvp call. The intended executable
is passed in the argv[] list. Typically set to "/usr/bin/env --". Used
wth SB2_RUST_USE_REAL_EXECVP which will not do path processing to exec
this binary but the binary will then exec the command provided via sb2
lookups.
SB2_RUST_USE_REAL_EXECVP
When set will cause the libc execvp() to be called and not the sb2
gate
SB2_RUST_USE_REAL_FN
This ensures that no sb2 calls to gates for dup2/chdir etc are made
between fork and exec
SB2_RUST_NO_SPAWNVP
When set the rust libc::spawnvp() call (which is gated) is bypassed
and the fallback fork/exec is used.
Note that some reworking of rust's handling of program/argv[0] was
needed to support the SHIM functionality
Co-authored-by: Ruben De Smet <ruben.de.smet@rubdos.be>
Signed-off-by: David Greaves <david.greaves@jolla.com>
Signed-off-by: Ruben De Smet <ruben.de.smet@rubdos.be>
---
.../src/sys/unix/process/process_common.rs | 55 ++++++-
.../std/src/sys/unix/process/process_unix.rs | 137 ++++++++++++++++--
2 files changed, 172 insertions(+), 20 deletions(-)
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index bac32d9e60e..3cd9767da88 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -85,7 +85,7 @@ pub unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::
////////////////////////////////////////////////////////////////////////////////
pub struct Command {
- program: CString,
+ pub(crate) program: CString,
args: Vec<CString>,
/// Exactly what will be passed to `execvp`.
///
@@ -94,6 +94,13 @@ pub struct Command {
/// `args` to properly update this as well.
argv: Argv,
env: CommandEnv,
+ pub(crate) execvp: Option<ExecvpFn>,
+ pub(crate) dup2: Option<Dup2Fn>,
+ pub(crate) close: Option<CloseFn>,
+ pub(crate) chdir: Option<ChdirFn>,
+ pub(crate) setuid: Option<SetuidFn>,
+ pub(crate) setgid: Option<SetgidFn>,
+ pub(crate) setgroups: Option<SetgroupsFn>,
program_kind: ProgramKind,
cwd: Option<CString>,
@@ -110,6 +117,14 @@ pub struct Command {
pgroup: Option<pid_t>,
}
+pub(crate) type ExecvpFn = fn(*const c_char, *const *const c_char) -> c_int;
+pub(crate) type Dup2Fn = fn(c_int, c_int) -> c_int;
+pub(crate) type CloseFn = fn(c_int) -> c_int;
+pub(crate) type ChdirFn = fn(*const c_char) -> c_int;
+pub(crate) type SetuidFn = fn(uid_t) -> c_int;
+pub(crate) type SetgidFn = fn(gid_t) -> c_int;
+pub(crate) type SetgroupsFn = fn(libc::size_t, *const gid_t) -> c_int;
+
// Create a new type for argv, so that we can make it `Send` and `Sync`
struct Argv(Vec<*const c_char>);
@@ -183,16 +198,24 @@ pub fn new(program: &OsStr) -> Command {
let mut saw_nul = false;
let program_kind = ProgramKind::new(program.as_ref());
let program = os2c(program, &mut saw_nul);
+ let arg0 = program.clone();
Command {
- argv: Argv(vec![program.as_ptr(), ptr::null()]),
- args: vec![program.clone()],
+ argv: Argv(vec![arg0.as_ptr(), ptr::null()]),
+ args: vec![arg0],
program,
program_kind,
env: Default::default(),
+ execvp: None,
+ dup2: None,
+ close: None,
+ chdir: None,
+ setuid: None,
+ setgid: None,
+ setgroups: None,
cwd: None,
uid: None,
gid: None,
- saw_nul,
+ saw_nul: saw_nul,
closures: Vec::new(),
groups: None,
stdin: None,
@@ -207,16 +230,24 @@ pub fn new(program: &OsStr) -> Command {
let mut saw_nul = false;
let program_kind = ProgramKind::new(program.as_ref());
let program = os2c(program, &mut saw_nul);
+ let arg0 = program.clone();
Command {
- argv: Argv(vec![program.as_ptr(), ptr::null()]),
- args: vec![program.clone()],
+ argv: Argv(vec![arg0.as_ptr(), ptr::null()]),
+ args: vec![arg0],
program,
program_kind,
env: Default::default(),
+ execvp: None,
+ dup2: None,
+ close: None,
+ chdir: None,
+ setuid: None,
+ setgid: None,
+ setgroups: None,
cwd: None,
uid: None,
gid: None,
- saw_nul,
+ saw_nul: saw_nul,
closures: Vec::new(),
groups: None,
stdin: None,
@@ -227,6 +258,16 @@ pub fn new(program: &OsStr) -> Command {
}
}
+ // This allows process_unix::{spawn, exec} to push program to the
+ // start of /usr/bin/env's arg list
+ pub fn insert_program(&mut self, arg: String) {
+ let arg = OsString::from(arg);
+ let arg = os2c(&arg, &mut self.saw_nul);
+ self.program = arg.clone();
+ self.argv.0.insert(0, arg.as_ptr());
+ self.args.insert(0, arg);
+ }
+
pub fn set_arg_0(&mut self, arg: &OsStr) {
// Set a new arg0
let arg = os2c(arg, &mut self.saw_nul);
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 72aca4e6659..495368b58ec 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -25,7 +25,7 @@
use libc::RTP_ID as pid_t;
#[cfg(not(target_os = "vxworks"))]
-use libc::{c_int, pid_t};
+use libc::{c_char, c_int, dlsym, pid_t};
#[cfg(not(any(
target_os = "vxworks",
@@ -62,6 +62,10 @@ fn get_clock_resolution() -> Duration {
}
}
+use crate::ffi::OsString;
+use crate::intrinsics::transmute;
+use sys::os::getenv;
+
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
@@ -95,6 +99,65 @@ pub fn spawn(
#[cfg(not(target_os = "linux"))]
let (input, output) = sys::pipe::anon_pipe()?;
+ // If there is a RUST_EXEC_SHIM (could be "/usr/bin/env --")
+ // then we're probably going to directly execvp it via dlsym
+ // to avoid issues with threads and malloc post-fork and
+ // pre-exec. That will then re-execvp but this time sb2 will
+ // do magic. See also RUST_EXECVP_REAL
+
+ // We do this here and pass so do_exec() so any malloc's are
+ // pre-fork()
+
+ // At this point self.program is the real program. argv[0] is
+ // now a clone() of program.
+
+ let libc_h =
+ unsafe { libc::dlopen("libc.so.6\0".as_ptr() as *const c_char, libc::RTLD_LAZY) };
+
+ match getenv(&OsString::from("SB2_RUST_EXECVP_SHIM")) {
+ Some(var) => {
+ // handle "/usr/bin/env <arg> <arg>"
+ let var = var.into_string().expect("Valid string"); // so we can .split()
+ let words: Vec<&str> = var.as_str().split(" ").collect();
+ for w in words.iter().rev() {
+ self.insert_program(w.to_string());
+ }
+ // At this point self.program is the SHIM. argv[0] is
+ // the SHIM and argv[>0] is the real program.
+ }
+ None => {} // Business as usual
+ };
+ match getenv(&OsString::from("SB2_RUST_USE_REAL_EXECVP")) {
+ Some(_var) => unsafe {
+ let real_execvp_p =
+ dlsym(libc_h, "execvp\0".as_ptr() as *const c_char) as *const ();
+ self.execvp = Some(transmute::<*const (), ExecvpFn>(real_execvp_p));
+ },
+ None => {}
+ };
+ match getenv(&OsString::from("SB2_RUST_USE_REAL_FN")) {
+ Some(_var) => unsafe {
+ let real_dup2_p = dlsym(libc_h, "dup2\0".as_ptr() as *const c_char) as *const ();
+ self.dup2 = Some(transmute::<*const (), Dup2Fn>(real_dup2_p));
+ let real_close_p = dlsym(libc_h, "close\0".as_ptr() as *const c_char) as *const ();
+ self.close = Some(transmute::<*const (), CloseFn>(real_close_p));
+ let real_chdir_p = dlsym(libc_h, "chdir\0".as_ptr() as *const c_char) as *const ();
+ self.chdir = Some(transmute::<*const (), ChdirFn>(real_chdir_p));
+ let real_setuid_p =
+ dlsym(libc_h, "setuid\0".as_ptr() as *const c_char) as *const ();
+ self.setuid = Some(transmute::<*const (), SetuidFn>(real_setuid_p));
+ let real_setgid_p =
+ dlsym(libc_h, "setgid\0".as_ptr() as *const c_char) as *const ();
+ self.setgid = Some(transmute::<*const (), SetgidFn>(real_setgid_p));
+ let real_setgroups_p =
+ dlsym(libc_h, "setgroups\0".as_ptr() as *const c_char) as *const ();
+ self.setgroups = Some(transmute::<*const (), SetgroupsFn>(real_setgroups_p));
+ },
+ None => {}
+ };
+ // We close before calling but that's OK as this is just a lookup handle
+ unsafe { cvt(libc::dlclose(libc_h))? };
+
// Whatever happens after the fork is almost for sure going to touch or
// look at the environment in one way or another (PATH in `execvp` or
// accessing the `environ` pointer ourselves). Make sure no other thread
@@ -111,7 +174,7 @@ pub fn spawn(
if pid == 0 {
crate::panic::always_abort();
mem::forget(env_lock); // avoid non-async-signal-safe unlocking
- drop(input);
+ self.unwrap_drop(input);
#[cfg(target_os = "linux")]
if self.get_create_pidfd() {
self.send_pidfd(&output);
@@ -268,7 +331,47 @@ pub fn exec(&mut self, default: Stdio) -> io::Error {
Err(e) => e,
}
}
-
+ fn unwrap_drop(&mut self, fh: impl crate::os::unix::io::AsRawFd) {
+ // drop() simply calls libc::close(fh.fd)
+ match self.close {
+ Some(real_close) => {
+ (real_close)(fh.as_raw_fd());
+ }
+ None => {
+ drop(fh);
+ }
+ }
+ }
+ fn unwrap_dup2(&mut self, src: c_int, dst: c_int) -> c_int {
+ match self.dup2 {
+ Some(real_dup2) => (real_dup2)(src, dst),
+ None => unsafe { libc::dup2(src, dst) },
+ }
+ }
+ fn unwrap_chdir(&self, dir: *const c_char) -> c_int {
+ match self.chdir {
+ Some(real_chdir) => (real_chdir)(dir),
+ None => unsafe { libc::chdir(dir) },
+ }
+ }
+ fn unwrap_setuid(&self, uid: uid_t) -> c_int {
+ match self.setuid {
+ Some(real_setuid) => (real_setuid)(uid),
+ None => unsafe { libc::setuid(uid) },
+ }
+ }
+ fn unwrap_setgid(&self, gid: gid_t) -> c_int {
+ match self.setgid {
+ Some(real_setgid) => (real_setgid)(gid),
+ None => unsafe { libc::setgid(gid) },
+ }
+ }
+ fn unwrap_setgroups(&self, ngroups: libc::size_t, gid: *const gid_t) -> c_int {
+ match self.setgroups {
+ Some(real_setgroups) => (real_setgroups)(ngroups, gid),
+ None => unsafe { libc::setgroups(ngroups, gid) },
+ }
+ }
// And at this point we've reached a special time in the life of the
// child. The child must now be considered hamstrung and unable to
// do anything other than syscalls really. Consider the following
@@ -308,13 +411,13 @@ unsafe fn do_exec(
use crate::sys::{self, cvt_r};
if let Some(fd) = stdio.stdin.fd() {
- cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
+ cvt_r(|| self.unwrap_dup2(fd, libc::STDIN_FILENO))?;
}
if let Some(fd) = stdio.stdout.fd() {
- cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
+ cvt_r(|| self.unwrap_dup2(fd, libc::STDOUT_FILENO))?;
}
if let Some(fd) = stdio.stderr.fd() {
- cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
+ cvt_r(|| self.unwrap_dup2(fd, libc::STDERR_FILENO))?;
}
#[cfg(not(target_os = "l4re"))]
@@ -322,10 +425,10 @@ unsafe fn do_exec(
if let Some(_g) = self.get_groups() {
//FIXME: Redox kernel does not support setgroups yet
#[cfg(not(target_os = "redox"))]
- cvt(libc::setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
+ cvt(self.unwrap_setgroups(_g.len().try_into().unwrap(), _g.as_ptr()))?;
}
if let Some(u) = self.get_gid() {
- cvt(libc::setgid(u as gid_t))?;
+ cvt(self.unwrap_setgid(u as gid_t))?;
}
if let Some(u) = self.get_uid() {
// When dropping privileges from root, the `setgroups` call
@@ -337,13 +440,13 @@ unsafe fn do_exec(
//FIXME: Redox kernel does not support setgroups yet
#[cfg(not(target_os = "redox"))]
if libc::getuid() == 0 && self.get_groups().is_none() {
- cvt(libc::setgroups(0, crate::ptr::null()))?;
+ cvt(self.unwrap_setgroups(0, crate::ptr::null()))?;
}
- cvt(libc::setuid(u as uid_t))?;
+ cvt(self.unwrap_setuid(u as uid_t))?;
}
}
if let Some(ref cwd) = *self.get_cwd() {
- cvt(libc::chdir(cwd.as_ptr()))?;
+ cvt(self.unwrap_chdir(cwd.as_ptr()))?;
}
if let Some(pgroup) = self.get_pgroup() {
@@ -408,8 +511,12 @@ fn drop(&mut self) {
_reset = Some(Reset(*sys::os::environ()));
*sys::os::environ() = envp.as_ptr();
}
-
- libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
+ match self.execvp {
+ Some(real_execvp) => {
+ (real_execvp)(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr())
+ }
+ None => libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr()),
+ };
Err(io::Error::last_os_error())
}
@@ -436,6 +543,7 @@ fn posix_spawn(
_: &ChildPipes,
_: Option<&CStringArray>,
) -> io::Result<Option<Process>> {
+ eprintln!("process_unix:270: in null posix_spawn");
Ok(None)
}
@@ -459,12 +567,15 @@ fn posix_spawn(
use crate::mem::MaybeUninit;
use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified};
+ let skip_spawnvp: bool = getenv(&OsString::from("SB2_RUST_NO_SPAWNVP")).is_some();
+
if self.get_gid().is_some()
|| self.get_uid().is_some()
|| (self.env_saw_path() && !self.program_is_path())
|| !self.get_closures().is_empty()
|| self.get_groups().is_some()
|| self.get_create_pidfd()
+ || skip_spawnvp
{
return Ok(None);
}
--
2.43.0