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

ABI break: z_stream::zalloc and z_stream::zfree should be able to set to None (null) #55

Open
crlf0710 opened this issue Sep 11, 2019 · 9 comments

Comments

@crlf0710
Copy link
Member

No description provided.

@crlf0710
Copy link
Member Author

crlf0710 commented Nov 9, 2019

rust-lang/rust#66059 is gonna turn some code into panic, i think.

@RalfJung
Copy link
Member

Oh so these are function pointers (fn() types)? Indeed instantiating those with mem::zeroed() or mem::uninitialized() is immediate UB. If you want a nullable function pointer, use Option<fn()>; that one you can zero-initialize.

@crlf0710
Copy link
Member Author

@RalfJung Yes, and that will be a breaking change for this crate.

@joshtriplett
Copy link
Member

Unfortunately, this would indeed be a breaking ABI change in libz-sys, and that would be a major disruption to the ecosystem. I'm going to leave this issue open, to document the problem, but I don't think it's going to be fixable.

@joshtriplett joshtriplett changed the title z_stream::zalloc and z_stream::zfree should be able to set to None(null pointer) ABI break: z_stream::zalloc and z_stream::zfree should be able to set to None (null) Aug 17, 2020
@RalfJung
Copy link
Member

RalfJung commented Aug 17, 2020

This is a UB bug, and as our UB diagnostics get better this might become a panic. Specifically, lines like this will panic.

@joshtriplett
Copy link
Member

@RalfJung The UB isn't in this crate, though. It's in any crate that creates a zeroed z_stream.

I would love to fix this, but I don't see how that could be possible without breaking ABI.

@RalfJung
Copy link
Member

@joshtriplett

It's in any crate that creates a zeroed z_stream.

Fair. This crate makes it impossible to avoid the UB though.

I don't see how that could be possible without breaking ABI.

Not sure how feasible that is, but there could be a new z_stream2 struct with the fixed types, and corresponding versions of z_stream-taking functions? Many of those seem to be by-pointer anyway, and z_streamp works for both z_stream and z_stream2 as it is just a raw pointer.

@joshtriplett
Copy link
Member

@RalfJung

Fair. This crate makes it impossible to avoid the UB though.

You can avoid the UB by setting the pointers to a valid value. You just can't set the pointers to NULL and rely on the defaults.

Not sure how feasible that is, but there could be a new z_stream2 struct with the fixed types, and corresponding versions of z_stream-taking functions? Many of those seem to be by-pointer anyway, and z_streamp works for both z_stream and z_stream2 as it is just a raw pointer.

Interesting. Would it be UB to transmute a raw pointer to a z_stream2 (or some_module::z_stream) to a raw pointer to z_stream? Or is that OK as long as nothing dereferences that pointer and that pointer is passed directly to C?

@RalfJung
Copy link
Member

RalfJung commented Aug 18, 2020

You can avoid the UB by setting the pointers to a valid value. You just can't set the pointers to NULL and rely on the defaults.

Right. So you cannot use the entire zlib API (the defaults) and you cannot use mem::zeroed to prepare the zlib config struct -- not sure if that was ever an intended use-case, and not sure how often it happens, I just know there is at least one crate to do this.

Interesting. Would it be UB to transmute a raw pointer to a z_stream2 (or some_module::z_stream) to a raw pointer to z_stream? Or is that OK as long as nothing dereferences that pointer and that pointer is passed directly to C?

Raw pointers may point to anything, including nothing at all. They do not even have to be dereferencable, or aligned, or anything like that. That's their key reason to exist in my view -- to opt out of all the guarantees that come with references.

You don't even need to transmute for that, you can just cast the raw pointer in safe code. And as you say that is okay as long as the NULL fn ptr is not loaded on the Rust side.

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

No branches or pull requests

3 participants