Rewrite chunks of inotify.c to remove a memory leak. nxdomain-address
authorSimon Kelley <simon@thekelleys.org.uk>
Thu, 29 May 2025 21:44:15 +0000 (22:44 +0100)
committerSimon Kelley <simon@thekelleys.org.uk>
Thu, 29 May 2025 21:44:15 +0000 (22:44 +0100)
The code is much easier to follow now, this shouldn't
happen again.

src/inotify.c

index 8f07a98..e2f2fb3 100644 (file)
@@ -133,29 +133,44 @@ void inotify_dnsmasq_init()
     }
 }
 
-static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
+static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *file)
 {
   /* Check if this file is already known in dd->files */
-  struct hostsfile *ah = NULL;
-  for(ah = dd->files; ah; ah = ah->next)
-    if(ah && ah->fname && strcmp(path, ah->fname) == 0)
+  struct hostsfile *ah;
+  size_t dirlen = sizeof(dd->dname);
+  
+  /* ah->fname always starts with the string in dd->dname */
+  for (ah = dd->files; ah; ah = ah->next)
+    if (ah->fname[dirlen+2] == '/' &&
+        strcmp(&ah->fname[dirlen+3], file) == 0)
       return ah;
-
+  
   /* Not known, create new hostsfile record for this dyndir */
-  struct hostsfile *newah = NULL;
-  if(!(newah = whine_malloc(sizeof(struct hostsfile))))
-    return NULL;
-
-  /* Add this file to the tip of the linked list */
-  newah->next = dd->files;
-  dd->files = newah;
-
-  /* Copy flags, set index and the full file path */
-  newah->flags = dd->flags;
-  newah->index = daemon->host_index++;
-  newah->fname = path;
+  if ((ah = whine_malloc(sizeof(struct hostsfile))))
+    {
+      char *path;
 
-  return newah;
+      if (!(path = whine_malloc(dirlen + strlen(file) + 2)))
+       {
+         free(ah);
+         return NULL;
+       }
+      
+      strcpy(path, dd->dname);
+      strcat(path, "/");
+      strcat(path, file);
+      
+      /* Add this file to the tip of the linked list */
+      ah->next = dd->files;
+      dd->files = ah;
+      
+      /* Copy flags, set index and the full file path */
+      ah->flags = dd->flags;
+      ah->index = daemon->host_index++;
+      ah->fname = path;
+    }
+  
+  return ah;
 }
 
 
@@ -204,10 +219,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
 
        while ((ent = readdir(dir_stream)))
         {
-          size_t lendir = strlen(dd->dname);
           size_t lenfile = strlen(ent->d_name);
-          char *path;
-
+                  
           /* ignore emacs backups and dotfiles */
           if (lenfile == 0 || 
               ent->d_name[lenfile - 1] == '~' ||
@@ -215,33 +228,36 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
               ent->d_name[0] == '.')
             continue;
 
-          if ((path = whine_malloc(lendir + lenfile + 2)))
+          if (dd->flags & AH_HOSTS)
             {
               struct hostsfile *ah;
 
-              strcpy(path, dd->dname);
-              strcat(path, "/");
-              strcat(path, ent->d_name);
-
-              if (!(ah = dyndir_addhosts(dd, path)))
-                {
-                  free(path);
-                  continue;
-                }
-              
               /* ignore non-regular files */
-              if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
-                {
-                  if (dd->flags & AH_HOSTS)
-                    total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
+              if ((ah = dyndir_addhosts(dd, ent->d_name)) &&
+                  stat(ah->fname, &buf) != -1 && S_ISREG(buf.st_mode))
+                total_size = read_hostsfile(ah->fname, ah->index, total_size, rhash, revhashsz);
+            }
 #ifdef HAVE_DHCP
-                  else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
+          else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
+            {
+              char *path;
+              
+              if ((path = whine_malloc(strlen(dd->dname) + lenfile + 2)))
+                {
+                  strcpy(path, dd->dname);
+                  strcat(path, "/");
+                  strcat(path, ent->d_name);
+                  
+                  /* ignore non-regular files */
+                  if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
                     option_read_dynfile(path, dd->flags);
-#endif            
+                  
+                  free(path);
                 }
             }
+#endif            
         }
-
+       
        closedir(dir_stream);
     }
 }
@@ -285,50 +301,51 @@ int inotify_check(time_t now)
          for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
            if (dd->wd == in->wd)
              {
-               size_t lendir = strlen(dd->dname);
-               char *path;
-                               
-               if ((path = whine_malloc(lendir + in->len + 2)))
+               if (dd->flags & AH_HOSTS)
                  {
-                   struct hostsfile *ah = NULL;
-
-                   strcpy(path, dd->dname);
-                   strcat(path, "/");
-                   strcat(path, in->name);
-
-                   /* Is this is a deletion event? */
-                   if (in->mask & IN_DELETE)
-                     my_syslog(LOG_INFO, _("inotify: %s removed"), path);
-                   else 
-                     my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
-
-                   if (dd->flags & AH_HOSTS)
+                   struct hostsfile *ah;
+                   if ((ah = dyndir_addhosts(dd, in->name)))
                      {
-                       if ((ah = dyndir_addhosts(dd, path)))
-                         {
-                           const unsigned int removed = cache_remove_uid(ah->index);
-                           if (removed > 0)
-                             my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
-
-                           /* (Re-)load hostsfile only if this event isn't triggered by deletion */
-                           if (!(in->mask & IN_DELETE))
-                             read_hostsfile(path, ah->index, 0, NULL, 0);
+                       const unsigned int removed = cache_remove_uid(ah->index);
+
+                       /* Is this is a deletion event? */
+                       if (in->mask & IN_DELETE)
+                         my_syslog(LOG_INFO, _("inotify: %s removed"), ah->fname);
+                       else 
+                         my_syslog(LOG_INFO, _("inotify: %s new or modified"), ah->fname);
+
+                       if (removed > 0)
+                         my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, ah->fname);
+                       
+                       /* (Re-)load hostsfile only if this event isn't triggered by deletion */
+                       if (!(in->mask & IN_DELETE))
+                         read_hostsfile(ah->fname, ah->index, 0, NULL, 0);
 #ifdef HAVE_DHCP
-                           if (daemon->dhcp || daemon->doing_dhcp6) 
-                             {
-                               /* Propagate the consequences of loading a new dhcp-host */
-                               dhcp_update_configs(daemon->dhcp_conf);
-                               lease_update_from_configs(); 
-                               lease_update_file(now); 
-                               lease_update_dns(1);
-                             }
-#endif
+                       if (daemon->dhcp || daemon->doing_dhcp6) 
+                         {
+                           /* Propagate the consequences of loading a new dhcp-host */
+                           dhcp_update_configs(daemon->dhcp_conf);
+                           lease_update_from_configs(); 
+                           lease_update_file(now); 
+                           lease_update_dns(1);
                          }
+#endif
                      }
+                 }
 #ifdef HAVE_DHCP
-                   else if (dd->flags & AH_DHCP_HST)
+               else if (!(in->mask & IN_DELETE))
+                 {
+                   char *path;
+
+                   if ((path = whine_malloc(strlen(dd->dname) + in->len + 2)))
                      {
-                       if (option_read_dynfile(path, AH_DHCP_HST))
+                       strcpy(path, dd->dname);
+                       strcat(path, "/");
+                       strcat(path, in->name);
+                       
+                       my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
+
+                       if ((dd->flags & AH_DHCP_HST) && option_read_dynfile(path, AH_DHCP_HST))
                          {
                            /* Propagate the consequences of loading a new dhcp-host */
                            dhcp_update_configs(daemon->dhcp_conf);
@@ -336,17 +353,19 @@ int inotify_check(time_t now)
                            lease_update_file(now); 
                            lease_update_dns(1);
                          }
-                     }
-                   else if (dd->flags & AH_DHCP_OPT)
-                     option_read_dynfile(path, AH_DHCP_OPT);
-#endif
+                       
+                       if (dd->flags & AH_DHCP_OPT)
+                         option_read_dynfile(path, AH_DHCP_OPT);
                    
-                   if (!ah)
-                     free(path);
+                       free(path);
+                     }
                  }
+#endif
+               
              }
        }
     }
+  
   return hit;
 }