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

Add the Linux prctl syscall #1550

Merged
merged 1 commit into from
Aug 27, 2023
Merged

Add the Linux prctl syscall #1550

merged 1 commit into from
Aug 27, 2023

Conversation

DOhlsson
Copy link
Contributor

@DOhlsson DOhlsson commented Oct 3, 2021

I have added support for the prctl syscall.

However, I am not sure that this is the right way to do it. Another way would be to wrap each possible flag to prctl as it's own function, that way it would be more similar to how the prctl crate does it. Wrapping the different options would be safer, since some options to prctl return data through pointers.

What do you think?

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

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

The first question is: "if the prctl crate is good enough, then why does Nix need these bindings too?"
Secondly, I don't think the proposed API is very good. Those four c_ulong arguments look like they have different meanings for different PrctlOption values, and that's not very Rusty. One alternative would be to specify the option as an enum with a variable number arguments for each value. Another, as you suggested, would be to turn each prctl option into its own function. Not being familiar with prctl myself, I can't say which is best.

@DOhlsson
Copy link
Contributor Author

DOhlsson commented Oct 5, 2021

I forgot to mention the related issue (#601) in my first post.

To answer your first question, I think that it would be nice to have prctl implemented in Nix. There are other crates for inotify and epoll too, but if I can get all three of them in one crate then that is my preferred option.

I agree that my pull request isn't very good. But I am willing to put in the work to bring it up to Nix's standards.

The weird thing about prctl is that some options can return data through the function call and other options return data to the location pointed to by one of the arguments, this behaviour makes it quite unsafe. Hence why I think that making wrapper functions is the best option.

I'll work a bit on this PR and come back with the better solution.

@DOhlsson DOhlsson requested a review from asomers January 6, 2022 16:31
@DOhlsson
Copy link
Contributor Author

DOhlsson commented Jan 6, 2022

What is your opinion on the solution now? I have not implemented all the options yet as I wanted to hear your opinion first.

Possibly the prctl function and PrctlOption enum do not have to be public anymore, if all possibilities are implemented as separate functions.

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

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

The new methods look a lot safer. That's what I had in mind. Also, in the tests, don't forget to clean up after yourself!

test/sys/mod.rs Outdated Show resolved Hide resolved
src/sys/prctl.rs Outdated Show resolved Hide resolved
src/sys/prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
@DOhlsson
Copy link
Contributor Author

Thank you for your feedback! I have a couple more of the options to add (and fix the tests), but I hope to get some time to get it done this week.

What is your opinion on the more exotic options that are available through prctl, should I implement them as well in this PR? Like architecture specific options, that I don't have the hardware to test with (powerpc, ia, mips etc.) and options to control capabilities where there are recommended higher-level libraries like libcap.

I have implemented the process-control options and left out the more exotic options for now.

@DOhlsson
Copy link
Contributor Author

@asomers I can't seem to figure out what is going wrong in the builds. Do you have any idea? Could you point me in the right direction?

The errors are in architectures I don't have access to, namely MIPS, MIPS64, mipsel, arm gnueabi and armv7 gnueabihf. It appears to be pointer related, as the error EFAULT from prctl indicates that it is an invalid address.

I pass the pointer to prctl as a &mut c_int cast as c_ulong so there might be something architecture specific that I do not know.

Do you know of any examples in the rest of the Nix code where a pointer is passed to and written by the syscall?

@asomers
Copy link
Member

asomers commented Apr 11, 2022

uclibceabihf is failing through no fault of your own, and hopefully it will soon be fixed on the master branch. arm gnueabi and armv7 gnueabihf are tested using QEMU. QEMU doesn't implement all syscalls. If prctl is one, then you can add #[cfg_attr(qemu, ignore)] to those test cases.

@DOhlsson DOhlsson marked this pull request as ready for review April 15, 2022 21:58
@DOhlsson DOhlsson requested a review from asomers April 15, 2022 21:59
@DOhlsson
Copy link
Contributor Author

@asomers I have fixed the tests and completed a bunch more options. I think this PR is ready if you are satisfied with it.

@DOhlsson
Copy link
Contributor Author

DOhlsson commented Jul 9, 2022

@asomers What do you think? Is this PR ready or is there anything I should change?

@ColonelThirtyTwo
Copy link

I'm interested in this too, particularly setting no new privileges.

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

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

All of the tests seem to assume that the default settings will be present. Are you sure that will be true, or could it depend on the environment? Also, is it possible for any of them to interfere with other tests, including in other modules? It makes me nervous that they all change global settings.

src/sys/prctl.rs Outdated Show resolved Hide resolved
src/sys/prctl.rs Outdated Show resolved Hide resolved
src/sys/prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Outdated Show resolved Hide resolved
test/sys/test_prctl.rs Show resolved Hide resolved
@DOhlsson
Copy link
Contributor Author

All of the tests seem to assume that the default settings will be present. Are you sure that will be true, or could it depend on the environment?

I have changed the tests so that they do not assume that the defaults are set. And I've also added cleanup that resets the settings to what they were before the test.

Also, is it possible for any of them to interfere with other tests, including in other modules? It makes me nervous that they all change global settings.

Some of these affect the process and not only the thread, so it might be possible that these tests affect other tests.
I'll move the tests into their own test target.

@asomers
Copy link
Member

asomers commented Jul 10, 2022

I see, you did move them into their own process. That looks good. Would you mind squashing your commits now?

@DOhlsson DOhlsson force-pushed the prctl branch 2 times, most recently from 19ad914 to 60acaa1 Compare July 11, 2022 06:41
@DOhlsson
Copy link
Contributor Author

@asomers I have squashed the commits. I also added a line to the changelog.

@DOhlsson
Copy link
Contributor Author

@asomers is it ready to merge or is there anything else I should do?

src/sys/prctl.rs Outdated
Comment on lines 140 to 145
/// Set the name of the calling thread. Strings longer than 15 bytes will be truncated.
pub fn set_name(name: &str) -> Result<()> {
let name = CString::new(name.as_bytes()).unwrap();

let res = unsafe { libc::prctl(libc::PR_SET_NAME, name.as_ptr(), 0, 0, 0) };

Errno::result(res).map(drop)
}

Choose a reason for hiding this comment

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

The str input plus the automatic truncation don't seem like they go well together, as the truncation could cut off part of a character.

I wonder if it would be better to just have the input argument be &[u8]?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@eric-seppanen Changing to &[u8] would not prevent characters to be cut off in the middle? If the user just does an as_bytes() on their string it could still truncate in the middle of a character.

Copy link

@eric-seppanen eric-seppanen Oct 25, 2022

Choose a reason for hiding this comment

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

That's true. My concern was that &str implies that unicode will be handled appropriately, which isn't a promise this function can make.

Maybe &OsStr would be the right choice? It implies that there's a string, but clearly expresses to the caller that the handling of the bytes is os-specific.

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 don't think there is any convenient way to convey that the input string could be truncated. In the end I settled on CStr because it hints at the underlying C code and because it's the type of string least likely to be UTF-8.

src/sys/prctl.rs Outdated
Comment on lines 149 to 158
/// Return the name of the calling thread
pub fn get_name() -> Result<String> {
// Size of buffer determined by linux/sched.h TASK_COMM_LEN
let buf = [0u8; 16];

let res = unsafe { libc::prctl(libc::PR_GET_NAME, &buf, 0, 0, 0) };

let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
let cstr = CStr::from_bytes_with_nul(&buf[..=len]).map_err(|_| Errno::EINVAL)?;
let name = cstr.to_str().map_err(|_| Errno::EINVAL)?;

Errno::result(res).map(|_| name.to_owned())
}

Choose a reason for hiding this comment

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

If set_name were to switch to &[u8] then perhaps this should change the return value from String to Box<[u8]>?

Then the caller could decide how to handle non-UTF-8 data.

Choose a reason for hiding this comment

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

On second thought, Vec<u8> might be better than Box<[u8]>, to make it easier for the caller to do String::from_utf8(name) afterward.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same here as on the previous comment. I settled for CString because then the user will be forced to handle the utf8 conversion themselves.

@DOhlsson
Copy link
Contributor Author

@asomers I have rebased this branch onto latest commit from master. Can this PR be merged as is, or would you like me to change anything else?

Comment on lines +154 to +155
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
let name = CStr::from_bytes_with_nul(&buf[..=len]).map_err(|_| Errno::EINVAL)?;
Copy link
Member

Choose a reason for hiding this comment

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

Ideally you could do this instead. But that would require raising the MSRV to 1.69.0, so probably we shouldn't do it.

Suggested change
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
let name = CStr::from_bytes_with_nul(&buf[..=len]).map_err(|_| Errno::EINVAL)?;
let name = CStr::from_bytes_until_nul(&buf[..]);

@asomers asomers added this pull request to the merge queue Aug 27, 2023
Merged via the queue into nix-rust:master with commit 3a4037c Aug 27, 2023
35 checks passed
@SteveLauC SteveLauC mentioned this pull request Nov 26, 2023
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

4 participants