@@ -405,6 +405,7 @@ static dsa_area *create_internal(void *place, size_t size,
405
405
static dsa_area * attach_internal (void * place , dsm_segment * segment ,
406
406
dsa_handle handle );
407
407
static void check_for_freed_segments (dsa_area * area );
408
+ static void check_for_freed_segments_locked (dsa_area * area );
408
409
409
410
/*
410
411
* Create a new shared area in a new DSM segment. Further DSM segments will
@@ -1065,6 +1066,7 @@ dsa_dump(dsa_area *area)
1065
1066
*/
1066
1067
1067
1068
LWLockAcquire (DSA_AREA_LOCK (area ), LW_EXCLUSIVE );
1069
+ check_for_freed_segments_locked (area );
1068
1070
fprintf (stderr , "dsa_area handle %x:\n" , area -> control -> handle );
1069
1071
fprintf (stderr , " max_total_segment_size: %zu\n" ,
1070
1072
area -> control -> max_total_segment_size );
@@ -1762,6 +1764,23 @@ get_segment_by_index(dsa_area *area, dsa_segment_index index)
1762
1764
(DSA_SEGMENT_HEADER_MAGIC ^ area -> control -> handle ^ index ));
1763
1765
}
1764
1766
1767
+ /*
1768
+ * Callers of dsa_get_address() and dsa_free() don't hold the area lock,
1769
+ * but it's a bug in the calling code and undefined behavior if the
1770
+ * address is not live (ie if the segment might possibly have been freed,
1771
+ * they're trying to use a dangling pointer).
1772
+ *
1773
+ * For dsa.c code that holds the area lock to manipulate segment_bins
1774
+ * lists, it would be a bug if we ever reach a freed segment here. After
1775
+ * it's marked as freed, the only thing any backend should do with it is
1776
+ * unmap it, and it should always have done that in
1777
+ * check_for_freed_segments_locked() before arriving here to resolve an
1778
+ * index to a segment_map.
1779
+ *
1780
+ * Either way we can assert that we aren't returning a freed segment.
1781
+ */
1782
+ Assert (!area -> segment_maps [index ].header -> freed );
1783
+
1765
1784
return & area -> segment_maps [index ];
1766
1785
}
1767
1786
@@ -1778,8 +1797,6 @@ destroy_superblock(dsa_area *area, dsa_pointer span_pointer)
1778
1797
int size_class = span -> size_class ;
1779
1798
dsa_segment_map * segment_map ;
1780
1799
1781
- segment_map =
1782
- get_segment_by_index (area , DSA_EXTRACT_SEGMENT_NUMBER (span -> start ));
1783
1800
1784
1801
/* Remove it from its fullness class list. */
1785
1802
unlink_span (area , span );
@@ -1790,6 +1807,9 @@ destroy_superblock(dsa_area *area, dsa_pointer span_pointer)
1790
1807
* could deadlock.
1791
1808
*/
1792
1809
LWLockAcquire (DSA_AREA_LOCK (area ), LW_EXCLUSIVE );
1810
+ check_for_freed_segments_locked (area );
1811
+ segment_map =
1812
+ get_segment_by_index (area , DSA_EXTRACT_SEGMENT_NUMBER (span -> start ));
1793
1813
FreePageManagerPut (segment_map -> fpm ,
1794
1814
DSA_EXTRACT_OFFSET (span -> start ) / FPM_PAGE_SIZE ,
1795
1815
span -> npages );
@@ -1944,6 +1964,7 @@ get_best_segment(dsa_area *area, Size npages)
1944
1964
Size bin ;
1945
1965
1946
1966
Assert (LWLockHeldByMe (DSA_AREA_LOCK (area )));
1967
+ check_for_freed_segments_locked (area );
1947
1968
1948
1969
/*
1949
1970
* Start searching from the first bin that *might* have enough contiguous
@@ -2220,10 +2241,30 @@ check_for_freed_segments(dsa_area *area)
2220
2241
freed_segment_counter = area -> control -> freed_segment_counter ;
2221
2242
if (unlikely (area -> freed_segment_counter != freed_segment_counter ))
2222
2243
{
2223
- int i ;
2224
-
2225
2244
/* Check all currently mapped segments to find what's been freed. */
2226
2245
LWLockAcquire (DSA_AREA_LOCK (area ), LW_EXCLUSIVE );
2246
+ check_for_freed_segments_locked (area );
2247
+ LWLockRelease (DSA_AREA_LOCK (area ));
2248
+ }
2249
+ }
2250
+
2251
+ /*
2252
+ * Workhorse for check_for_free_segments(), and also used directly in path
2253
+ * where the area lock is already held. This should be called after acquiring
2254
+ * the lock but before looking up any segment by index number, to make sure we
2255
+ * unmap any stale segments that might have previously had the same index as a
2256
+ * current segment.
2257
+ */
2258
+ static void
2259
+ check_for_freed_segments_locked (dsa_area * area )
2260
+ {
2261
+ Size freed_segment_counter ;
2262
+ int i ;
2263
+
2264
+ Assert (LWLockHeldByMe (DSA_AREA_LOCK (area )));
2265
+ freed_segment_counter = area -> control -> freed_segment_counter ;
2266
+ if (unlikely (area -> freed_segment_counter != freed_segment_counter ))
2267
+ {
2227
2268
for (i = 0 ; i <= area -> high_segment_index ; ++ i )
2228
2269
{
2229
2270
if (area -> segment_maps [i ].header != NULL &&
@@ -2235,7 +2276,6 @@ check_for_freed_segments(dsa_area *area)
2235
2276
area -> segment_maps [i ].mapped_address = NULL ;
2236
2277
}
2237
2278
}
2238
- LWLockRelease (DSA_AREA_LOCK (area ));
2239
2279
area -> freed_segment_counter = freed_segment_counter ;
2240
2280
}
2241
2281
}
0 commit comments