Skip to content
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

Add Dir.fchdir #7135

Merged
merged 3 commits into from
Mar 24, 2023
Merged

Add Dir.fchdir #7135

merged 3 commits into from
Mar 24, 2023

Conversation

jeremyevans
Copy link
Contributor

This is useful for passing directory file descriptors over UNIX sockets or to child processes to avoid TOCTOU vulnerabilities.

The implementation follows the Dir.chdir code.

This will raise NotImplementedError on platforms not supporting both fchdir and dirfd.

return rb_ensure(fchdir_yield, (VALUE)&args, fchdir_restore, (VALUE)&args);
}
else {
int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_fchdir, &fd,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we yield the GVL here? can fchdir actually block for an appreciable amount of time on any actual system? Does it even do any I/O?

(I'm guessing the answer is "NFS" or something like that...)

In any case, if the block-not-given form yields the GVL to do the chdir, should the block-given form also do so?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know whether fchdir can block. Since I'm not sure it is nonblocking in all cases, I decided to be conservative and follow what we do for chdir.

In terms of the block form not yielding the GVL, I'm not sure why it doesn't do a nogvl call in the block case. If this was an issue for fchdir, it would be an issue for chdir as well.

pwd = Dir.pwd
dir = Dir.new(DirSpecs.mock_dir)
@dirs << dir
Dir.fchdir(dir.fileno) { Dir.pwd }.should == DirSpecs.mock_dir
Copy link
Member

Choose a reason for hiding this comment

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

Isn't Dir.fchdir(dir) possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. We can support that if people want.

Copy link
Member

Choose a reason for hiding this comment

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

Rather I'd suggest making the argument Dir only and falling back to Dir.chidir(dir.path).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The primary purpose of Dir.fchdir is to work with directory file descriptor integers. Currently we don't have Dir.fdopen (something to create a Dir object from a file descriptor integer), so requiring Dir objects would make it impossible to use this method with file descriptors passed through UNIX sockets or from parent processes.

Maybe we could make Dir.chdir accept Dir objects, and call Dir.fchdir with the file descriptor if directory file descriptors are supported, or use dir.path if not? That seems like it should be a separate pull request, though.

@jeremyevans jeremyevans force-pushed the dir-fchdir branch 3 times, most recently from ab732e7 to a1f699b Compare February 9, 2023 22:26
@jeremyevans jeremyevans requested a review from nobu February 9, 2023 23:11
This is useful for passing directory file descriptors over UNIX
sockets or to child processes to avoid TOCTOU vulnerabilities.

The implementation follows the Dir.chdir code.

This will raise NotImplementedError on platforms not supporting
both fchdir and dirfd.

Implements [Feature #19347]
This uses Dir.fchdir if supported, or Dir.chdir otherwise.

Implements [Feature #19347]
This returns a Dir instance for the given directory file descriptor.
If fdopendir is not supported, this raises NotImplementedError.

Implements [Feature #19347]
rescue NotImplementedError
false
rescue Exception
true
Copy link
Member

Choose a reason for hiding this comment

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

Should it be false here? If it raises an exception it's not supported, right?

Copy link
Member

Choose a reason for hiding this comment

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

I changed it to false in ruby/spec: ruby/spec@c075975

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it isn't supported, it should be raising NotImplementedError. If it raises another exception, that indicates it is supported, but the support is broken, which means the specs should rightfully fail for it.

I don't like the change in ruby/spec@c075975, because it allows a ruby implementation that advertises Ruby 3.3 version to not implement the method. If a ruby implementation says it implements Ruby 3.3, it must implement the method, even if the method always raises NotImplementedError.

Copy link
Member

Choose a reason for hiding this comment

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

Note that at least one spec will fail in that case, because if has_fchdir=false, then the raises NotImplementedError spec will still fail if it's not a NotImplementedError.

But you are right, we shouldn't rescue NoMethodError there. That should be handled by guard or so.
I think we can actually just use Dir.respond_to?(:fchdir) and that will also correctly tell us if it's available on that platform or not. I'll do that change.

Copy link
Member

Choose a reason for hiding this comment

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

(the trick is respond_to? returns false for a method defined as rb_notimplement)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That works for me, thanks!

eregon added a commit to ruby/spec that referenced this pull request May 1, 2023
seven1m pushed a commit to seven1m/ruby_spec that referenced this pull request Sep 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants