Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve IUnknown Unsafe Annotations #1734

Merged
merged 8 commits into from May 6, 2022
Merged

Conversation

rylev
Copy link
Contributor

@rylev rylev commented May 4, 2022

This PR does two things:

  • Makes the QueryInterface and Release methods on IUknownImpl unsafe.
    • These methods require ensuring certain variants for them to be called safely. For example, Release can free memory so the user might be sure that once they call Release the no longer use self.
  • Makes QueryInterface, AddRef, and Release take non-exclusive references since they are effectively methods on aliased references and thus cannot be &mut self.
    • As a side effect I needed to change QueryInterface's out param to be *mut *const c_void instead of *mut *mut c_void. I believe this more closely models what an interface out param is.

cc @eholk who has expressed interest in helping review safety related PRs

@rylev rylev requested review from eholk and kennykerr May 4, 2022 17:25
@rylev
Copy link
Contributor Author

rylev commented May 5, 2022

I'm just going to check with @eholk before merging.

@eholk
Copy link

eholk commented May 5, 2022

I'm just going to check with @eholk before merging.

Looks good to me!

Oops, I mixed this up with the other PR. I'll take a look :)

Copy link

@eholk eholk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I added a few clarification questions and possible suggestions, but I think it's fine as is.

self.count.add_ref()
}
fn Release(&mut self) -> u32 {
unsafe fn Release(&self) -> u32 {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to make Release take self? If so, I think you could make this safe.

I'm guessing that doesn't fit with lots of COM patterns, since I remember making lots of manual calls to Release back when I've done COM programming before. Still, using this object after calling Release seems suspect, since Release might drop itself, but I guess that's why this is unsafe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Release gets called in the Drop impl of COM objects, and we only have a &mut self in drop so I don't think it's possible for Release to take self. This is unfortunately where COM's semantics and Rust semantics don't 100% align and so we're left with a dangling pointer for a short amount of time. I think the unfortunate answer is that we just have to be careful. Luckily, users will almost never interact with this method and instead Release will get called automatically on drop.

///
/// This function is safe to call as long as the interface pointer is non-null and valid for writes
/// of an interface pointer.
unsafe fn QueryInterface(&self, iid: &GUID, interface: *mut *const core::ffi::c_void) -> HRESULT;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use NonNull to encode the non-null safety requirement here if you wanted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider that, but I'm not sure it's worth it since NonNull is a bit awkward to work with, and the user still has to verify the pointer is valid for writing an interface pointer to. Since NULL is not valid for writes, the safety check should already tip the caller to check for non-null.

@@ -28,7 +28,7 @@ impl IClassFactory_Impl for Factory {
assert!(outer.is_none());
let unknown: IInspectable = Object().into();
// TODO: https://github.com/microsoft/windows-rs/issues/1441
unsafe { unknown.query(&*iid, object).ok() }
unsafe { unknown.query(&*iid, object as *mut _).ok() }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth adding a SAFETY comment here, although I don't know that it'd be terribly informative. It looks like you're just calling an unsafe function, query, and following its requirements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a good idea, but we can take care of that when we document query's safety needs (which I personally don't yet have a good grasp on).

@rylev rylev merged commit 96de869 into microsoft:master May 6, 2022
@rylev rylev deleted the iunknown-safety branch May 6, 2022 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants