Skip to content

Commit 8f50134

Browse files
committed
exec
1 parent 8eb055b commit 8f50134

File tree

2 files changed

+203
-5
lines changed

2 files changed

+203
-5
lines changed

Lib/test/test_os.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,7 +2204,6 @@ def test_execv_with_bad_arglist(self):
22042204
self.assertRaises(ValueError, os.execv, 'notepad', ('',))
22052205
self.assertRaises(ValueError, os.execv, 'notepad', [''])
22062206

2207-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.execve not implemented yet for all platforms')
22082207
def test_execvpe_with_bad_arglist(self):
22092208
self.assertRaises(ValueError, os.execvpe, 'notepad', [], None)
22102209
self.assertRaises(ValueError, os.execvpe, 'notepad', [], {})
@@ -2264,7 +2263,6 @@ def test_internal_execvpe_str(self):
22642263
if os.name != "nt":
22652264
self._test_internal_execvpe(bytes)
22662265

2267-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.execve not implemented yet for all platforms')
22682266
def test_execve_invalid_env(self):
22692267
args = [sys.executable, '-c', 'pass']
22702268

@@ -2286,7 +2284,6 @@ def test_execve_invalid_env(self):
22862284
with self.assertRaises(ValueError):
22872285
os.execve(args[0], args, newenv)
22882286

2289-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.execve not implemented yet for all platforms')
22902287
@unittest.skipUnless(sys.platform == "win32", "Win32-specific test")
22912288
def test_execve_with_empty_path(self):
22922289
# bpo-32890: Check GetLastError() misuse

crates/vm/src/stdlib/nt.rs

Lines changed: 203 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,143 @@ pub(crate) mod module {
239239
#[cfg(target_env = "msvc")]
240240
unsafe extern "C" {
241241
fn _wexecv(cmdname: *const u16, argv: *const *const u16) -> intptr_t;
242+
fn _wexecve(
243+
cmdname: *const u16,
244+
argv: *const *const u16,
245+
envp: *const *const u16,
246+
) -> intptr_t;
247+
fn _wspawnv(mode: i32, cmdname: *const u16, argv: *const *const u16) -> intptr_t;
248+
fn _wspawnve(
249+
mode: i32,
250+
cmdname: *const u16,
251+
argv: *const *const u16,
252+
envp: *const *const u16,
253+
) -> intptr_t;
254+
}
255+
256+
#[cfg(target_env = "msvc")]
257+
#[pyfunction]
258+
fn spawnv(
259+
mode: i32,
260+
path: OsPath,
261+
argv: Either<PyListRef, PyTupleRef>,
262+
vm: &VirtualMachine,
263+
) -> PyResult<intptr_t> {
264+
use std::iter::once;
265+
266+
let make_widestring =
267+
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
268+
269+
let path = path.to_wide_cstring(vm)?;
270+
271+
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
272+
let arg = PyStrRef::try_from_object(vm, obj)?;
273+
make_widestring(arg.as_str())
274+
})?;
275+
276+
let first = argv
277+
.first()
278+
.ok_or_else(|| vm.new_value_error("spawnv() arg 3 must not be empty"))?;
279+
280+
if first.is_empty() {
281+
return Err(vm.new_value_error("spawnv() arg 3 first element cannot be empty"));
282+
}
283+
284+
let argv_spawn: Vec<*const u16> = argv
285+
.iter()
286+
.map(|v| v.as_ptr())
287+
.chain(once(std::ptr::null()))
288+
.collect();
289+
290+
let result = unsafe { suppress_iph!(_wspawnv(mode, path.as_ptr(), argv_spawn.as_ptr())) };
291+
if result == -1 {
292+
Err(errno_err(vm))
293+
} else {
294+
Ok(result)
295+
}
296+
}
297+
298+
#[cfg(target_env = "msvc")]
299+
#[pyfunction]
300+
fn spawnve(
301+
mode: i32,
302+
path: OsPath,
303+
argv: Either<PyListRef, PyTupleRef>,
304+
env: PyDictRef,
305+
vm: &VirtualMachine,
306+
) -> PyResult<intptr_t> {
307+
use std::iter::once;
308+
309+
let make_widestring =
310+
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
311+
312+
let path = path.to_wide_cstring(vm)?;
313+
314+
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
315+
let arg = PyStrRef::try_from_object(vm, obj)?;
316+
make_widestring(arg.as_str())
317+
})?;
318+
319+
let first = argv
320+
.first()
321+
.ok_or_else(|| vm.new_value_error("spawnve() arg 2 cannot be empty"))?;
322+
323+
if first.is_empty() {
324+
return Err(vm.new_value_error("spawnve() arg 2 first element cannot be empty"));
325+
}
326+
327+
let argv_spawn: Vec<*const u16> = argv
328+
.iter()
329+
.map(|v| v.as_ptr())
330+
.chain(once(std::ptr::null()))
331+
.collect();
332+
333+
// Build environment strings as "KEY=VALUE\0" wide strings
334+
let mut env_strings: Vec<widestring::WideCString> = Vec::new();
335+
for (key, value) in env.into_iter() {
336+
let key = PyStrRef::try_from_object(vm, key)?;
337+
let value = PyStrRef::try_from_object(vm, value)?;
338+
let key_str = key.as_str();
339+
let value_str = value.as_str();
340+
341+
// Validate: no null characters in key or value
342+
if key_str.contains('\0') || value_str.contains('\0') {
343+
return Err(vm.new_value_error("embedded null character"));
344+
}
345+
// Validate: no '=' in key
346+
if key_str.contains('=') {
347+
return Err(vm.new_value_error("illegal environment variable name"));
348+
}
349+
350+
let env_str = format!("{}={}", key_str, value_str);
351+
env_strings.push(make_widestring(&env_str)?);
352+
}
353+
354+
let envp: Vec<*const u16> = env_strings
355+
.iter()
356+
.map(|s| s.as_ptr())
357+
.chain(once(std::ptr::null()))
358+
.collect();
359+
360+
let result = unsafe {
361+
suppress_iph!(_wspawnve(
362+
mode,
363+
path.as_ptr(),
364+
argv_spawn.as_ptr(),
365+
envp.as_ptr()
366+
))
367+
};
368+
if result == -1 {
369+
Err(errno_err(vm))
370+
} else {
371+
Ok(result)
372+
}
242373
}
243374

244375
#[cfg(target_env = "msvc")]
245376
#[pyfunction]
246377
fn execv(
247-
path: PyStrRef,
378+
path: OsPath,
248379
argv: Either<PyListRef, PyTupleRef>,
249380
vm: &VirtualMachine,
250381
) -> PyResult<()> {
@@ -253,7 +384,7 @@ pub(crate) mod module {
253384
let make_widestring =
254385
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
255386

256-
let path = make_widestring(path.as_str())?;
387+
let path = path.to_wide_cstring(vm)?;
257388

258389
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
259390
let arg = PyStrRef::try_from_object(vm, obj)?;
@@ -281,6 +412,76 @@ pub(crate) mod module {
281412
}
282413
}
283414

415+
#[cfg(target_env = "msvc")]
416+
#[pyfunction]
417+
fn execve(
418+
path: OsPath,
419+
argv: Either<PyListRef, PyTupleRef>,
420+
env: PyDictRef,
421+
vm: &VirtualMachine,
422+
) -> PyResult<()> {
423+
use std::iter::once;
424+
425+
let make_widestring =
426+
|s: &str| widestring::WideCString::from_os_str(s).map_err(|err| err.to_pyexception(vm));
427+
428+
let path = path.to_wide_cstring(vm)?;
429+
430+
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
431+
let arg = PyStrRef::try_from_object(vm, obj)?;
432+
make_widestring(arg.as_str())
433+
})?;
434+
435+
let first = argv
436+
.first()
437+
.ok_or_else(|| vm.new_value_error("execve: argv must not be empty"))?;
438+
439+
if first.is_empty() {
440+
return Err(vm.new_value_error("execve: argv first element cannot be empty"));
441+
}
442+
443+
let argv_execve: Vec<*const u16> = argv
444+
.iter()
445+
.map(|v| v.as_ptr())
446+
.chain(once(std::ptr::null()))
447+
.collect();
448+
449+
// Build environment strings as "KEY=VALUE\0" wide strings
450+
let mut env_strings: Vec<widestring::WideCString> = Vec::new();
451+
for (key, value) in env.into_iter() {
452+
let key = PyStrRef::try_from_object(vm, key)?;
453+
let value = PyStrRef::try_from_object(vm, value)?;
454+
let key_str = key.as_str();
455+
let value_str = value.as_str();
456+
457+
// Validate: no null characters in key or value
458+
if key_str.contains('\0') || value_str.contains('\0') {
459+
return Err(vm.new_value_error("embedded null character"));
460+
}
461+
// Validate: no '=' in key
462+
if key_str.contains('=') {
463+
return Err(vm.new_value_error("illegal environment variable name"));
464+
}
465+
466+
let env_str = format!("{}={}", key_str, value_str);
467+
env_strings.push(make_widestring(&env_str)?);
468+
}
469+
470+
let envp: Vec<*const u16> = env_strings
471+
.iter()
472+
.map(|s| s.as_ptr())
473+
.chain(once(std::ptr::null()))
474+
.collect();
475+
476+
if (unsafe { suppress_iph!(_wexecve(path.as_ptr(), argv_execve.as_ptr(), envp.as_ptr())) }
477+
== -1)
478+
{
479+
Err(errno_err(vm))
480+
} else {
481+
Ok(())
482+
}
483+
}
484+
284485
#[pyfunction]
285486
fn _getfinalpathname(path: OsPath, vm: &VirtualMachine) -> PyResult {
286487
let real = path

0 commit comments

Comments
 (0)