microsoft / windows-rs

Rust for Windows
https://kennykerr.ca/rust-getting-started/
Apache License 2.0
10.1k stars 473 forks source link

Should `windows_registry::Key::(get|set)_string` use `OsStr` instead of `str`? #3119

Closed rami3l closed 1 week ago

rami3l commented 2 weeks ago

Hi, and thanks for making this project!

Background

Coming from https://github.com/rust-lang/rustup/pull/3896, we (the Rustup team) are currently evaluating the possibility of replacing winreg with windows-registry in Rustup. This is mainly because we use the registry APIs to inspect and change the PATH variable, which is a necessary step in Rustup's installation process.

However, the following function signatures look a bit concerning:

https://github.com/microsoft/windows-rs/blob/2ec4e06d366c46ba9634316959f680ea3dc42a5a/crates/libs/registry/src/key.rs#L245 https://github.com/microsoft/windows-rs/blob/2ec4e06d366c46ba9634316959f680ea3dc42a5a/crates/libs/registry/src/key.rs#L95

... which is because being valid UTF8 is a core invariant of the str type. OTOH, it seems to me that there's no guarantee that we should always get/set valid UTF16LE from/to the registry, and we'd like to make sure that Rustup is still working correctly even if there are non-Unicode bytes in the registry value in question (which could be added by a third-party app?).

For example, we now have the following smoke test that inserts into the registry a sequence of u16 that would be considered malformed UTF16LE, and it's expected to work:

                // Set up a non unicode PATH
                let reg_value = RegValue {
                    bytes: vec![
                        0x00, 0xD8, // leading surrogate
                        0x01, 0x01, // bogus trailing surrogate
                        0x00, 0x00, // null
                    ],
                    vtype: RegType::REG_EXPAND_SZ,
                };
                RegKey::predef(HKEY_CURRENT_USER)
                    .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
                    .unwrap()
                    .set_raw_value("PATH", &reg_value)
                    .unwrap();

_https://github.com/rust-lang/rustup/blob/4c3ff9ce638e8402fbc2e8755d9e8a2cd148f6be/tests/suite/cli_paths.rs#L427-L440_

Suggestion

For the exact reason explained above, I believe OsStr/OsString is a better alternative to str/String in APIs like this.

Quoting https://doc.rust-lang.org/std/ffi/struct.OsString.html:

The need for this type arises from the fact that:

  • On Windows, strings are often arbitrary sequences of non-zero 16-bit values, interpreted as UTF-16 when it is valid to do so.

Once that change is made, we would be able to use windows::ffi::OsStringExt::from_wide to construct an OsString from any &[u16].

I'm still not very familiar with Windows development myself, so please feel free to correct me if I'm wrong about anything. Many thanks in advance!

kennykerr commented 2 weeks ago

How about this: #3120

It allows you to gracefully handle registry values even when their byte values are not necessarily valid or corresponding to their purported type. You can thus query a "string" value as bytes and interpret it however you wish.

Let me know what you think.

rami3l commented 2 weeks ago

How about this: #3120

It allows you to gracefully handle registry values even when their byte values are not necessarily valid or corresponding to their purported type. You can thus query a "string" value as bytes and interpret it however you wish.

Let me know what you think.

@kennykerr Thanks for the quick response! After a quick look it seems that this only solves half the problem though: since we're modifying the PATH, we have to find a way to put the value back after appending to it...

I still personally prefer the OsStr approach, as it's quite clear that:

kennykerr commented 2 weeks ago

How about if we added get_os_string and set_os_string? That way folks that need no_std support or just prefer str can still use windows-registry and folks that prefer/need OsStr can opt for that as well.

rami3l commented 2 weeks ago

How about if we added get_os_string and set_os_string? That way folks that need no_std support or just prefer str can still use windows-registry and folks that prefer/need OsStr can opt for that as well.

@kennykerr Sounds good!

kennykerr commented 1 week ago

3121 adds OsString support.