Skip to content

Commit 35f8860

Browse files
authored
Fix test_subprocess and locale (#6416)
* Fix test_subprocess * fix windows locale
2 parents 75dcf80 + 752c0f6 commit 35f8860

4 files changed

Lines changed: 55 additions & 2 deletions

File tree

Lib/test/test_locale.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,6 @@ def test_getsetlocale_issue1813(self):
511511
self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
512512
self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
513513

514-
@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; Error not raised')
515514
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
516515
def test_setlocale_long_encoding(self):
517516
with self.assertRaises(locale.Error):

Lib/test/test_subprocess.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,6 @@ def test_one_environment_variable(self):
857857
self.assertEqual(p.returncode, 0)
858858
self.assertEqual(stdout.strip(), b"fruit=orange")
859859

860-
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON, null byte is not checked")
861860
def test_invalid_cmd(self):
862861
# null character in the command name
863862
cmd = sys.executable + '\0'

crates/stdlib/src/locale.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,34 @@ mod _locale {
198198
locale: OptionalArg<Option<PyStrRef>>,
199199
}
200200

201+
/// Maximum code page encoding name length on Windows
202+
#[cfg(windows)]
203+
const MAX_CP_LEN: usize = 15;
204+
205+
/// Check if the encoding part of a locale string is too long (Windows only)
206+
#[cfg(windows)]
207+
fn check_locale_name(locale: &str) -> bool {
208+
if let Some(dot_pos) = locale.find('.') {
209+
let encoding_part = &locale[dot_pos + 1..];
210+
// Find the end of encoding (could be followed by '@' modifier)
211+
let encoding_len = encoding_part.find('@').unwrap_or(encoding_part.len());
212+
encoding_len <= MAX_CP_LEN
213+
} else {
214+
true
215+
}
216+
}
217+
218+
/// Check locale names for LC_ALL (handles semicolon-separated locales)
219+
#[cfg(windows)]
220+
fn check_locale_name_all(locale: &str) -> bool {
221+
for part in locale.split(';') {
222+
if !check_locale_name(part) {
223+
return false;
224+
}
225+
}
226+
true
227+
}
228+
201229
#[pyfunction]
202230
fn setlocale(args: LocaleArgs, vm: &VirtualMachine) -> PyResult {
203231
let error = error(vm);
@@ -208,6 +236,21 @@ mod _locale {
208236
let result = match args.locale.flatten() {
209237
None => libc::setlocale(args.category, ptr::null()),
210238
Some(locale) => {
239+
// On Windows, validate encoding name length
240+
#[cfg(windows)]
241+
{
242+
let valid = if args.category == LC_ALL {
243+
check_locale_name_all(locale.as_str())
244+
} else {
245+
check_locale_name(locale.as_str())
246+
};
247+
if !valid {
248+
return Err(vm.new_exception_msg(
249+
error,
250+
String::from("unsupported locale setting"),
251+
));
252+
}
253+
}
211254
let c_locale: CString =
212255
CString::new(locale.as_str()).map_err(|e| e.to_pyexception(vm))?;
213256
libc::setlocale(args.category, c_locale.as_ptr())

crates/vm/src/stdlib/winapi.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,18 @@ mod _winapi {
248248
Ok(ws.into_vec_with_nul())
249249
};
250250

251+
// Validate no embedded null bytes in command name and command line
252+
if let Some(ref name) = args.name
253+
&& name.as_str().contains('\0')
254+
{
255+
return Err(crate::exceptions::cstring_error(vm));
256+
}
257+
if let Some(ref cmd) = args.command_line
258+
&& cmd.as_str().contains('\0')
259+
{
260+
return Err(crate::exceptions::cstring_error(vm));
261+
}
262+
251263
let app_name = args.name.map(wstr).transpose()?;
252264
let app_name = app_name.as_ref().map_or_else(null, |w| w.as_ptr());
253265

0 commit comments

Comments
 (0)