diff options
author | Florian Weimer <fweimer@redhat.com> | 2020-01-21 17:38:15 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2020-02-14 08:14:07 +0100 |
commit | fa00db0a6eb755837ae5d413515e0da582b304f3 (patch) | |
tree | a93122a684888db52a32b30868c0733ed229a094 | |
parent | dd0b4df329ff7ff2a656404db271c8ee8379ff9d (diff) | |
download | glibc-fa00db0a6eb755837ae5d413515e0da582b304f3.tar.gz |
resolv: Fix ABA race in /etc/resolv.conf change detection [BZ #25420]
__resolv_conf_get_current should only record the initial file
change data if after verifying that file just read matches the
original measurement. Fixes commit aef16cc8a4c670036d45590877
("resolv: Automatically reload a changed /etc/resolv.conf file
[BZ #984]").
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
-rw-r--r-- | resolv/resolv_conf.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c index bdd2ebb909..29a1f4fb94 100644 --- a/resolv/resolv_conf.c +++ b/resolv/resolv_conf.c @@ -136,18 +136,25 @@ __resolv_conf_get_current (void) { /* Parse configuration while holding the lock. This avoids duplicate work. */ - conf = __resolv_conf_load (NULL, NULL); + struct file_change_detection after_load; + conf = __resolv_conf_load (NULL, &after_load); if (conf != NULL) { if (global_copy->conf_current != NULL) conf_decrement (global_copy->conf_current); global_copy->conf_current = conf; /* Takes ownership. */ - /* Update file modification stamps. The configuration we - read could be a newer version of the file, but this does - not matter because this will lead to an extraneous reload - later. */ - global_copy->file_resolve_conf = initial; + /* Update file change detection data, but only if it matches + the initial measurement. This avoids an ABA race in case + /etc/resolv.conf is temporarily replaced while the file + is read (after the initial measurement), and restored to + the initial version later. */ + if (file_is_unchanged (&initial, &after_load)) + global_copy->file_resolve_conf = after_load; + else + /* If there is a discrepancy, trigger a reload during the + next use. */ + global_copy->file_resolve_conf.size = -1; } } |