summaryrefslogtreecommitdiff
path: root/source/l/glibc/glibc-2.15.nscd-race-fix.diff
blob: f1323570f580702cc79f805af33923dac2ca6c9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
--- c/nscd/nscd_gethst_r.c	2012-01-01 05:16:32.000000000 -0700
+++ c/nscd/nscd_gethst_r.c	2012-03-28 10:45:51.546600822 -0600
@@ -101,9 +101,27 @@ libc_freeres_fn (hst_map_free)
 uint32_t
 __nscd_get_nl_timestamp (void)
 {
+  uint32_t retval;
   if (__nss_not_use_nscd_hosts != 0)
     return 0;
 
+  int cnt = 0;
+  /* __nscd_get_mapping can change hst_map_handle.mapped to NO_MAPPING.
+   However, __nscd_get_mapping assumes the prior value was not NO_MAPPING.
+   Thus we have to acquire the lock to prevent this thread from changing
+   hst_map_handle.mapped to NO_MAPPING while another thread is inside
+    __nscd_get_mapping.  */
+  while (__builtin_expect
+	 (atomic_compare_and_exchange_val_acq (&__hst_map_handle.lock,
+					       1, 0) != 0, 0))
+    {
+      // XXX Best number of rounds?
+      if (__builtin_expect (++cnt > 5, 0))
+	return 0;
+
+      atomic_delay ();
+    }
+
   struct mapped_database *map = __hst_map_handle.mapped;
 
   if (map == NULL
@@ -113,9 +131,14 @@ __nscd_get_nl_timestamp (void)
     map = __nscd_get_mapping (GETFDHST, "hosts", &__hst_map_handle.mapped);
 
   if (map == NO_MAPPING)
-    return 0;
+    retval =  0;
+  else
+    retval =  map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
+
+  /* Release the lock.  */
+  __hst_map_handle.lock = 0;
 
-  return map->head->extra_data[NSCD_HST_IDX_CONF_TIMESTAMP];
+  return retval;
 }