File toctou-race-allows-arbitrary-file-deletion.patch of Package fdupes.33850

From 85680897148f1ac33b55418e00334116e419717f Mon Sep 17 00:00:00 2001
From: Adrian Lopez <adrianlopezroche@gmail.com>
Date: Tue, 16 Aug 2022 14:39:18 -0400
Subject: [PATCH] Call stat() before deleting any file to ensure it's still the
 same file.

---
 Makefile.am          |  4 ++++
 fdupes.c             | 11 +++++++----
 ncurses-commands.c   |  3 ++-
 removeifnotchanged.c | 41 +++++++++++++++++++++++++++++++++++++++++
 removeifnotchanged.h |  8 ++++++++
 5 files changed, 62 insertions(+), 5 deletions(-)
 create mode 100644 removeifnotchanged.c
 create mode 100644 removeifnotchanged.h

Index: fdupes-1.6.1/fdupes.c
===================================================================
--- fdupes-1.6.1.orig/fdupes.c	2016-08-21 04:54:46.000000000 +0000
+++ fdupes-1.6.1/fdupes.c	2024-04-25 09:13:19.407546801 +0000
@@ -109,6 +109,42 @@ typedef struct _filetree {
   struct _filetree *right;
 } filetree_t;
 
+int removeifnotchanged(const file_t *file, char **errorstring)
+{
+  int result;
+  struct stat st;
+
+  static char *filechanged = "File contents changed during processing";
+  static char *unknownerror = "Unknown error";
+
+  stat(file->d_name, &st);
+
+  if (file->device != st.st_dev ||
+      file->inode != st.st_ino ||
+      file->mtime != st.st_mtime ||
+      file->size != st.st_size)
+  {
+    if (errorstring != 0)
+        *errorstring = filechanged;
+
+    return -2;
+  }
+  else
+  {
+    result = remove(file->d_name);
+
+    if (result != 0 && errorstring != 0)
+    {
+      *errorstring = strerror(errno);
+
+      if (*errorstring == 0)
+        *errorstring = unknownerror;
+    }
+
+    return result;
+  }
+}
+
 void errormsg(char *message, ...)
 {
   va_list ap;
@@ -773,6 +809,7 @@ void deletefiles(file_t *files, int prom
   int max = 0;
   int x;
   int i;
+  char *errorstring;
 
   curfile = files;
   
@@ -884,11 +921,11 @@ void deletefiles(file_t *files, int prom
 	if (preserve[x])
 	  printf("   [+] %s\n", dupelist[x]->d_name);
 	else {
-	  if (remove(dupelist[x]->d_name) == 0) {
+	  if (removeifnotchanged(dupelist[x], &errorstring) == 0) {
 	    printf("   [-] %s\n", dupelist[x]->d_name);
 	  } else {
 	    printf("   [!] %s ", dupelist[x]->d_name);
-	    printf("-- unable to delete file!\n");
+	    printf("-- unable to delete file: %s!\n", errorstring);
 	  }
 	}
       }
@@ -977,6 +1014,7 @@ void deletesuccessor(file_t **existing,
 {
   file_t *to_keep;
   file_t *to_delete;
+  char *errorstring;
 
   if (comparef(duplicate, *existing) >= 0)
   {
@@ -994,11 +1032,11 @@ void deletesuccessor(file_t **existing,
   if (!ISFLAG(flags, F_HIDEPROGRESS)) fprintf(stderr, "\r%40s\r", " ");
 
   printf("   [+] %s\n", to_keep->d_name);
-  if (remove(to_delete->d_name) == 0) {
+  if (removeifnotchanged(to_delete, &errorstring) == 0) {
     printf("   [-] %s\n", to_delete->d_name);
   } else {
     printf("   [!] %s ", to_delete->d_name);
-    printf("-- unable to delete file!\n");
+    printf("-- unable to delete file: %s!\n", errorstring);
   }
 
   printf("\n");
openSUSE Build Service is sponsored by