From 995cfe18668743841b304cacda02bd22fe4d39c0 Mon Sep 17 00:00:00 2001 From: abicky Date: Mon, 29 Jun 2020 00:25:15 +0900 Subject: [PATCH] Ensure that temporary file is used only by one process mkstemp(3) ensures that a unique file is created, but in the previous implementation, there's a possibility that a process uses the temporary file created by another process if mkstemp(3) fails to create a file due to EEXIST. That has the same risk as https://github.com/Shopify/bootsnap/issues/174. This commit will also resolve https://github.com/Shopify/bootsnap/issues/177 if the cause is that multiple processes try to create a file with the same name at the same time. --- ext/bootsnap/bootsnap.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/ext/bootsnap/bootsnap.c b/ext/bootsnap/bootsnap.c index 19a0a13e..ab41eff2 100644 --- a/ext/bootsnap/bootsnap.c +++ b/ext/bootsnap/bootsnap.c @@ -32,6 +32,8 @@ #define KEY_SIZE 64 +#define MAX_CREATE_TEMPFILE_ATTEMPT 3 + /* * An instance of this key is written as the first 64 bytes of each cache file. * The mtime and size members track whether the file contents have changed, and @@ -499,25 +501,32 @@ atomic_write_cache_file(char * path, struct bs_cache_key * key, VALUE data, cons { char template[MAX_CACHEPATH_SIZE + 20]; char * tmp_path; - int fd, ret; + int fd, ret, attempt; ssize_t nwrite; - tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE); - strcat(tmp_path, ".tmp.XXXXXX"); + for (attempt = 0; attempt < MAX_CREATE_TEMPFILE_ATTEMPT; ++attempt) { + tmp_path = strncpy(template, path, MAX_CACHEPATH_SIZE); + strcat(tmp_path, ".tmp.XXXXXX"); - // mkstemp modifies the template to be the actual created path - fd = mkstemp(tmp_path); - if (fd < 0) { - if (mkpath(tmp_path, 0775) < 0) { + // mkstemp modifies the template to be the actual created path + fd = mkstemp(tmp_path); + if (fd > 0) break; + + if (attempt == 0 && mkpath(tmp_path, 0775) < 0) { *errno_provenance = "bs_fetch:atomic_write_cache_file:mkpath"; return -1; } - fd = open(tmp_path, O_WRONLY | O_CREAT, 0664); - if (fd < 0) { - *errno_provenance = "bs_fetch:atomic_write_cache_file:open"; - return -1; - } } + if (fd < 0) { + *errno_provenance = "bs_fetch:atomic_write_cache_file:mkstemp"; + return -1; + } + + if (chmod(tmp_path, 0644) < 0) { + *errno_provenance = "bs_fetch:atomic_write_cache_file:chmod"; + return -1; + } + #ifdef _WIN32 setmode(fd, O_BINARY); #endif