Description
If libc::getgrouplist
returns -1
, indicating the supplied groups
buffer is too short to hold all the user's groups, the current code will double the buffer and try again. Unfortunately, the ngroups
value it passes to libc::getgrouplist
doesn't reflect the len of the buffer. After the 1st iteration in this scenario, libc
will set ngroups
to the # of groups it found, which can be a larger # than the doubling of the group
's capacity. The 2nd iteration of the loop will now have a mismatch between group
's capacity and the ngroups
value it supplies. This will lead to libc
writing into unallocated memory beyond the buffer's allocation, leading to a segfault, allocator corruption (e.g. free
on destroying the Vec<Gid>
complaining of invalid size passed in), or more generally, UB behavior.
The simplest fix is to set ngroups
to groups.capacity()
on each loop iteration, i.e.:
...
let mut groups = Vec::<Gid>::with_capacity(min(ngroups_max, 8) as usize);
...
loop {
let mut ngroups = groups.capacity() as i32;
let ret = unsafe {
libc::getgrouplist(user.as_ptr(),
gid as getgrouplist_group_t,
groups.as_mut_ptr() as *mut getgrouplist_group_t,
&mut ngroups)
};
...
Separately, since Linux sets ngroups
to be the # of groups found (when returning -1
), it probably makes sense to reserve that amount in groups
rather than doing the doubling capacity dance. But that's a separate issue.
cc @geofft