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

Duplicate all public functions with _and_unset_env #13

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

tbu-
Copy link
Contributor

@tbu- tbu- commented Jan 15, 2025

They need to be unsafe because std::env::remove_var is unsafe.

Fixes #9.

@lnicola
Copy link
Owner

lnicola commented Jan 15, 2025

Thanks! I'm not sure I agree that the unset_env should not be set. Let's split the function into sd_notify and sd_notify_and_unset_env (or similar).

@tbu-
Copy link
Contributor Author

tbu- commented Jan 15, 2025

Do you wish me to make a breaking change (to be released in a new major version) or should I simply add more functions and mark the current one as deprecated?

@lnicola
Copy link
Owner

lnicola commented Jan 15, 2025

Yeah, we can go to 0.5.0, it's not such a big change anyway.

@tbu-
Copy link
Contributor Author

tbu- commented Jan 15, 2025

Roughly like this? I'll add the missing documentation and fix the tests if this is an okay API.

Copy link
Contributor

@mbuesch mbuesch 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 :)

All new unsafe functions should get a proper docstring which references the not-unset-env function and describes what unset-env does.

README.md Outdated Show resolved Hide resolved
@@ -156,12 +161,16 @@ pub fn notify(unset_env: bool, state: &[NotifyState]) -> io::Result<()> {
}
}

pub unsafe fn notify_and_unset_env(state: &[NotifyState]) -> io::Result<()> {
let result = notify(state);
unsafe {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think older Rust versions throw a warning "unnecessary unsafe" here and we might want to add a warning suppression if we want to support them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What old rust versions are you worried about? Users who depend on this crate will never see a warning because warnings are not shown for dependencies. This crate seems to run with the latest stable in CI. The Rust compiler does not warn for unnecessary unsafe here since rust-lang/rust#124636, Rust 1.80 (which would only be relevant for developers, not users of this crate).

Copy link
Contributor

Choose a reason for hiding this comment

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

What old rust versions are you worried about?

I guess you gave the answer to yourself?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So you're worried about developers of this crate who use rust versions older than 1.80 seeing warnings?

Copy link
Contributor

Choose a reason for hiding this comment

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

This all boils down to the question which MSRV this project supports.
And if this throws a warning with this MSRV, the warning should be fixed IMO.
Otherwise the build logs are cluttered with useless warnings on the test builds that are necessary before release.

Copy link
Owner

Choose a reason for hiding this comment

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

I don't think it matters. IIRC, when you build a crate, you don't get warnings for its dependencies, so this doesn't affect the users.

As for this crate itself, it's currently missing a rust-version declaration (I'm not opposed to adding one). But warnings on the MSRV shouldn't block changes, it targets stable.

fds: &[BorrowedFd<'_>],
) -> io::Result<()> {
let result = notify_with_fds(state, fds);
unsafe {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

let sock = UnixDatagram::unbound()?;
sock.connect(socket_path)?;
Ok(Some(sock))
fn connected_unix_datagram(path: &OsStr) -> io::Result<UnixDatagram> {
Copy link
Contributor

Choose a reason for hiding this comment

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

What about Path/PathBuf for paths in general? Path/PathBuf implement AsRef<OsStr>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a private function so we only have to support what we pass. Generics increase code bloat, because whole functions get duplicated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Generics increase code bloat, because whole functions get duplicated.

No?

Copy link
Contributor Author

@tbu- tbu- Jan 17, 2025

Choose a reason for hiding this comment

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

From the short answer, I can't exactly tell what you're disagreeing with.

In Rust, the way generics work is that the function will be compiled once for each set of type parameters it is called with. So if we have a function fn conected_unix_datagram<T: AsRef<OsStr>>(path: &T) -> … (which is the same as fn connected_unix_datagram(path: &impl AsRef<OsStr>) -> …) and call it with &OsStr and once with &OsString, it'll end up being in the compiled binary twice. This is what I meant with "code bloat". The Rust standard library mitigates that by having each function that takes an AsRef generic be a thin wrapper around a function without generics, like e.g. in https://github.com/rust-lang/rust/blob/0c2c096e1ac471b0c34629f9820a7cb1e6d4695d/library/std/src/fs.rs#L3008-L3014. Since the function is non-public, we control all the call sites and all the call sites just want to pass a &OsStr, I thought it'd be unnecessary to do the helper function.

Since this is your crate, I can implement this in whatever way you'd like.

  1. Generics with helper function (like std does for public functions).
  2. Generics without helper function (like I interpreted your first comment).
  3. No generics (like it's currently implemented).

Which way would you like?

Copy link
Contributor

Choose a reason for hiding this comment

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

From the short answer, I can't exactly tell what you're disagreeing with.

I disagree that using &Path instead of &OsStr causes bloat or duplication of any kind.

Path is the ideomatic type for paths. That's why I suggested it.

So if we have a function fn conected_unix_datagram<T: AsRef>(path: &T)

I did not suggest that.

Copy link
Owner

Choose a reason for hiding this comment

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

I also misunderstood initially, you're proposing for that function to take &Path. That would work, but it's not super important either way, it's a private helper with one or two callers.

@@ -297,19 +297,21 @@ fn listen_fds_internal(unset_env: bool) -> io::Result<impl ExactSizeIterator<Ite
Ok(listen_fds)
}

pub unsafe fn listen_fds_and_unset_env() -> io::Result<impl ExactSizeIterator<Item = RawFd>> {
let result = listen_fds();
unsafe {
Copy link
Contributor

Choose a reason for hiding this comment

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

... old Rust versions warning. See above.


pub unsafe fn listen_fds_with_names_and_unset_env() -> io::Result<impl ExactSizeIterator<Item = (RawFd, String)>> {
let result = listen_fds_with_names();
unsafe {
Copy link
Contributor

Choose a reason for hiding this comment

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

... old Rust versions warning. See above.

@@ -445,6 +418,15 @@ pub fn watchdog_enabled(unset_env: bool, usec: &mut u64) -> bool {
}
}

pub unsafe fn watchdog_enabled_and_unset_env(usec: &mut u64) -> bool {
let result = watchdog_enabled(usec);
unsafe {
Copy link
Contributor

Choose a reason for hiding this comment

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

... old Rust versions warning. See above.

@tbu- tbu- force-pushed the pr_unset_env_unsafe branch from d9ed7a9 to 33917ab Compare January 16, 2025 10:49
@tbu- tbu- changed the title Note that using the unset_env parameter is unsafe Duplicate all public functions with _and_unset_env Jan 16, 2025
tbu- added 2 commits January 16, 2025 11:55
This does not fix the actual issue but alerts users to this unsoundness
issue. Also changes the examples so that the parameter isn't set to
`true` mindlessly.

CC lnicola#9
They need to be `unsafe` because `std::env::remove_var` is unsafe.

Fixes lnicola#9.
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.

Soundness issue with std::env::remove_var
3 participants