Ahhh, you have to love the way user IDs are handled in Linux. Back in the old UNIX days a process just had one UID. Then it had separate effective and real UIDs. Then sometime while I wasn’t looking it grew a saved-set-uid, probably as an ugly solution to a simple problem with real and effective UIDs that I remember finding at Encore back in 1990. Then Linux came along and added fsuid so that file servers could take on a user’s identity for filesystem operations. Not a bad idea, really, but it sure would have been nice if the people who added setfsuid and setfsgid had thought through the problem enough to add setfsgroups as well . . . but that’s a different discussion. The confusing thing about setfsuid is that it’s not clear whether it has its effect at process or thread level. I’ve seen plenty of claims both ways, but a slight majority seem to think it’s process level. In other words, if you have two threads that are handling requests for two different users, you’d have to serialize their use of setfsuid to make sure each one operated under the correct (assumed) identity. That seemed really broken to me, but it was the most common interpretation, so instead of trusting random sources on the web I wrote a test program. This creates a file with restricted permissions, then has one child thread do setfsuid to a bogus identity, then has a second child thread try to open the file. Here’s the result:
parent waking first child kid1 (27054) setting fsuid to 9876 kid1 (27054) creating .childfile file created OK parent waking second child kid2 (27055) trying to open .parentfile file opened, setfsuid must be per-thread test complete
That seems pretty conclusive. If setfsuid had its effect at process level, the open would have failed. The test program also has the first thread create a new file, which does show up with the bogus owner, so clearly the setfsuid call had the right effect on that thread even though it didn’t affect the sibling thread. This is good. This is what I want, and probably what every other developer would want, so why would anyone have thought setfsuid worked at process level? I’d guess it’s partly because many people in the Linux community have worked very hard to obfuscate the relationships between forked processes, cloned processes, and threads as much as possible. I did actually check the kernel code, and the fsuid is stored as part of the “task_struct” which most people would intuitively associate with a process . . . but with cloning it’s actually closer to a thread. It’s also possible that there were bugs at one time that caused setfsuid to affect the whole process even though it was never supposed to. In any case, the actual answer seems to be the right one. Hopefully, anybody else who wonders can come here and use the test program to check for themselves.