-
Notifications
You must be signed in to change notification settings - Fork 18k
os/user: non-cgo implementation of LookupGroup and LookupGroupId can fail when large groups are present in /etc/group #43636
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
Comments
for anyone trying at home: the setup can be just (so it doesn't take forever): echo -n 'largegroup:x:1000:' >> /etc/group
for i in $(seq 1 7500); do echo -n user$i, >> /etc/group ; done |
I'll take a stab and see what can be done! |
I idependently starteded to work on this yesterday and missed @tpaschalis comment (my bad for not letting anyone else know!) Either way, I have a patch (applicable against today's master) that fixes the bug and passes all the tests. Here it goes:
@tpaschalis , if you already knee-deep in cooking up a patch for this, by all means please continue and submit your patch (would be happy to be one of reviewers). If you haven't started yet, perhaps I can submit this patch and save you some time. Yours,
|
Hello @andreybokhanko! I hoped I'd find some time to do it yesterday, but you beat me to it. Feel free to submit your solution, and CC me as well if you'd like! |
Change https://golang.org/cl/283601 mentions this issue: |
@tpaschalis, 👍 Sorry for causing confusion... it's funny that out of 771 currently open tickets with "help wanted" badge, we both chose the same one. But sometimes this happens. 😀 I've just submitted a pull request: https://go-review.googlesource.com/c/go/+/283601 |
@ianlancetaylor Could you, please, assign this issue on me? It seems that I can't do so myself. |
@andreybokhanko what do you think about calling the method Buffer to configure a maximum token size of 4 << 20 (4MB) or bigger instead? That seems like a simpler fix to me. One actually wants some limit here, but 64k might be too small these days. What do glibc and musl libc use here? Do they have the same problems? |
Thanks @andreybokhanko for jumping on this so quickly! Just wanted to say that it would also be great if as part of this issue resolution some unit tests were added that cover these kinds of cases with large lines in Appreciate the timely response from the community on this one! |
Hi @nightlyone , that's a viable design alternative, indeed. What I don't like with this approach, though, is that we are essentially replacing one arbitrary limitation with another. Yep, 4MB might look more than enough today, but so was 64K some time ago... :)
They have _SC_GETGR_R_SIZE_MAX constant for maximum size, that they get from OS (https://www.gnu.org/software/libc/manual/html_node/Constants-for-Sysconf.html). This is actually what CGO implementation of lookup functions uses as well. As I understand, we can't use this C library constant in non-CGO implementation. |
Fully agree. I'm new in Go community, so don't really know unit testing requirements here; but an extra unit test never hurts (well, almost never :)) I've just amended my patch with a unit test. |
@andreybokhanko _SC_GETGR_R_SIZE_MAX seems to be only some initial buffer size and not the actual limit as explained in https://tomlee.co/2012/10/problems-with-large-linux-unix-groups-and-getgrgid_r-getgrnam_r/ So looks like there is no actual practical limit and your approach of using an unbounded line buffer is the correct implementation. @ianlancetaylor any opinions from your side? |
I don't see any reason to use an unbounded buffer or to read the whole line into memory. Unless I'm missing something we never care about the members of the group. We can just read the first three fields and then skip to the end of the line without saving the data anywhere. This is admittedly a bigger reworking of this code. |
As @nightlyone confirmed, there is no actual practical limit to the group name -- that's why we have to use an unbounded buffer. As for reading the rest of the line (with members of the group) into memory -- sure, we can skip this. And yes, this would require a much hairier and uglier implementation -- including re-implementing Andrey |
While in principle the group name has unbounded length, in practice the list of group members is where lines get long. It's worth not bothering to save those group members in memory when we don't have to. Yes, it's a bigger change to the current code, but I think it's the right place to end up. |
OK, got it. Will re-implement as you advised. Thanks! Andrey |
Hi @ianlancetaylor , I re-implemented the fix as you advised (well, if I understood you well). Now we read rows in 4096 byte chunks. Please kindly take a look: https://go-review.googlesource.com/c/go/+/283601 Andrey |
This is fixed by I453321f1ab15fd4d0002f97fcec7d0789e1e0da5; should be available in the next Go release. @rturner3 thanks for reporting! @ianlancetaylor thanks for the thorough reivew! Andrey |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
/etc/group
.CGO_ENABLED=0
, callLookupGroup()
orLookupGroupId()
from theos/user
package.The provided example is run in a Docker container in order to not pollute the local host's users and groups. Note that the script executed to create a large group in
/etc/group
may take a few minutes to execute.What did you expect to see?
What did you see instead?
This appears to happen when the length of a line in
/etc/group
exceeds bufio.MaxScanTokenSize bytes (currently 65536 bytes) in length. The implementation in lookup_unix.go does not gracefully handle lines in/etc/group
exceeding 65536 bytes in length in its usage ofbufio.Scanner
.As far as I know, there are no documented limits to the size of a group in Linux, so ideally the Go implementations of
LookupGroup()
andLookupGroupId()
should work regardless of whether the binary is compiled withcgo
or not.One possible solution could be to read in the file using a
bufio.Reader
by callingr.ReadLine()
.The text was updated successfully, but these errors were encountered: