Home | History | Annotate | Line # | Download | only in unit
      1 #include "test/jemalloc_test.h"
      2 
      3 /*
      4  * Test that large_ralloc_no_move causes a failure (returns true) when
      5  * in-place extent expansion cannot succeed for either usize_max or
      6  * usize_min.
      7  *
      8  * A previous bug omitted the ! negation on the second extent expansion
      9  * attempt (usize_min fallback), causing false success (return false) when
     10  * the expansion actually failed.
     11  */
     12 TEST_BEGIN(test_large_ralloc_no_move_expand_fail) {
     13 	/*
     14 	 * Allocate two adjacent large objects in the same arena to block
     15 	 * in-place expansion of the first one.
     16 	 */
     17 	unsigned arena_ind;
     18 	size_t   sz = sizeof(arena_ind);
     19 	expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
     20 	    0, "Unexpected mallctl() failure");
     21 
     22 	int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
     23 
     24 	size_t large_sz = SC_LARGE_MINCLASS;
     25 	/* Allocate several blocks to prevent expansion of the first. */
     26 	void *blocks[8];
     27 	for (size_t i = 0; i < ARRAY_SIZE(blocks); i++) {
     28 		blocks[i] = mallocx(large_sz, flags);
     29 		expect_ptr_not_null(blocks[i], "Unexpected mallocx() failure");
     30 	}
     31 
     32 	/*
     33 	 * Try to expand blocks[0] in place. Use usize_min < usize_max to
     34 	 * exercise the fallback path.
     35 	 */
     36 	tsd_t   *tsd = tsd_fetch();
     37 	edata_t *edata = emap_edata_lookup(
     38 	    tsd_tsdn(tsd), &arena_emap_global, blocks[0]);
     39 	expect_ptr_not_null(edata, "Unexpected edata lookup failure");
     40 
     41 	size_t oldusize = edata_usize_get(edata);
     42 	size_t usize_min = sz_s2u(oldusize + 1);
     43 	size_t usize_max = sz_s2u(oldusize * 2);
     44 
     45 	/* Ensure min and max are in different size classes. */
     46 	if (usize_min == usize_max) {
     47 		usize_max = sz_s2u(usize_min + 1);
     48 	}
     49 
     50 	bool ret = large_ralloc_no_move(
     51 	    tsd_tsdn(tsd), edata, usize_min, usize_max, false);
     52 
     53 	/*
     54 	 * With adjacent allocations blocking expansion, this should fail.
     55 	 * The bug caused ret == false (success) even when expansion failed.
     56 	 */
     57 	if (!ret) {
     58 		/*
     59 		 * Expansion might actually succeed if adjacent memory
     60 		 * is free.  Verify the size actually changed.
     61 		 */
     62 		size_t newusize = edata_usize_get(edata);
     63 		expect_zu_ge(newusize, usize_min,
     64 		    "Expansion reported success but size didn't change");
     65 	}
     66 
     67 	for (size_t i = 0; i < ARRAY_SIZE(blocks); i++) {
     68 		dallocx(blocks[i], flags);
     69 	}
     70 }
     71 TEST_END
     72 
     73 int
     74 main(void) {
     75 	return test_no_reentrancy(test_large_ralloc_no_move_expand_fail);
     76 }
     77