-
Notifications
You must be signed in to change notification settings - Fork 667
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
Fix memory unsafety in unistd::getgrouplist #1542
Conversation
src/unistd.rs
Outdated
// Linux stores the number of groups found in ngroups. We can | ||
// use that to resize the buffer exactly. | ||
cfg_if! { | ||
if #[cfg(target_os = "linux")] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about other platforms so this is only for linux. I also skipped checking if ngroups
is within ngroups_max
in this case, to keep the code simple. The assumption is that linux is consistent between sysconf(NGROUPS_MAX)
and its return value here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be true on Solaris: https://docs.oracle.com/cd/E86824_01/html/E54766/getgrouplist-3c.html
and on musl but only since 2015: https://git.musl-libc.org/cgit/musl/commit/?id=2894a44b40e460fc4112988407818439f2e9672d
It might be better to detect this instead of trying to hard-code platforms, maybe something like
- store the old ngroups
- call getgrouplist
- if ngroups != old_ngroups, use that, otherwise use 2 * old_ngroups
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't I revert this portion of the change and save it for another day? I agree that a more principled approach might be better. For now we can focus on just the buffer overrun fix.
Is this good to go? There is an outstanding security advisory about this issue, but I don't want to alert people to a non-actionable advisory (i.e. with no fixed version to upgrade to) if that can be avoided. |
I tried to reproduce this in a test and had some trouble:
|
Ok, I tested manually:
Then, I wrote this test: #[test]
// `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "fuchsia")))]
fn test_getgrouplist() {
let _m = crate::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
let username = User::from_uid(Uid::current()).unwrap().unwrap().name;
let new_groups = getgrouplist(&CString::new(username).unwrap(), Gid::current()).unwrap();
assert_eq!(new_groups.len(), 65);
} I ran the test on master, and on master with this patch applied. Without the patch, I get this:
With the patch, the test passes. I think this is ready, if you can think of a way to bundle that into a unit test that can go in CI, I'm open to suggestions, otherwise I recommend we just merge it. |
Oh, @Shnatsel sorry, I thought you were a nix maintainer, you came here from the security advisory. I think we need a maintainer to weigh in here. |
Thanks for the fix. It looks good. And I agree that it's pretty hard to make an automatic test for this. Can you override /etc/groups by using Linux namespaces? While you mull that over, I'm going to hijack this PR to fix the CHANGELOG and merge. |
Sigh, git confusingly pushed master instead of the branch I had checked out. And now github won't let me update the PR again, probably because the source branch is named master. I reopened this as #1545 . |
@asomers sorry for the git issues. I tried to do this work via the GH webui (didn’t have access to personal computer at the time) and it went a bit sideways. Thanks for reopening and merging under #1545. And yeah, I couldn’t think of a good automated test for this either. Maybe something can be done if we abstract away the While on this subject, it might be prudent to add |
If we're going to mock out libc, then we should just do it at compile time rather than use a trait. Mockall can do that, and I've thought about doing it some time. But for most libc functions the cost/benefit ratio isn't there. |
Possibly you could fake out One other option I thought of was to do everything that |
The trait approach would still dispatch statically (via generics). Unit test would just supply a mock receiver for the call; real code would dispatch to libc based one. I’ve not used Anyway, agree the cost/benefit may not be there. However, perhaps some of the more “hairy” libc functions could benefit from it since one could craft special/infrequent scenarios more easily. |
Mockall can work either by mocking traits, or by replacing structs via |
Fixes #1541