-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Better registry support #148
Comments
@Hawk777 you're welcome to join the discussion. |
My first implementation had a lot of simplifications, I'm gonna upgrade that.
Yeah, seems the same case we had at #145.
Do you have any suggestion, or maybe a correct C++ implementation? |
Generally the registry has no notion of types, a type is just a dword, and the data is just a collection of bytes. You need to verify everything. So for example, you verify stuff for SZ type: winsafe/src/advapi/handles/hkey.rs Line 1067 in a5d193c
but not for other types such as EXPAND_SZ and MULTI_SZ. Correct (I think) C++ implementation example: |
Could you be more specific about which funcions I should look at in this header? |
It's a large file indeed, and it also makes use of registry_helpers.h, but I'll try to focus more on what I meant.
Your REG_SZ handling looks fine, but you make no validation for REG_EXPAND_SZ at all. I'd expect both to have the same validation. The only difference between the two is that for a REG_EXPAND_SZ value, it's expected to also expand env vars. I think they just depend on GetRegValue to do the proper null-termination for SZ and EXPAND_SZ. Relevant core implementation:
The impl: They properly pass the start and the end of the buffer, and don't rely on two null terminators to be present because it's not guaranteed. |
I don’t have any detailed comments; it’s been quite a while since I’ve worked on my Windows project, and I didn’t dig into things quite to this depth even then. Overall, it probably makes sense for Winsafe to take one of two philosophies. Either (1) validate everything fully and return something immediately useful to the user (but with the risk that some registry values that could exist might actually be completely unreadable by Winsafe due to failing the validation), or (2) validate nothing and hand the user a bag of bytes to do with as they please (which would be less convenient but mean that every possible registry value, even the sketchy ones, are readable, and Winsafe would still be taking care of some amount of abstraction by eliminating the handling of raw pointers and sizing of buffers). I’m not sure which philosophy is the right one though! |
I believe the answer is (1) (as it is done now pretty OK, sans some points
that are the topic of this issue), getting sketchy values is rarely
useful unless you do security or some esoteric tool, in which case just use
the raw APIs. What can be done internally is getting the raw bytes, and
then doing the rest of the operations in safe code. Then, for example, the
parse_multi_z_str unsafety problem wouldn't be possible because you can't
go out of bounds in safe code.
…On Tue, Jan 28, 2025 at 7:00 PM Christopher Head ***@***.***> wrote:
I don’t have any detailed comments; it’s been quite a while since I’ve
worked on my Windows project, and I didn’t dig into things quite to this
depth even then.
Overall, it probably makes sense for Winsafe to take one of two
philosophies. Either (1) validate everything fully and return something
immediately useful to the user (but with the risk that some registry values
that *could exist* might actually be completely unreadable by Winsafe due
to failing the validation), or (2) validate nothing and hand the user a bag
of bytes to do with as they please (which would be less convenient but mean
that every possible registry value, even the sketchy ones, are readable,
and Winsafe would still be taking care of some amount of abstraction by
eliminating the handling of raw pointers and sizing of buffers).
I’m not sure *which* philosophy is the right one though!
—
Reply to this email directly, view it on GitHub
<#148 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABMDRPDIHX5YBDA2FE5LFDT2M6ZRFAVCNFSM6AAAAABUYOOO22VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMJZGU3DSNRSHE>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
That's fine when the boundaries are known, but there are cases like |
You're right, in this cases you have no choice but to trust the buffer. But in the case of the registry, it's incorrect to trust the buffer because it can be anything. That's what happens when you deal with API designed decades ago with various historical accidental design decisions and little awareness to safety. Just like the These inconsistencies are insane part... |
Yeah, that's nightmare fuel. Well, we do what we can do. I believe adding an |
@justanotheranonymoususer could you please take a look at this new RegGetValue implementation? |
Thank you, I left a few comments on the commits
…On Thu, Feb 6, 2025, 00:14 Rodrigo ***@***.***> wrote:
@justanotheranonymoususer <https://github.com/justanotheranonymoususer>
could you please take a look at this new RegGetValue
<https://rodrigocfd.github.io/winsafe/src/winsafe/advapi/handles/hkey.rs.html#386-447>
implementation?
—
Reply to this email directly, view it on GitHub
<#148 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABMDRPCWC5MIQ33L7MA5VED2OKELDAVCNFSM6AAAAABUYOOO22VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMZYGE2TSNJUGA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Correct.
Indeed, no need to check if the data type has changed. As long as the buffer is enough, it's fine. That also means I need to retrieve the type just once, in the 2nd call.
It's a value returned from the OS, I suppose it will always return a valid value. Did you ever seen the OS returning an invalid type?
I changed the logic a bit. If the buffer is not enough the function will return Please take a look again. |
Regedit is unstructured data. It's not supposed to happen "normally", but you probably don't want to panic if it does.
I mean, let's say first call says you have 10 bytes. You alloc 10 bytes. Second query says you actually have 4 bytes. Now you have a vector: But since you don't use But also see my comments on the commit below, you just assume that the size is correct, using unsafe What's basically missing is |
Oh I just saw you commented on another file:
I assume the OS won't return a wrong size for numeric values, that would mean a bug in the OS, right?
Yup. |
So, if the buffer is smaller than the first value, I should shrink it and make the call again, until the value is exact? |
No, just try it. The registry is silly, prehistoric storage of data. When you write data, it basically has the following:
Just like when you enumerate files in a folder, you can't trust a The fact that the type has a meaning of a type is just a convention. The only thing Microsoft did, probably because there were too many bugs with this confusing API, is to create GetRegValueW, a newer API which helps a tiny bit by appending null terminators to string types if they're missing. That's it. The rest is still the same - a value is a combination of a 32-bit number and an arbitrary blob of bytes. The rest can be anything.
You don't need to make the call again, you can just resize the vector to make it smaller. Even better, you can just make a slice of the x first bytes. |
@justanotheranonymoususer, as for the DWORD (and QWORD, by analogy) situation, I see 3 possibilities for the returned data length:
What do you think? |
I'd return an error if the size doesn't match, it shouldn't happen, and if
it does it means that something is wrong, similarly to trying to read a
string and finding DWORD.
BTW looks like Microsoft is also working on a safe registry wrapper in
rust, you might want to take a look:
https://github.com/microsoft/windows-rs/blob/master/crates/libs/registry/readme.md
…On Tue, Feb 11, 2025 at 2:47 PM Rodrigo ***@***.***> wrote:
@justanotheranonymoususer <https://github.com/justanotheranonymoususer>,
as for the DWORD (and QWORD, by analogy) situation, I see 3 possibilities
for the returned data length:
- exactly 4 bytes – happy path;
- more than 4 bytes – just consider the first 4 bytes;
- less than 4 bytes – maybe fill with 0x00?
What do you think?
—
Reply to this email directly, view it on GitHub
<#148 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABMDRPA4TL7INH74AA74OGL2PHWNJAVCNFSM6AAAAABUYOOO22VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMNJQG4ZDEMJUHE>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
I'm okay with that.
I've seen other high-level community crates for specific Windows areas, like registry for the Registry itself. If MS is now creating a high-level Registry wrapper, I'd say it's more likely that they'll look at what we are doing than the other way around. I will also take a look at |
Interesting, I wasn't familiar with this API, and frankly never needed something like this.
I tend to agree, although I don't think it matters all that much, |
I skimmed over the registry functions and I have several suggestions:
RegGetValue
receives no options at all, but:RRF_SUBKEY_WOW6432KEY
/RRF_SUBKEY_WOW6464KEY
are necessary sometimes (with this function and RegOpen*/RegCreate* too)RRF_NOEXPAND
is always used, why?RRF_RT_REG_DWORD
for performance and ease of useif data_len1 == data_len2 + 2 { // https://stackoverflow.com/q/29223180
validate_retrieved_reg_val
)parse_multi_z_str
is unsafe because it's not bounded by the size but the buffer can be without the double null terminatorThe text was updated successfully, but these errors were encountered: