1 #include "test/jemalloc_test.h" 2 #include "test/san.h" 3 4 const char *malloc_conf = 5 "tcache_ncached_max:256-1024:1001|2048-2048:0|8192-8192:1,tcache_max:4096"; 6 extern void tcache_bin_info_compute( 7 cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX]); 8 extern bool tcache_get_default_ncached_max_set(szind_t ind); 9 extern const cache_bin_info_t *tcache_get_default_ncached_max(void); 10 11 static void 12 check_bins_info(cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX]) { 13 size_t mib_get[4], mib_get_len; 14 mib_get_len = sizeof(mib_get) / sizeof(size_t); 15 const char *get_name = "thread.tcache.ncached_max.read_sizeclass"; 16 size_t ncached_max; 17 size_t sz = sizeof(size_t); 18 expect_d_eq(mallctlnametomib(get_name, mib_get, &mib_get_len), 0, 19 "Unexpected mallctlnametomib() failure"); 20 21 for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) { 22 size_t bin_size = sz_index2size(i); 23 expect_d_eq( 24 mallctlbymib(mib_get, mib_get_len, (void *)&ncached_max, 25 &sz, (void *)&bin_size, sizeof(size_t)), 26 0, "Unexpected mallctlbymib() failure"); 27 expect_zu_eq(ncached_max, tcache_bin_info[i].ncached_max, 28 "Unexpected ncached_max for bin %d", i); 29 /* Check ncached_max returned under a non-bin size. */ 30 bin_size--; 31 size_t temp_ncached_max = 0; 32 expect_d_eq(mallctlbymib(mib_get, mib_get_len, 33 (void *)&temp_ncached_max, &sz, 34 (void *)&bin_size, sizeof(size_t)), 35 0, "Unexpected mallctlbymib() failure"); 36 expect_zu_eq(temp_ncached_max, ncached_max, 37 "Unexpected ncached_max for inaccurate bin size."); 38 } 39 } 40 41 static void * 42 ncached_max_check(void *args) { 43 cache_bin_info_t tcache_bin_info[TCACHE_NBINS_MAX]; 44 cache_bin_info_t tcache_bin_info_backup[TCACHE_NBINS_MAX]; 45 tsd_t *tsd = tsd_fetch(); 46 tcache_t *tcache = tsd_tcachep_get(tsd); 47 assert(tcache != NULL); 48 tcache_slow_t *tcache_slow = tcache->tcache_slow; 49 50 tcache_bin_info_compute(tcache_bin_info); 51 memcpy( 52 tcache_bin_info_backup, tcache_bin_info, sizeof(tcache_bin_info)); 53 /* Check ncached_max set by malloc_conf. */ 54 for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) { 55 bool first_range = (i >= sz_size2index(256) 56 && i <= sz_size2index(1024)); 57 bool second_range = (i == sz_size2index(2048)); 58 bool third_range = (i == sz_size2index(8192)); 59 cache_bin_sz_t target_ncached_max = 0; 60 if (first_range || second_range || third_range) { 61 target_ncached_max = first_range 62 ? 1001 63 : (second_range ? 0 : 1); 64 expect_true(tcache_get_default_ncached_max_set(i), 65 "Unexpected state for bin %u", i); 66 expect_zu_eq(target_ncached_max, 67 tcache_bin_info[i].ncached_max, 68 "Unexpected generated ncached_max for bin %u", i); 69 expect_zu_eq(target_ncached_max, 70 tcache_get_default_ncached_max()[i].ncached_max, 71 "Unexpected pre-set ncached_max for bin %u", i); 72 } else { 73 expect_false(tcache_get_default_ncached_max_set(i), 74 "Unexpected state for bin %u", i); 75 } 76 } 77 unsigned nbins = tcache_nbins_get(tcache_slow); 78 for (szind_t i = nbins; i < TCACHE_NBINS_MAX; i++) { 79 cache_bin_info_init(&tcache_bin_info[i], 0); 80 } 81 /* Check the initial bin settings. */ 82 check_bins_info(tcache_bin_info); 83 84 size_t mib_set[4], mib_set_len; 85 mib_set_len = sizeof(mib_set) / sizeof(size_t); 86 const char *set_name = "thread.tcache.ncached_max.write"; 87 expect_d_eq(mallctlnametomib(set_name, mib_set, &mib_set_len), 0, 88 "Unexpected mallctlnametomib() failure"); 89 90 /* Test the ncached_max set with tcache on. */ 91 char inputs[100] = "8-128:1|160-160:11|170-320:22|224-8388609:0"; 92 char *inputp = inputs; 93 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 94 (void *)&inputp, sizeof(char *)), 95 0, "Unexpected mallctlbymib() failure"); 96 for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) { 97 if (i >= sz_size2index(8) && i <= sz_size2index(128)) { 98 cache_bin_info_init(&tcache_bin_info[i], 1); 99 } 100 if (i == sz_size2index(160)) { 101 cache_bin_info_init(&tcache_bin_info[i], 11); 102 } 103 if (i >= sz_size2index(170) && i <= sz_size2index(320)) { 104 cache_bin_info_init(&tcache_bin_info[i], 22); 105 } 106 if (i >= sz_size2index(224)) { 107 cache_bin_info_init(&tcache_bin_info[i], 0); 108 } 109 if (i >= nbins) { 110 cache_bin_info_init(&tcache_bin_info[i], 0); 111 } 112 } 113 check_bins_info(tcache_bin_info); 114 115 /* 116 * Close the tcache and set ncached_max of some bins. It will be 117 * set properly but thread.tcache.ncached_max.read still returns 0 118 * since the bin is not available yet. After enabling the tcache, 119 * the new setting will not be carried on. Instead, the default 120 * settings will be applied. 121 */ 122 bool e0 = false, e1; 123 size_t bool_sz = sizeof(bool); 124 expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e1, &bool_sz, 125 (void *)&e0, bool_sz), 126 0, "Unexpected mallctl() error"); 127 expect_true(e1, "Unexpected previous tcache state"); 128 strcpy(inputs, "0-112:8"); 129 /* Setting returns ENOENT when the tcache is disabled. */ 130 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 131 (void *)&inputp, sizeof(char *)), 132 ENOENT, "Unexpected mallctlbymib() failure"); 133 /* All ncached_max should return 0 once tcache is disabled. */ 134 for (szind_t i = 0; i < TCACHE_NBINS_MAX; i++) { 135 cache_bin_info_init(&tcache_bin_info[i], 0); 136 } 137 check_bins_info(tcache_bin_info); 138 139 e0 = true; 140 expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e1, &bool_sz, 141 (void *)&e0, bool_sz), 142 0, "Unexpected mallctl() error"); 143 expect_false(e1, "Unexpected previous tcache state"); 144 memcpy(tcache_bin_info, tcache_bin_info_backup, 145 sizeof(tcache_bin_info_backup)); 146 for (szind_t i = tcache_nbins_get(tcache_slow); i < TCACHE_NBINS_MAX; 147 i++) { 148 cache_bin_info_init(&tcache_bin_info[i], 0); 149 } 150 check_bins_info(tcache_bin_info); 151 152 /* 153 * Set ncached_max of bins not enabled yet. Then, enable them by 154 * resetting tcache_max. The ncached_max changes should stay. 155 */ 156 size_t tcache_max = 1024; 157 assert_d_eq(mallctl("thread.tcache.max", NULL, NULL, 158 (void *)&tcache_max, sizeof(size_t)), 159 .0, "Unexpected.mallctl().failure"); 160 for (szind_t i = sz_size2index(1024) + 1; i < TCACHE_NBINS_MAX; i++) { 161 cache_bin_info_init(&tcache_bin_info[i], 0); 162 } 163 strcpy(inputs, "2048-6144:123"); 164 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 165 (void *)&inputp, sizeof(char *)), 166 0, "Unexpected mallctlbymib() failure"); 167 check_bins_info(tcache_bin_info); 168 169 tcache_max = 6144; 170 assert_d_eq(mallctl("thread.tcache.max", NULL, NULL, 171 (void *)&tcache_max, sizeof(size_t)), 172 .0, "Unexpected.mallctl().failure"); 173 memcpy(tcache_bin_info, tcache_bin_info_backup, 174 sizeof(tcache_bin_info_backup)); 175 for (szind_t i = sz_size2index(2048); i < TCACHE_NBINS_MAX; i++) { 176 if (i <= sz_size2index(6144)) { 177 cache_bin_info_init(&tcache_bin_info[i], 123); 178 } else if (i > sz_size2index(6144)) { 179 cache_bin_info_init(&tcache_bin_info[i], 0); 180 } 181 } 182 check_bins_info(tcache_bin_info); 183 184 /* Test an empty input, it should do nothing. */ 185 strcpy(inputs, ""); 186 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 187 (void *)&inputp, sizeof(char *)), 188 0, "Unexpected mallctlbymib() failure"); 189 check_bins_info(tcache_bin_info); 190 191 /* Test a half-done string, it should return EINVAL and do nothing. */ 192 strcpy(inputs, "4-1024:7|256-1024"); 193 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 194 (void *)&inputp, sizeof(char *)), 195 EINVAL, "Unexpected mallctlbymib() failure"); 196 check_bins_info(tcache_bin_info); 197 198 /* 199 * Test an invalid string with start size larger than end size. It 200 * should return success but do nothing. 201 */ 202 strcpy(inputs, "1024-256:7"); 203 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 204 (void *)&inputp, sizeof(char *)), 205 0, "Unexpected mallctlbymib() failure"); 206 check_bins_info(tcache_bin_info); 207 208 /* 209 * Test a string exceeding the length limit, it should return EINVAL 210 * and do nothing. 211 */ 212 char *long_inputs = (char *)malloc(10000 * sizeof(char)); 213 expect_true(long_inputs != NULL, "Unexpected allocation failure."); 214 for (int i = 0; i < 200; i++) { 215 memcpy(long_inputs + i * 9, "4-1024:3|", 9); 216 } 217 memcpy(long_inputs + 200 * 9, "4-1024:3", 8); 218 long_inputs[200 * 9 + 8] = '\0'; 219 inputp = long_inputs; 220 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 221 (void *)&inputp, sizeof(char *)), 222 EINVAL, "Unexpected mallctlbymib() failure"); 223 check_bins_info(tcache_bin_info); 224 free(long_inputs); 225 226 /* 227 * Test a string with invalid characters, it should return EINVAL 228 * and do nothing. 229 */ 230 strcpy(inputs, "k8-1024:77p"); 231 inputp = inputs; 232 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 233 (void *)&inputp, sizeof(char *)), 234 EINVAL, "Unexpected mallctlbymib() failure"); 235 check_bins_info(tcache_bin_info); 236 237 /* Test large ncached_max, it should return success but capped. */ 238 strcpy(inputs, "1024-1024:65540"); 239 expect_d_eq(mallctlbymib(mib_set, mib_set_len, NULL, NULL, 240 (void *)&inputp, sizeof(char *)), 241 0, "Unexpected mallctlbymib() failure"); 242 cache_bin_info_init( 243 &tcache_bin_info[sz_size2index(1024)], CACHE_BIN_NCACHED_MAX); 244 check_bins_info(tcache_bin_info); 245 246 return NULL; 247 } 248 249 TEST_BEGIN(test_ncached_max) { 250 test_skip_if(!config_stats); 251 test_skip_if(!opt_tcache); 252 test_skip_if(san_uaf_detection_enabled()); 253 /* TODO: change nthreads to 8 to reduce CI loads. */ 254 unsigned nthreads = 108; 255 VARIABLE_ARRAY(thd_t, threads, nthreads); 256 for (unsigned i = 0; i < nthreads; i++) { 257 thd_create(&threads[i], ncached_max_check, NULL); 258 } 259 for (unsigned i = 0; i < nthreads; i++) { 260 thd_join(threads[i], NULL); 261 } 262 } 263 TEST_END 264 265 int 266 main(void) { 267 return test(test_ncached_max); 268 } 269