Utilities: harden exim_tidydb against corrupt wait-records. Bug 2343
[exim.git] / src / src / exim_dbutil.c
index 80f65653000c11c21c02d2e98a61995222ad976c..7ca7a307361607b65b7d0233390120fdb469ad2b 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -59,6 +60,10 @@ string_vformat_trc(gstring * g, const uschar * func, unsigned line,
 uschar *
 string_sprintf_trc(const char * fmt, const uschar * func, unsigned line, ...)
 { return NULL; }
+BOOL
+string_format_trc(uschar * buf, int len, const uschar * func, unsigned line,
+  const char * fmt, ...)
+{ return FALSE; }
 
 struct global_flags    f;
 unsigned int           log_selector[1];
@@ -101,7 +106,6 @@ SIGNAL_BOOL sigalrm_seen;
 void
 sigalrm_handler(int sig)
 {
-sig = sig;            /* Keep picky compilers happy */
 sigalrm_seen = 1;
 }
 
@@ -172,8 +176,6 @@ va_start(ap, format);
 vfprintf(stderr, format, ap);
 fprintf(stderr, "\n");
 va_end(ap);
-selector = selector;     /* Keep picky compilers happy */
-flags = flags;
 }
 
 
@@ -332,7 +334,7 @@ if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL;
 #else
 filename = string_sprintf("%s/%s", dirname, name);
 #endif
-EXIM_DBOPEN(filename, dirname, flags, 0, &(dbblock->dbptr));
+EXIM_DBOPEN(filename, dirname, flags, 0, &dbblock->dbptr);
 
 if (!dbblock->dbptr)
   {
@@ -386,7 +388,7 @@ pick out the timestamps, etc., do the copying centrally here.
 Arguments:
   dbblock   a pointer to an open database block
   key       the key of the record to be read
-  length    where to put the length (or NULL if length not wanted)
+  length    where to put the length (or NULL if length not wanted). Includes overhead.
 
 Returns: a pointer to the retrieved record, or
          NULL if the record is not found
@@ -414,7 +416,7 @@ we should store the taint status along with the data. */
 
 yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
 memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
-if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
+if (length) *length = EXIM_DATUM_SIZE(result_datum);
 
 EXIM_DATUM_FREE(result_datum);    /* Some DBM libs require freeing */
 return yield;
@@ -513,7 +515,6 @@ dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
 {
 EXIM_DATUM key_datum, value_datum;
 uschar *yield;
-value_datum = value_datum;    /* dummy; not all db libraries use this */
 
 /* Some dbm require an initialization */
 
@@ -615,6 +616,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
        t = wait->text;
        name[MESSAGE_ID_LENGTH] = 0;
 
+    /* Leave corrupt records alone */
        if (wait->count > WAIT_NAME_MAX)
          {
          fprintf(stderr,
@@ -1217,7 +1219,7 @@ for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
   /* A continuation record may have been deleted or renamed already, so
   non-existence is not serious. */
 
-  if (value == NULL) continue;
+  if (!value) continue;
 
   /* Delete if too old */
 
@@ -1238,12 +1240,33 @@ for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
 
     /* Leave corrupt records alone */
 
+    if (wait->time_stamp > time(NULL))
+      {
+      printf("**** Data for '%s' corrupted\n  time in future: %s\n",
+        key, print_time(((dbdata_generic *)value)->time_stamp));
+      continue;
+      }
     if (wait->count > WAIT_NAME_MAX)
       {
-      printf("**** Data for %s corrupted\n  count=%d=0x%x max=%d\n",
+      printf("**** Data for '%s' corrupted\n  count=%d=0x%x max=%d\n",
         key, wait->count, wait->count, WAIT_NAME_MAX);
       continue;
       }
+    if (wait->sequence > WAIT_CONT_MAX)
+      {
+      printf("**** Data for '%s' corrupted\n  sequence=%d=0x%x max=%d\n",
+        key, wait->sequence, wait->sequence, WAIT_CONT_MAX);
+      continue;
+      }
+
+    /* Record over 1 year old; just remove it */
+
+    if (wait->time_stamp < time(NULL) - 365*24*60*60)
+      {
+      dbfn_delete(dbm, key);
+      printf("deleted %s (too old)\n", key);
+      continue;
+      }
 
     /* Loop for renamed continuation records. For each message id,
     check to see if the message exists, and if not, remove its entry