Skip to content

Conversation

@jmgao
Copy link

@jmgao jmgao commented Jan 8, 2020

Previously, we were using umask(mask = umask(0)) to fetch the current
umask in order to apply it to the desired mode, but this is broken in
the presence of threads. There is no portable way to directly fetch
umask without mutating it.

Instead, create the file with 0777 permissions, and fstat it to reverse
engineer what umask was when the file was created, and then apply that
as a mask on the mode.

Fixes: jmgao/pore#46

@ethomson
Copy link
Member

ethomson commented Jan 8, 2020

Reading this quickly on my phone, so apologies if I misread - but it looks to me like this opens the new file world-writable, which would give us a different race condition.

Instead, why don't we add a new function -- p_getumask -- which uses getumask(3) where it's available (eg, Linux) and reimplements it where it's not. This reimplementation can do the create a file and check its mode mechanism that you've implemented here, but it then deletes that file. This removes the world-writable file that may become a configuration file race.

@jmgao
Copy link
Author

jmgao commented Jan 8, 2020

I considered something like that, but it doesn't seem obvious that there's a good place to try to create the file to probe for umask, since there might be, for example, a noexec mount that screws with your probed umask. Now that I think about it, we can just do this twice to avoid the race: once to get the umask and throw away, and once with the proper permissions.

(getumask is apparently vaporware: grepping through the kernel confirms that there isn't a getumask syscall.)

@jmgao
Copy link
Author

jmgao commented Jan 8, 2020

Wait, we don't need to chmod at all, we can just open with that mode directly. chmod was only needed before because mkstemp creates the files with 0600 permissions.

@jmgao jmgao force-pushed the umask branch 2 times, most recently from 213a524 to 10d0689 Compare January 8, 2020 23:53
@neithernut
Copy link
Contributor

getumask is apparently vaporware: grepping through the kernel confirms that there isn't a getumask syscall.

Well, getumask(3) is a libc function. And a GNU extension. According to the man-page it's equivalent to:

mode_t getumask(void) {
    mode_t mask = umask(0);
    umask(mask);
    return mask;
}

So there is probably really no benefit in using it if it is available.

Pedantic nit: the term "vaporware" refers to software that was only announced but never implemented. This function was implemented, so it doesn't fit the definition. Still, it's pretty useless if you consider portability.

@jmgao
Copy link
Author

jmgao commented Jan 9, 2020

It's declared in the glibc headers, but there's only an implementation on Hurd. If you try to call it on Linux, you'll fail to link.

Copy link
Member

@ethomson ethomson left a comment

Choose a reason for hiding this comment

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

Thanks so much for the PR and especially for including a unit test. There are a few minor stylistic nits, but this also fails on Windows.

I'm happy to dig in on Windows and finish up the changes here, unless you'd prefer to take care of it yourself.

Really happy to see a threadsafe version of this. Thanks again! 🙇‍♂️

@jmgao
Copy link
Author

jmgao commented Jan 20, 2020

Sorry, I've been meaning to dig into the Windows failures, but I haven't managed to scrape up the time. The error seems like a leak that presumably should happen on all platforms, but after staring at the code a bit, I don't see where it is.

Maybe the test is returning early and failing to clean up the buffer because one of the assertions is failing on windows? I didn't see anything that indicated that in the logging, but I might have missed it.

@ethomson
Copy link
Member

Maybe the test is returning early and failing to clean up the buffer because one of the assertions is failing on windows?

That’s correct - our tests don’t tear everything down when they fail, so we’ll detect leaks there. Apologies that this isn’t more obvious; our CI grew rather organically, and leak detection was added on.

We may be able to disable printing the leaks if clar exits with a nonzero exit code. If nothing else, we could print a big banner at the bottom of the output on a failure.

@ethomson
Copy link
Member

I do have a concern about rand without srand. From a man page:

If no seed value is provided, the rand() function is automatically seeded with a value of 1.

Which would mean that our temporary files are deterministic. I can imagine a denial of service attack based on this - or perhaps more likely, we fail to create new files because we neglected to clean up the old ones accidentally.

@jmgao
Copy link
Author

jmgao commented Jan 22, 2020

Disabled the test on windows, and cleaned up all of the nits: hopefully this revision passes CI.

Previously, we were using `umask(mask = umask(0))` to fetch the current
umask in order to apply it to the desired mode, but this is broken in
the presence of threads. There is no portable way to directly fetch
umask without mutating it.

Instead, add a reimplementation of mkstemp that uses a passed-in mode,
instead of explicitly chmodding to 0600 like POSIX requires of mkstemp.

Fixes: jmgao/pore#46
int git_buffer_global_init(void)
{
/* Seed rand for git_buf_put_rand. */
srand((unsigned int)time(NULL));
Copy link
Member

Choose a reason for hiding this comment

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

We really shouldn't be using rand() and srand() at all. Seeding via time(NULL) only gives you a negligible amount of bits, as you can typically deduce at what time a certain command is running. I don't know about Windows, but on Unix-like systems most nowadays support calls like getrandom() that are much saner to use than the combination of rand() and srand().

while (tries--) {
git_buf_sets(path_out, filename);
git_buf_puts(path_out, "_git2_");
git_buf_put_rand(path_out, 8);
Copy link
Member

Choose a reason for hiding this comment

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

I'd argue that manually generating the name doesn't make a lot of sense. Instead, we should probably be using something like tempnam(3P) that properly handles this for us, and that would also remove the need for git_buf_put_rand and the random-seeding hassle.

Base automatically changed from master to main January 7, 2021 10:09
@ethomson
Copy link
Member

Closed via #6178

@jmgao jmgao closed this Jan 31, 2022
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.

pore set access bits of many directories to 777

4 participants