From e4825a5194af822104ec9fcac00004a2bad30f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Wed, 3 Jul 2024 13:26:21 +0200 Subject: [PATCH] [rubygems/rubygems] Fix another race condition We also need to protect prior removal of the binstub, otherwise it can happen that: * Process A removes prior binstub FOO. * Process B removes prior binstub FOO (does nothing actually because Process A already removed it). * Process A writes binstub FOO for gem BAR from the beginning of file. * Process B writes binstub FOO for gem BAZ from the beginning of file. Similarly as before, if binstub FOO for gem BAR is bigger that binstub FOO for gem BAZ, garbage bytes will be left around at the end of the file, corrupting the binstub. The solution is to also protect removal of the previous binstub. To do this, we use a file lock on an explicit `.lock` file. https://github.com/rubygems/rubygems/commit/d99a80e62d --- lib/rubygems/installer.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 3705174ff0..d558c0be2b 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -538,12 +538,14 @@ class Gem::Installer def generate_bin_script(filename, bindir) bin_script_path = File.join bindir, formatted_program_filename(filename) - require "fileutils" - FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers + Gem.open_file_with_flock("#{bin_script_path}.lock") do + require "fileutils" + FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers - Gem.open_file_with_flock(bin_script_path) do |file| - file.write app_script_text(filename) - file.chmod(options[:prog_mode] || 0o755) + File.open(bin_script_path, "wb", 0o755) do |file| + file.write app_script_text(filename) + file.chmod(options[:prog_mode] || 0o755) + end end verbose bin_script_path