Skip to content

Commit 00b9ddb

Browse files
committed
Auto merge of #55611 - alexcrichton:beta-next, r=alexcrichton
[beta] Fixes #46775 -- don't mutate the process's environment in Command::exec This is a backport of the following PRs: * #55359 * #55569 * #55304
2 parents 04da282 + 39fa89b commit 00b9ddb

File tree

11 files changed

+179
-129
lines changed

11 files changed

+179
-129
lines changed

.travis.yml

+20-90
Original file line numberDiff line numberDiff line change
@@ -197,23 +197,10 @@ matrix:
197197
. src/ci/docker/x86_64-gnu-tools/repo.sh;
198198
commit_toolstate_change "$MESSAGE_FILE" "$TRAVIS_BUILD_DIR/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" "$(git log --format=%s -n1 HEAD)" "$MESSAGE_FILE" "$TOOLSTATE_REPO_ACCESS_TOKEN";
199199

200-
env:
201-
global:
202-
- SCCACHE_BUCKET=rust-lang-ci-sccache2
203-
- SCCACHE_REGION=us-west-1
204-
- AWS_ACCESS_KEY_ID=AKIAJAMV3QAMMA6AXHFQ
205-
# AWS_SECRET_ACCESS_KEY=...
206-
- secure: "j96XxTVOSUf4s4r4htIxn/fvIa5DWbMgLqWl7r8z2QfgUwscmkMXAwXuFNc7s7bGTpV/+CgDiMFFM6BAFLGKutytIF6oA02s9b+usQYnM0th7YQ2AIgm9GtMTJCJp4AoyfFmh8F2faUICBZlfVLUJ34udHEe35vOklix+0k4WDo="
207-
# TOOLSTATE_REPO_ACCESS_TOKEN=...
208-
- secure: "ESfcXqv4N2VMhqi2iIyw6da9VrsA78I4iR1asouCaq4hzTTrkB4WNRrfURy6xg72gQ4nMhtRJbB0/2jmc9Cu1+g2CzXtyiL223aJ5CKrXdcvbitopQSDfp07dMWm+UED+hNFEanpErKAeU/6FM3A+J+60PMk8MCF1h9tqNRISJw="
209-
210200
before_install:
211-
# We'll use the AWS cli to download/upload cached docker layers, so install
212-
# that here.
213-
- if [ "$TRAVIS_OS_NAME" = linux ]; then
214-
pip install --user awscli;
215-
export PATH=$PATH:$HOME/.local/bin;
216-
fi
201+
# We'll use the AWS cli to download/upload cached docker layers as well as
202+
# push our deployments, so download that here.
203+
- pip install --user awscli; export PATH=$PATH:$HOME/.local/bin
217204
- mkdir -p $HOME/rustsrc
218205
# FIXME(#46924): these two commands are required to enable IPv6,
219206
# they shouldn't exist, please revert once more official solutions appeared.
@@ -276,6 +263,23 @@ after_success:
276263
echo "#### Build successful; Disk usage after running script:";
277264
df -h;
278265
du . | sort -nr | head -n100
266+
- >
267+
if [ "$DEPLOY$DEPLOY_ALT" == "1" ]; then
268+
mkdir -p deploy/$TRAVIS_COMMIT;
269+
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
270+
rm -rf build/dist/doc &&
271+
cp -r build/dist/* deploy/$TRAVIS_COMMIT;
272+
else
273+
rm -rf obj/build/dist/doc &&
274+
cp -r obj/build/dist/* deploy/$TRAVIS_COMMIT;
275+
fi;
276+
ls -la deploy/$TRAVIS_COMMIT;
277+
deploy_dir=rustc-builds;
278+
if [ "$DEPLOY_ALT" == "1" ]; then
279+
deploy_dir=rustc-builds-alt;
280+
fi;
281+
travis_retry aws s3 cp --no-progress --recursive --acl public-read ./deploy s3://rust-lang-ci2/$deploy_dir
282+
fi
279283
280284
after_failure:
281285
- >
@@ -322,77 +326,3 @@ after_failure:
322326

323327
notifications:
324328
email: false
325-
326-
before_deploy:
327-
- mkdir -p deploy/$TRAVIS_COMMIT
328-
- >
329-
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
330-
rm -rf build/dist/doc &&
331-
cp -r build/dist/* deploy/$TRAVIS_COMMIT;
332-
else
333-
rm -rf obj/build/dist/doc &&
334-
cp -r obj/build/dist/* deploy/$TRAVIS_COMMIT;
335-
fi
336-
- ls -la deploy/$TRAVIS_COMMIT
337-
338-
deploy:
339-
- provider: s3
340-
bucket: rust-lang-ci2
341-
skip_cleanup: true
342-
local_dir: deploy
343-
upload_dir: rustc-builds
344-
acl: public_read
345-
region: us-west-1
346-
access_key_id: AKIAJVBODR3IA4O72THQ
347-
secret_access_key:
348-
secure: "kUGd3t7JcVWFESgIlzvsM8viZgCA9Encs3creW0xLJaLSeI1iVjlJK4h/2/nO6y224AFrh/GUfsNr4/4AlxPuYb8OU5oC5Lv+Ff2JiRDYtuNpyQSKAQp+bRYytWMtrmhja91h118Mbm90cUfcLPwkdiINgJNTXhPKg5Cqu3VYn0="
349-
on:
350-
branch: auto
351-
condition: $DEPLOY = 1
352-
353-
# this is the same as the above deployment provider except that it uploads to
354-
# a slightly different directory and has a different trigger
355-
- provider: s3
356-
bucket: rust-lang-ci2
357-
skip_cleanup: true
358-
local_dir: deploy
359-
upload_dir: rustc-builds-alt
360-
acl: public_read
361-
region: us-west-1
362-
access_key_id: AKIAJVBODR3IA4O72THQ
363-
secret_access_key:
364-
secure: "kUGd3t7JcVWFESgIlzvsM8viZgCA9Encs3creW0xLJaLSeI1iVjlJK4h/2/nO6y224AFrh/GUfsNr4/4AlxPuYb8OU5oC5Lv+Ff2JiRDYtuNpyQSKAQp+bRYytWMtrmhja91h118Mbm90cUfcLPwkdiINgJNTXhPKg5Cqu3VYn0="
365-
on:
366-
branch: auto
367-
condition: $DEPLOY_ALT = 1
368-
369-
# These two providers are the same as the two above, except deploy on the
370-
# try branch. Travis does not appear to provide a way to use "or" in these
371-
# conditions.
372-
- provider: s3
373-
bucket: rust-lang-ci2
374-
skip_cleanup: true
375-
local_dir: deploy
376-
upload_dir: rustc-builds
377-
acl: public_read
378-
region: us-west-1
379-
access_key_id: AKIAJVBODR3IA4O72THQ
380-
secret_access_key:
381-
secure: "kUGd3t7JcVWFESgIlzvsM8viZgCA9Encs3creW0xLJaLSeI1iVjlJK4h/2/nO6y224AFrh/GUfsNr4/4AlxPuYb8OU5oC5Lv+Ff2JiRDYtuNpyQSKAQp+bRYytWMtrmhja91h118Mbm90cUfcLPwkdiINgJNTXhPKg5Cqu3VYn0="
382-
on:
383-
branch: try
384-
condition: $DEPLOY = 1
385-
386-
- provider: s3
387-
bucket: rust-lang-ci2
388-
skip_cleanup: true
389-
local_dir: deploy
390-
upload_dir: rustc-builds-alt
391-
acl: public_read
392-
region: us-west-1
393-
access_key_id: AKIAJVBODR3IA4O72THQ
394-
secret_access_key:
395-
secure: "kUGd3t7JcVWFESgIlzvsM8viZgCA9Encs3creW0xLJaLSeI1iVjlJK4h/2/nO6y224AFrh/GUfsNr4/4AlxPuYb8OU5oC5Lv+Ff2JiRDYtuNpyQSKAQp+bRYytWMtrmhja91h118Mbm90cUfcLPwkdiINgJNTXhPKg5Cqu3VYn0="
396-
on:
397-
branch: try
398-
condition: $DEPLOY_ALT = 1

appveyor.yml

+4-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
environment:
2-
SCCACHE_BUCKET: rust-lang-ci-sccache2
3-
SCCACHE_REGION: us-west-1
4-
AWS_ACCESS_KEY_ID: AKIAJAMV3QAMMA6AXHFQ
5-
AWS_SECRET_ACCESS_KEY:
6-
secure: 7Y+JiquYedOAgnUU26uL0DPzrxmTtR+qIwG6rNKSuWDffqU3vVZxbGXim9QpTO80
72
SCCACHE_DIGEST: f808afabb4a4eb1d7112bcb3fa6be03b61e93412890c88e177c667eb37f46353d7ec294e559b16f9f4b5e894f2185fe7670a0df15fd064889ecbd80f0c34166c
8-
TOOLSTATE_REPO_ACCESS_TOKEN:
9-
secure: gKGlVktr7iuqCoYSxHxDE9ltLOKU0nYDEuQxvWbNxUIW7ri5ppn8L06jQzN0GGzN
103

114
# By default schannel checks revocation of certificates unlike some other SSL
125
# backends, but we've historically had problems on CI where a revocation
@@ -235,10 +228,8 @@ before_deploy:
235228
236229
deploy:
237230
- provider: S3
238-
skip_cleanup: true
239-
access_key_id: AKIAJVBODR3IA4O72THQ
240-
secret_access_key:
241-
secure: tQWIE+DJHjXaV4np/3YeETkEmXngtIuIgAO/LYKQaUshGLgN8cBCFGG3cHx5lKLt
231+
access_key_id: $(AWS_ACCESS_KEY_ID)
232+
secret_access_key: $(AWS_SECRET_ACCESS_KEY)
242233
bucket: rust-lang-ci2
243234
set_public: true
244235
region: us-west-1
@@ -252,10 +243,8 @@ deploy:
252243
# This provider is the same as the one above except that it has a slightly
253244
# different upload directory and a slightly different trigger
254245
- provider: S3
255-
skip_cleanup: true
256-
access_key_id: AKIAJVBODR3IA4O72THQ
257-
secret_access_key:
258-
secure: tQWIE+DJHjXaV4np/3YeETkEmXngtIuIgAO/LYKQaUshGLgN8cBCFGG3cHx5lKLt
246+
access_key_id: $(AWS_ACCESS_KEY_ID)
247+
secret_access_key: $(AWS_SECRET_ACCESS_KEY)
259248
bucket: rust-lang-ci2
260249
set_public: true
261250
region: us-west-1

src/librustc_lint/unused.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ use rustc::hir;
2929
declare_lint! {
3030
pub UNUSED_MUST_USE,
3131
Warn,
32-
"unused result of a type flagged as #[must_use]"
32+
"unused result of a type flagged as #[must_use]",
33+
report_in_external_macro: true
3334
}
3435

3536
declare_lint! {

src/librustc_resolve/resolve_imports.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ use syntax_pos::{MultiSpan, Span};
3636

3737
use std::cell::{Cell, RefCell};
3838
use std::collections::BTreeMap;
39-
use std::fmt::Write;
4039
use std::{mem, ptr};
4140

4241
/// Contains data for specific types of import directives.
@@ -778,17 +777,14 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
778777

779778
let msg = format!("`{}` import is ambiguous", name);
780779
let mut err = self.session.struct_span_err(span, &msg);
781-
let mut suggestion_choices = String::new();
780+
let mut suggestion_choices = vec![];
782781
if external_crate.is_some() {
783-
write!(suggestion_choices, "`::{}`", name);
782+
suggestion_choices.push(format!("`::{}`", name));
784783
err.span_label(span,
785784
format!("can refer to external crate `::{}`", name));
786785
}
787786
if let Some(result) = results.module_scope {
788-
if !suggestion_choices.is_empty() {
789-
suggestion_choices.push_str(" or ");
790-
}
791-
write!(suggestion_choices, "`self::{}`", name);
787+
suggestion_choices.push(format!("`self::{}`", name));
792788
if uniform_paths_feature {
793789
err.span_label(result.span,
794790
format!("can refer to `self::{}`", name));
@@ -801,7 +797,7 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
801797
err.span_label(result.span,
802798
format!("shadowed by block-scoped `{}`", name));
803799
}
804-
err.help(&format!("write {} explicitly instead", suggestion_choices));
800+
err.help(&format!("write {} explicitly instead", suggestion_choices.join(" or ")));
805801
if uniform_paths_feature {
806802
err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
807803
} else {

src/libstd/sys/unix/process/process_common.rs

+8
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ impl Command {
141141
pub fn get_argv(&self) -> &Vec<*const c_char> {
142142
&self.argv.0
143143
}
144+
#[cfg(not(target_os = "fuchsia"))]
145+
pub fn get_program(&self) -> &CString {
146+
return &self.program;
147+
}
144148

145149
#[allow(dead_code)]
146150
pub fn get_cwd(&self) -> &Option<CString> {
@@ -244,6 +248,10 @@ impl CStringArray {
244248
pub fn as_ptr(&self) -> *const *const c_char {
245249
self.ptrs.as_ptr()
246250
}
251+
#[cfg(not(target_os = "fuchsia"))]
252+
pub fn get_items(&self) -> &[CString] {
253+
return &self.items;
254+
}
247255
}
248256

249257
fn construct_envp(env: BTreeMap<DefaultEnvKey, OsString>, saw_nul: &mut bool) -> CStringArray {

src/libstd/sys/unix/process/process_unix.rs

+91-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use env;
12+
use ffi::CString;
1113
use io::{self, Error, ErrorKind};
1214
use libc::{self, c_int, gid_t, pid_t, uid_t};
1315
use ptr;
@@ -39,13 +41,15 @@ impl Command {
3941
return Ok((ret, ours))
4042
}
4143

44+
let possible_paths = self.compute_possible_paths(envp.as_ref());
45+
4246
let (input, output) = sys::pipe::anon_pipe()?;
4347

4448
let pid = unsafe {
4549
match cvt(libc::fork())? {
4650
0 => {
4751
drop(input);
48-
let err = self.do_exec(theirs, envp.as_ref());
52+
let err = self.do_exec(theirs, envp.as_ref(), possible_paths);
4953
let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
5054
let bytes = [
5155
(errno >> 24) as u8,
@@ -113,12 +117,48 @@ impl Command {
113117
"nul byte found in provided data")
114118
}
115119

120+
let possible_paths = self.compute_possible_paths(envp.as_ref());
116121
match self.setup_io(default, true) {
117-
Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
122+
Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref(), possible_paths) },
118123
Err(e) => e,
119124
}
120125
}
121126

127+
fn compute_possible_paths(&self, maybe_envp: Option<&CStringArray>) -> Option<Vec<CString>> {
128+
let program = self.get_program().as_bytes();
129+
if program.contains(&b'/') {
130+
return None;
131+
}
132+
// Outside the match so we can borrow it for the lifetime of the function.
133+
let parent_path = env::var("PATH").ok();
134+
let paths = match maybe_envp {
135+
Some(envp) => {
136+
match envp.get_items().iter().find(|var| var.as_bytes().starts_with(b"PATH=")) {
137+
Some(p) => &p.as_bytes()[5..],
138+
None => return None,
139+
}
140+
},
141+
// maybe_envp is None if the process isn't changing the parent's env at all.
142+
None => {
143+
match parent_path.as_ref() {
144+
Some(p) => p.as_bytes(),
145+
None => return None,
146+
}
147+
},
148+
};
149+
150+
let mut possible_paths = vec![];
151+
for path in paths.split(|p| *p == b':') {
152+
let mut binary_path = Vec::with_capacity(program.len() + path.len() + 1);
153+
binary_path.extend_from_slice(path);
154+
binary_path.push(b'/');
155+
binary_path.extend_from_slice(program);
156+
let c_binary_path = CString::new(binary_path).unwrap();
157+
possible_paths.push(c_binary_path);
158+
}
159+
return Some(possible_paths);
160+
}
161+
122162
// And at this point we've reached a special time in the life of the
123163
// child. The child must now be considered hamstrung and unable to
124164
// do anything other than syscalls really. Consider the following
@@ -152,7 +192,8 @@ impl Command {
152192
unsafe fn do_exec(
153193
&mut self,
154194
stdio: ChildPipes,
155-
maybe_envp: Option<&CStringArray>
195+
maybe_envp: Option<&CStringArray>,
196+
maybe_possible_paths: Option<Vec<CString>>,
156197
) -> io::Error {
157198
use sys::{self, cvt_r};
158199

@@ -193,9 +234,6 @@ impl Command {
193234
if let Some(ref cwd) = *self.get_cwd() {
194235
t!(cvt(libc::chdir(cwd.as_ptr())));
195236
}
196-
if let Some(envp) = maybe_envp {
197-
*sys::os::environ() = envp.as_ptr();
198-
}
199237

200238
// emscripten has no signal support.
201239
#[cfg(not(any(target_os = "emscripten")))]
@@ -231,8 +269,53 @@ impl Command {
231269
t!(callback());
232270
}
233271

234-
libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
235-
io::Error::last_os_error()
272+
// If the program isn't an absolute path, and our environment contains a PATH var, then we
273+
// implement the PATH traversal ourselves so that it honors the child's PATH instead of the
274+
// parent's. This mirrors the logic that exists in glibc's execvpe, except using the
275+
// child's env to fetch PATH.
276+
match maybe_possible_paths {
277+
Some(possible_paths) => {
278+
let mut pending_error = None;
279+
for path in possible_paths {
280+
libc::execve(
281+
path.as_ptr(),
282+
self.get_argv().as_ptr(),
283+
maybe_envp.map(|envp| envp.as_ptr()).unwrap_or_else(|| *sys::os::environ())
284+
);
285+
let err = io::Error::last_os_error();
286+
match err.kind() {
287+
io::ErrorKind::PermissionDenied => {
288+
// If we saw a PermissionDenied, and none of the other entries in
289+
// $PATH are successful, then we'll return the first EACCESS we see.
290+
if pending_error.is_none() {
291+
pending_error = Some(err);
292+
}
293+
},
294+
// Errors which indicate we failed to find a file are ignored and we try
295+
// the next entry in the path.
296+
io::ErrorKind::NotFound | io::ErrorKind::TimedOut => {
297+
continue
298+
},
299+
// Any other error means we found a file and couldn't execute it.
300+
_ => {
301+
return err;
302+
}
303+
}
304+
}
305+
if let Some(err) = pending_error {
306+
return err;
307+
}
308+
return io::Error::from_raw_os_error(libc::ENOENT);
309+
},
310+
_ => {
311+
libc::execve(
312+
self.get_argv()[0],
313+
self.get_argv().as_ptr(),
314+
maybe_envp.map(|envp| envp.as_ptr()).unwrap_or_else(|| *sys::os::environ())
315+
);
316+
return io::Error::last_os_error()
317+
}
318+
}
236319
}
237320

238321
#[cfg(not(any(target_os = "macos", target_os = "freebsd",

0 commit comments

Comments
 (0)