Skip to content

Commit 8a6bf3e

Browse files
committed
Avoid closing directory we're iterating
Ruby 3.4 started error checking directory access and starts to raise Errno::EBADF. This particular loop iterates on all open file descriptors and one is the directory listing from Dir.foreach. In the past this could have led to leaked file descriptors, but it's unlikely since it's likely the last opened file descriptor and have the highest number. Link: ruby/ruby@f2919bd Link: https://bugzilla.redhat.com/show_bug.cgi?id=2349352
1 parent d27a455 commit 8a6bf3e

File tree

2 files changed

+11
-7
lines changed

2 files changed

+11
-7
lines changed

lib/puppet/util.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,10 @@ def safe_posix_fork(stdin = $stdin, stdout = $stdout, stderr = $stderr, &block)
478478
$stderr = STDERR
479479

480480
begin
481-
Dir.foreach('/proc/self/fd') do |f|
482-
if f != '.' && f != '..' && f.to_i >= 3
481+
d = Dir.new('/proc/self/fd')
482+
ignore_fds = ['.', '..', d.fileno.to_s]
483+
d.each_child do |f|
484+
if !ignore_fds.include?(f) && f.to_i >= 3
483485
begin
484486
IO.new(f.to_i).close
485487
rescue

spec/unit/util_spec.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -616,18 +616,20 @@ def withenv_utf8(&block)
616616
fds.each do |fd|
617617
if fd == '.' || fd == '..'
618618
next
619-
elsif ['0', '1', '2'].include? fd
619+
elsif ['0', '1', '2', '5'].include? fd
620620
expect(IO).not_to receive(:new).with(fd.to_i)
621621
else
622622
expect(IO).to receive(:new).with(fd.to_i).and_return(double('io', close: nil))
623623
end
624624
end
625625

626-
dir_expectation = receive(:foreach).with('/proc/self/fd')
626+
dir = double(Dir, fileno: '5')
627+
dir_expectation = receive(:each_child)
627628
fds.each do |fd|
628629
dir_expectation = dir_expectation.and_yield(fd)
629630
end
630-
allow(Dir).to dir_expectation
631+
allow(dir).to dir_expectation
632+
allow(Dir).to receive(:new).with('/proc/self/fd').and_return(dir)
631633
Puppet::Util.safe_posix_fork
632634
end
633635

@@ -636,7 +638,7 @@ def withenv_utf8(&block)
636638
# letting it actually close fds, which seems risky
637639
(0..2).each {|n| expect(IO).not_to receive(:new).with(n)}
638640
(3..256).each {|n| expect(IO).to receive(:new).with(n).and_return(double('io', close: nil)) }
639-
allow(Dir).to receive(:foreach).with('/proc/self/fd').and_raise(Errno::ENOENT)
641+
allow(Dir).to receive(:new).with('/proc/self/fd').and_raise(Errno::ENOENT)
640642

641643
Puppet::Util.safe_posix_fork
642644
end
@@ -646,7 +648,7 @@ def withenv_utf8(&block)
646648
# letting it actually close fds, which seems risky
647649
(0..2).each {|n| expect(IO).not_to receive(:new).with(n)}
648650
(3..256).each {|n| expect(IO).to receive(:new).with(n).and_return(double('io', close: nil)) }
649-
allow(Dir).to receive(:foreach).with('/proc/self/fd').and_raise(Errno::ENOTDIR)
651+
allow(Dir).to receive(:new).with('/proc/self/fd').and_raise(Errno::ENOTDIR)
650652

651653
Puppet::Util.safe_posix_fork
652654
end

0 commit comments

Comments
 (0)