]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Clean up memory leak warnings from ASAN and Valgrind
authorPaul Smith <psmith@gnu.org>
Sun, 26 Mar 2023 19:35:00 +0000 (15:35 -0400)
committerPaul Smith <psmith@gnu.org>
Sat, 1 Apr 2023 15:13:12 +0000 (11:13 -0400)
* src/main.c (main): Add "sanitize" to .FEATURES if ASAN is enabled.
* src/expand.c (expand_variable_output): Remember "recursive" setting
in case it's changed by the expansion of the variable.
* src/file.c (rehash_file): If we drop a file from the global 'files'
hash, remember it in rehashed_files.  We can't free it because it's
still being referenced (callers will invoke check_renamed()) but
it will be a leak since it's no longer referenced by 'files'.
* src/remake.c (update_file_1): If we drop a dependency, remember it
in dropped_list.  We can't free it because it's still being referenced
by callers but it will be a leak since it's no longer referenced as
a prerequisite.
* tests/scripts/functions/guile: Don't run Guile tests when ASAN is
enabled.
* tests/scripts/functions/wildcard: Enabling ASAN causes glob(3) to
break!  Don't run this test.
* tests/scripts/features/exec: Valgrind's exec() doesn't support
scripts with no shbang.
* tests/scripts/jobserver: Valgrind fails if TMPDIR is set to an
invalid directory: skip those tests.
* tests/scripts/features/output-sync: Ditto.
* tests/scripts/features/temp_stdin: Ditto.

src/expand.c
src/file.c
src/main.c
src/read.c
src/remake.c
tests/scripts/features/exec
tests/scripts/features/jobserver
tests/scripts/features/output-sync
tests/scripts/features/temp_stdin
tests/scripts/functions/guile
tests/scripts/functions/wildcard

index 01fff81e4961e46237803455324e686d734c9b8d..533e7dfa719eebad9eb7ed2bd3b1d8ca84309a82 100644 (file)
@@ -226,6 +226,7 @@ char *
 expand_variable_output (char *ptr, const char *name, size_t length)
 {
   struct variable *v;
+  unsigned int recursive;
   char *value;
 
   v = lookup_variable (name, length);
@@ -237,11 +238,14 @@ expand_variable_output (char *ptr, const char *name, size_t length)
   if (!v || (v->value[0] == '\0' && !v->append))
     return ptr;
 
-  value = v->recursive ? recursively_expand (v) : v->value;
+  /* Remember this since expansion could change it.  */
+  recursive = v->recursive;
+
+  value = recursive ? recursively_expand (v) : v->value;
 
   ptr = variable_buffer_output (ptr, value, strlen (value));
 
-  if (v->recursive)
+  if (recursive)
     free (value);
 
   return ptr;
index 9828d2837e34d2c845b2c6d51ded2f58cf87f87b..f8e4fb917095774b6707518a5e7d9105495dd3eb 100644 (file)
@@ -60,6 +60,14 @@ file_hash_cmp (const void *x, const void *y)
 
 static struct hash_table files;
 
+/* We can't free files we take out of the hash table, because they are still
+   likely pointed to in various places.  The check_renamed() will be used if
+   we come across these, to find the new correct file.  This is mainly to
+   prevent leak checkers from complaining.  */
+static struct file **rehashed_files = NULL;
+static size_t rehashed_files_len = 0;
+#define REHASHED_FILES_INCR 5
+
 /* Whether or not .SECONDARY with no prerequisites was given.  */
 static int all_secondary = 0;
 
@@ -217,8 +225,7 @@ rehash_file (struct file *from_file, const char *to_hname)
 
   /* Find the end of the renamed list for the "from" file.  */
   file_key.hname = from_file->hname;
-  while (from_file->renamed != 0)
-    from_file = from_file->renamed;
+  check_renamed (from_file);
   if (file_hash_cmp (from_file, &file_key))
     /* hname changed unexpectedly!! */
     abort ();
@@ -331,6 +338,12 @@ rehash_file (struct file *from_file, const char *to_hname)
 
   to_file->builtin = 0;
   from_file->renamed = to_file;
+
+  if (rehashed_files_len % REHASHED_FILES_INCR == 0)
+    rehashed_files = xrealloc (rehashed_files,
+                               sizeof (struct file *) * (rehashed_files_len + REHASHED_FILES_INCR));
+
+  rehashed_files[rehashed_files_len++] = from_file;
 }
 
 /* Rename FILE to NAME.  This is not as simple as resetting
index 16bbb83ae056a16bad52795b71e1ff0e0190b008..bf74ab9429e3bf08f4c7516ba296cb5eb592ada0 100644 (file)
@@ -1466,6 +1466,9 @@ main (int argc, char **argv, char **envp)
 #endif
 #ifdef MAKE_MAINTAINER_MODE
                            " maintainer"
+#endif
+#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_MEMORY__)
+                           " sanitize"
 #endif
                            ;
 
index bae836f2c5ff47cc9d28af59d72dfec3797ee169..2d25be6a5989d9c257037e0d97a5d088ac5509e1 100644 (file)
@@ -3261,22 +3261,24 @@ parse_file_seq (char **stringp, size_t size, int stopmap,
 
       /* Strip leading "this directory" references.  */
       if (NONE_SET (flags, PARSEFS_NOSTRIP))
+        {
 #if MK_OS_VMS
-        /* Skip leading '[]'s. should only be one set or bug somewhere else */
-        if (p - s > 2 && s[0] == '[' && s[1] == ']')
+          /* Skip leading '[]'s. should only be one set or bug somewhere else */
+          if (p - s > 2 && s[0] == '[' && s[1] == ']')
             s += 2;
-        /* Skip leading '<>'s. should only be one set or bug somewhere else */
-        if (p - s > 2 && s[0] == '<' && s[1] == '>')
+          /* Skip leading '<>'s. should only be one set or bug somewhere else */
+          if (p - s > 2 && s[0] == '<' && s[1] == '>')
             s += 2;
 #endif
-        /* Skip leading './'s.  */
-        while (p - s > 2 && s[0] == '.' && s[1] == '/')
-          {
-            /* Skip "./" and all following slashes.  */
-            s += 2;
-            while (*s == '/')
-              ++s;
-          }
+          /* Skip leading './'s.  */
+          while (p - s > 2 && s[0] == '.' && s[1] == '/')
+            {
+              /* Skip "./" and all following slashes.  */
+              s += 2;
+              while (*s == '/')
+                ++s;
+            }
+        }
 
       /* Extract the filename just found, and skip it.
          Set NAME to the string, and NLEN to its length.  */
index d5cd3b38f0c6f3d1336c570baa2b53a92287d333..dfe981c3212acd6ca54055876b0366026f40c0aa 100644 (file)
@@ -69,6 +69,13 @@ static struct dep *goal_dep;
    All files start with considered == 0.  */
 static unsigned int considered = 0;
 
+/* During processing we might drop some dependencies, which can't be freed
+   immediately because they are still in use.  Remember them: this is mainly
+   to satisfy leak detectors.  */
+static struct dep **dropped_list = NULL;
+static size_t dropped_list_len = 0;
+#define DROPPED_LIST_INCR 5
+
 static enum update_status update_file (struct file *file, unsigned int depth);
 static enum update_status update_file_1 (struct file *file, unsigned int depth);
 static enum update_status check_dep (struct file *file, unsigned int depth,
@@ -608,15 +615,21 @@ update_file_1 (struct file *file, unsigned int depth)
               OSS (error, NILF, _("Circular %s <- %s dependency dropped."),
                    file->name, d->file->name);
 
-              /* We cannot free D here because our the caller will still have
-                 a reference to it when we were called recursively via
-                 check_dep below.  */
               if (lastd == 0)
                 file->deps = du->next;
               else
                 lastd->next = du->next;
 
               du = du->next;
+
+              /* We cannot free D here because our the caller will still have
+                 a reference to it when we were called recursively via
+                 check_dep below.  */
+              if (dropped_list_len % DROPPED_LIST_INCR == 0)
+                dropped_list = xrealloc (dropped_list,
+                                         sizeof (struct dep *) * (dropped_list_len + DROPPED_LIST_INCR));
+              dropped_list[dropped_list_len++] = d;
+
               continue;
             }
 
index f139cf8c8bbf520c0cefa79f3a70c3b708f73527..d96d31c15854b61f824c98feaff9f247ab1809d6 100644 (file)
@@ -15,7 +15,11 @@ my $details = "The various shells that this test uses are the default"
 $port_type eq 'UNIX' or return -1;
 $^O =~ /cygwin/ and return -1;
 
-my @shbangs = ('', '#!/bin/sh', "#!$perl_name");
+my @shbangs = ('#!/bin/sh', "#!$perl_name");
+
+# The exec in Valgrind's VM doesn't allow starting commands without any shbang
+$valgrind or push @shbangs, '';
+
 my @shells = ('', 'SHELL=/bin/sh');
 
 # Try whatever shell the user has, as long as it's not a C shell.
index a2f06ee8e2040a2934407059fc16f82c6cd68824..ad5f9e7f19f2269bb3f60877ce5ee49ecf025f2d 100644 (file)
@@ -168,7 +168,8 @@ if ($port_type ne 'W32') {
                   "all 1\nall 2\nrecurse");
 }
 
-if (exists $FEATURES{'jobserver-fifo'}) {
+# We can't reset TMPDIR to something invalid when using valgrind
+if (exists $FEATURES{'jobserver-fifo'} && !$valgrind) {
   # sv 62908.
   # Test that when mkfifo fails, make switches to pipe and succeeds.
   # Force mkfifo to fail by attempting to create a fifo in a non existent
index 3353f1c60c07dee7351d1bb54da58fe20e5ce977..779e24c8ebab0a35dcc55f8fbf763ee48870c8b0 100644 (file)
@@ -380,17 +380,23 @@ unlink($fout);
 # Create a non-writable temporary directory.
 # Run the test twice, because run_make_test cannot match a regex against a
 # multiline input.
-my $tdir = 'test_tmp_dir';
-mkdir($tdir, 0500);
-$ENV{'TMPDIR'} = $tdir;
 
-run_make_test(q!
+# If we do this Valgrind fails because it cannot write temp files... the docs
+# don't describe any way to tell valgrind to use a directory other than TMPDIR.
+
+if (!$valgrind) {
+    my $tdir = 'test_tmp_dir';
+    mkdir($tdir, 0500);
+    $ENV{'TMPDIR'} = $tdir;
+
+    run_make_test(q!
 all:; $(info hello, world)
 !, '-Orecurse', "/suppressing output-sync/");
 
-run_make_test(undef, '-Orecurse', "/#MAKE#: 'all' is up to date./");
+    run_make_test(undef, '-Orecurse', "/#MAKE#: 'all' is up to date./");
 
-rmdir($tdir);
+    rmdir($tdir);
+}
 }
 
 # This tells the test driver that the perl test script executed properly.
index fee32a9064937bcec391f75d124618d5d0dc8cb7..201dcb94c103e02f46c04160191eded04f66eb14 100644 (file)
@@ -115,16 +115,21 @@ rmdir($tmakedir);
 # makefile from stdin to a temporary file.
 # Create a non-writable temporary directory.
 
-my $tdir = 'test_tmp_dir';
-mkdir($tdir, 0500);
-$ENV{'TMPDIR'} = $tdir;
-close(STDIN);
-open(STDIN, "<", 'input.mk') || die "$0: cannot open input.mk for reading: $!";
+# If we do this Valgrind fails because it cannot write temp files... the docs
+# don't describe any way to tell valgrind to use a directory other than TMPDIR.
 
-run_make_test(q!
+if (!$valgrind) {
+    my $tdir = 'test_tmp_dir';
+    mkdir($tdir, 0500);
+    $ENV{'TMPDIR'} = $tdir;
+    close(STDIN);
+    open(STDIN, "<", 'input.mk') || die "$0: cannot open input.mk for reading: $!";
+
+    run_make_test(q!
 all:; $(info hello, world)
 !, '-f-', '/cannot store makefile from stdin to a temporary file.  Stop./', 512);
-rmdir($tdir);
+    rmdir($tdir);
+}
 }
 
 # This close MUST come at the end of the test!!
index 120aaf3bce1fc20dc454024985094568fc7f12b0..8c0012d92be50458404c6cfe016e6e16cb4a0abe 100644 (file)
@@ -21,7 +21,8 @@ $details = 'This only works on systems that support it.';
 # If we don't have Guile support, never mind.
 exists $FEATURES{guile} or return -1;
 
-# Guile and Valgrind don't play together at all.
+# Guile and Valgrind/ASAN don't play together at all.
+exists $FEATURES{sanitize} and return -1;
 $valgrind and return -1;
 
 # Verify simple data type conversions
index f01f574a97ab626d0b1a2123a8f931f535144cdd..1a1adddac3497e96d0fccaca3cdcb094b07bd0e7 100644 (file)
@@ -150,8 +150,9 @@ if ($port_type ne 'W32' && eval { symlink("",""); 1 }) {
 
   # Test for dangling symlinks
   # This doesn't work with the built-in glob... needs to be updated!
+  # It also for some obscure reason, will break if we use ASAN!!
 
-  if (get_config('USE_SYSTEM_GLOB') eq 'yes') {
+  if (get_config('USE_SYSTEM_GLOB') eq 'yes' && !exists($FEATURES{sanitize})) {
     symlink($dir, $lnk);
 
     run_make_test(qq!all: ; \@echo \$(wildcard $lnk)!, '', "$lnk");