Skip to content

Commit f914e8a

Browse files
authored
Merge pull request #739 from thomcc/hkey-paranoia
Be more defensive about how windows RegistryKeys are handled
2 parents 2d5cc70 + 544fb0f commit f914e8a

File tree

1 file changed

+38
-11
lines changed

1 file changed

+38
-11
lines changed

src/registry.rs

+38-11
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use std::ops::RangeFrom;
1414
use std::os::raw;
1515
use std::os::windows::prelude::*;
1616

17-
pub struct RegistryKey(Repr);
17+
/// Must never be `HKEY_PERFORMANCE_DATA`.
18+
pub(crate) struct RegistryKey(Repr);
1819

1920
type HKEY = *mut u8;
2021
type DWORD = u32;
@@ -29,7 +30,8 @@ type REGSAM = u32;
2930

3031
const ERROR_SUCCESS: DWORD = 0;
3132
const ERROR_NO_MORE_ITEMS: DWORD = 259;
32-
const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY;
33+
// Sign-extend into 64 bits if needed.
34+
const HKEY_LOCAL_MACHINE: HKEY = 0x80000002u32 as i32 as isize as HKEY;
3335
const REG_SZ: DWORD = 1;
3436
const KEY_READ: DWORD = 0x20019;
3537
const KEY_WOW64_32KEY: DWORD = 0x200;
@@ -66,8 +68,11 @@ extern "system" {
6668

6769
struct OwnedKey(HKEY);
6870

71+
/// Note: must not encode `HKEY_PERFORMANCE_DATA` or one of its subkeys.
6972
enum Repr {
70-
Const(HKEY),
73+
/// `HKEY_LOCAL_MACHINE`.
74+
LocalMachine,
75+
/// A subkey of `HKEY_LOCAL_MACHINE`.
7176
Owned(OwnedKey),
7277
}
7378

@@ -79,16 +84,17 @@ pub struct Iter<'a> {
7984
unsafe impl Sync for Repr {}
8085
unsafe impl Send for Repr {}
8186

82-
pub static LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::Const(HKEY_LOCAL_MACHINE));
87+
pub(crate) const LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::LocalMachine);
8388

8489
impl RegistryKey {
8590
fn raw(&self) -> HKEY {
8691
match self.0 {
87-
Repr::Const(val) => val,
92+
Repr::LocalMachine => HKEY_LOCAL_MACHINE,
8893
Repr::Owned(ref val) => val.0,
8994
}
9095
}
9196

97+
/// Open a sub-key of `self`.
9298
pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
9399
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
94100
let mut ret = 0 as *mut _;
@@ -140,9 +146,13 @@ impl RegistryKey {
140146
}
141147

142148
// The length here is the length in bytes, but we're using wide
143-
// characters so we need to be sure to halve it for the capacity
149+
// characters so we need to be sure to halve it for the length
144150
// passed in.
145-
let mut v = Vec::with_capacity(len as usize / 2);
151+
assert!(len % 2 == 0, "impossible wide string size: {} bytes", len);
152+
let vlen = len as usize / 2;
153+
// Defensively initialized, see comment about
154+
// `HKEY_PERFORMANCE_DATA` below.
155+
let mut v = vec![0u16; vlen];
146156
let err = RegQueryValueExW(
147157
self.raw(),
148158
name.as_ptr(),
@@ -151,17 +161,34 @@ impl RegistryKey {
151161
v.as_mut_ptr() as *mut _,
152162
&mut len,
153163
);
164+
// We don't check for `ERROR_MORE_DATA` (which would if the value
165+
// grew between the first and second call to `RegQueryValueExW`),
166+
// both because it's extremely unlikely, and this is a bit more
167+
// defensive more defensive against weird types of registry keys.
154168
if err != ERROR_SUCCESS as LONG {
155169
return Err(io::Error::from_raw_os_error(err as i32));
156170
}
157-
v.set_len(len as usize / 2);
158-
171+
// The length is allowed to change, but should still be even, as
172+
// well as smaller.
173+
assert!(len % 2 == 0, "impossible wide string size: {} bytes", len);
174+
// If the length grew but returned a success code, it *probably*
175+
// indicates we're `HKEY_PERFORMANCE_DATA` or a subkey(?). We
176+
// consider this UB, since those keys write "undefined" or
177+
// "unpredictable" values to len, and need to use a completely
178+
// different loop structure. This should be impossible (and enforce
179+
// it in the API to the best of our ability), but to mitigate the
180+
// damage we do some smoke-checks on the len, and ensure `v` has
181+
// been fully initialized (rather than trusting the result of
182+
// `RegQueryValueExW`).
183+
let actual_len = len as usize / 2;
184+
assert!(actual_len <= v.len());
185+
v.truncate(actual_len);
159186
// Some registry keys may have a terminating nul character, but
160187
// we're not interested in that, so chop it off if it's there.
161-
if v[v.len() - 1] == 0 {
188+
if !v.is_empty() && v[v.len() - 1] == 0 {
162189
v.pop();
163190
}
164-
Ok(OsString::from_wide(&v))
191+
return Ok(OsString::from_wide(&v));
165192
}
166193
}
167194
}

0 commit comments

Comments
 (0)