a2cf7d
From: Florian Weimer <fweimer@redhat.com>
a2cf7d
Date: Tue, 21 Jan 2020 16:38:15 +0000 (+0100)
a2cf7d
Subject: resolv: Fix ABA race in /etc/resolv.conf change detection [BZ #25420]
a2cf7d
X-Git-Url: https://sourceware.org/git/?p=glibc.git;a=commitdiff_plain;h=fa00db0a6eb755837ae5d413515e0da582b304f3
a2cf7d
a2cf7d
resolv: Fix ABA race in /etc/resolv.conf change detection [BZ #25420]
a2cf7d
a2cf7d
__resolv_conf_get_current should only record the initial file
a2cf7d
change data if after verifying that file just read matches the
a2cf7d
original measurement.  Fixes commit aef16cc8a4c670036d45590877
a2cf7d
("resolv: Automatically reload a changed /etc/resolv.conf file
a2cf7d
[BZ #984]").
a2cf7d
a2cf7d
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
a2cf7d
---
a2cf7d
a2cf7d
diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
a2cf7d
index bdd2ebb909..29a1f4fb94 100644
a2cf7d
--- a/resolv/resolv_conf.c
a2cf7d
+++ b/resolv/resolv_conf.c
a2cf7d
@@ -136,18 +136,25 @@ __resolv_conf_get_current (void)
a2cf7d
     {
a2cf7d
       /* Parse configuration while holding the lock.  This avoids
a2cf7d
          duplicate work.  */
a2cf7d
-      conf = __resolv_conf_load (NULL, NULL);
a2cf7d
+      struct file_change_detection after_load;
a2cf7d
+      conf = __resolv_conf_load (NULL, &after_load);
a2cf7d
       if (conf != NULL)
a2cf7d
         {
a2cf7d
           if (global_copy->conf_current != NULL)
a2cf7d
             conf_decrement (global_copy->conf_current);
a2cf7d
           global_copy->conf_current = conf; /* Takes ownership.  */
a2cf7d
 
a2cf7d
-          /* Update file modification stamps.  The configuration we
a2cf7d
-             read could be a newer version of the file, but this does
a2cf7d
-             not matter because this will lead to an extraneous reload
a2cf7d
-             later.  */
a2cf7d
-          global_copy->file_resolve_conf = initial;
a2cf7d
+          /* Update file change detection data, but only if it matches
a2cf7d
+             the initial measurement.  This avoids an ABA race in case
a2cf7d
+             /etc/resolv.conf is temporarily replaced while the file
a2cf7d
+             is read (after the initial measurement), and restored to
a2cf7d
+             the initial version later.  */
a2cf7d
+          if (file_is_unchanged (&initial, &after_load))
a2cf7d
+            global_copy->file_resolve_conf = after_load;
a2cf7d
+          else
a2cf7d
+            /* If there is a discrepancy, trigger a reload during the
a2cf7d
+               next use.  */
a2cf7d
+            global_copy->file_resolve_conf.size = -1;
a2cf7d
         }
a2cf7d
     }
a2cf7d