From 8fe7820c8bdad218ec2e237becb3b5a7eec54d2c Mon Sep 17 00:00:00 2001 From: Mohamed Date: Wed, 25 Feb 2026 21:13:25 +0200 Subject: [PATCH] MDEV-38494 .mariadb_history rename race condition When multiple mariadb sessions exit at the same time, they all write to the same $HISTFILE.TMP then rename it to $HISTFILE. The second rename fails with Errcode 2 because the first already moved the file. Fix: include the process PID in the temp file name so each session uses its own unique path and no rename collision can occur. --- client/mysql.cc | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index ea8e350956c16..b0e0983c98609 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1441,13 +1441,25 @@ int main(int argc,char *argv[]) if (verbose) tee_fprintf(stdout, "Reading history-file %s\n",histfile); read_history(histfile); - if (!(histfile_tmp= (char*) my_malloc(PSI_NOT_INSTRUMENTED, - strlen(histfile) + 5, MYF(MY_WME)))) { - fprintf(stderr, "Couldn't allocate memory for temp histfile!\n"); - exit(1); + /* Extra space for the suffix appended to histfile: + "." - separator (sizeof = 2, includes NUL) + + PID - up to 20 digits (ULONG_MAX = 18446744073709551615) + + ".TMP"- extension (sizeof = 5, includes NUL, doubles + as the string NUL terminator) + sizeof(".") and sizeof(".TMP") each carry a NUL, so the sum + over-allocates by one byte, which is intentional. */ + size_t hlen= strlen(histfile) + sizeof(".") + 20 + sizeof(".TMP"); + if (!(histfile_tmp= (char*) my_malloc(PSI_NOT_INSTRUMENTED, hlen, MYF(MY_WME)))) + { + fprintf(stderr, "Couldn't allocate memory for temp histfile!\n"); + exit(1); + } + /* Include PID in name to avoid rename collision when concurrent sessions exit. */ + /* pid_t is signed but getpid() always returns a non-negative value (POSIX). */ + snprintf(histfile_tmp, hlen, "%s.%lu.TMP", histfile, + (unsigned long) getpid()); } - sprintf(histfile_tmp, "%s.TMP", histfile); } }