1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 #undef NDEBUG 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netdb.h> 6 #include <arpa/inet.h> 7 #include <netinet/in.h> 8 #include <netinet/tcp.h> 9 #include <signal.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <errno.h> 13 #include <assert.h> 14 #include <string.h> 15 #include <inttypes.h> 16 #include <stdbool.h> 17 #include <unistd.h> 18 #include <netinet/in.h> 19 20 #include "protocol_binary.h" 21 #include "config.h" 22 #include "cache.h" 23 #include "util.h" 24 25 enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL }; 26 27 static enum test_return cache_create_test(void) 28 { 29 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*), 30 NULL, NULL); 31 assert(cache != NULL); 32 cache_destroy(cache); 33 return TEST_PASS; 34 } 35 36 const uint64_t constructor_pattern = 0xdeadcafebabebeef; 37 38 static int cache_constructor(void *buffer, void *notused1, int notused2) { 39 uint64_t *ptr = buffer; 40 *ptr = constructor_pattern; 41 return 0; 42 } 43 44 static enum test_return cache_constructor_test(void) 45 { 46 cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t), 47 cache_constructor, NULL); 48 assert(cache != NULL); 49 uint64_t *ptr = cache_alloc(cache); 50 uint64_t pattern = *ptr; 51 cache_free(cache, ptr); 52 cache_destroy(cache); 53 return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL; 54 } 55 56 static int cache_fail_constructor(void *buffer, void *notused1, int notused2) { 57 return 1; 58 } 59 60 static enum test_return cache_fail_constructor_test(void) 61 { 62 enum test_return ret = TEST_PASS; 63 64 cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t), 65 cache_fail_constructor, NULL); 66 assert(cache != NULL); 67 uint64_t *ptr = cache_alloc(cache); 68 if (ptr != NULL) { 69 ret = TEST_FAIL; 70 } 71 cache_destroy(cache); 72 return ret; 73 } 74 75 static void *destruct_data = 0; 76 77 static void cache_destructor(void *buffer, void *notused) { 78 destruct_data = buffer; 79 } 80 81 static enum test_return cache_destructor_test(void) 82 { 83 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*), 84 NULL, cache_destructor); 85 assert(cache != NULL); 86 char *ptr = cache_alloc(cache); 87 cache_free(cache, ptr); 88 cache_destroy(cache); 89 90 return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL; 91 } 92 93 static enum test_return cache_reuse_test(void) 94 { 95 int ii; 96 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*), 97 NULL, NULL); 98 char *ptr = cache_alloc(cache); 99 cache_free(cache, ptr); 100 for (ii = 0; ii < 100; ++ii) { 101 char *p = cache_alloc(cache); 102 assert(p == ptr); 103 cache_free(cache, ptr); 104 } 105 cache_destroy(cache); 106 return TEST_PASS; 107 } 108 109 static enum test_return cache_redzone_test(void) 110 { 111 #ifndef HAVE_UMEM_H 112 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*), 113 NULL, NULL); 114 115 /* Ignore SIGABORT */ 116 struct sigaction old_action; 117 struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0}; 118 sigemptyset(&action.sa_mask); 119 sigaction(SIGABRT, &action, &old_action); 120 121 /* check memory debug.. */ 122 char *p = cache_alloc(cache); 123 char old = *(p - 1); 124 *(p - 1) = 0; 125 cache_free(cache, p); 126 assert(cache_error == -1); 127 *(p - 1) = old; 128 129 p[sizeof(uint32_t)] = 0; 130 cache_free(cache, p); 131 assert(cache_error == 1); 132 133 /* restore signal handler */ 134 sigaction(SIGABRT, &old_action, NULL); 135 136 cache_destroy(cache); 137 138 return TEST_PASS; 139 #else 140 return TEST_SKIP; 141 #endif 142 } 143 144 static enum test_return test_safe_strtoul(void) { 145 uint32_t val; 146 assert(safe_strtoul("123", &val)); 147 assert(val == 123); 148 assert(safe_strtoul("+123", &val)); 149 assert(val == 123); 150 assert(!safe_strtoul("", &val)); // empty 151 assert(!safe_strtoul("123BOGUS", &val)); // non-numeric 152 /* Not sure what it does, but this works with ICC :/ 153 assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range 154 */ 155 156 // extremes: 157 assert(safe_strtoul("4294967295", &val)); // 2**32 - 1 158 assert(val == 4294967295L); 159 /* This actually works on 64-bit ubuntu 160 assert(!safe_strtoul("4294967296", &val)); // 2**32 161 */ 162 assert(!safe_strtoul("-1", &val)); // negative 163 return TEST_PASS; 164 } 165 166 167 static enum test_return test_safe_strtoull(void) { 168 uint64_t val; 169 assert(safe_strtoull("123", &val)); 170 assert(val == 123); 171 assert(safe_strtoull("+123", &val)); 172 assert(val == 123); 173 assert(!safe_strtoull("", &val)); // empty 174 assert(!safe_strtoull("123BOGUS", &val)); // non-numeric 175 assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range 176 177 // extremes: 178 assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1 179 assert(val == 18446744073709551615ULL); 180 assert(!safe_strtoull("18446744073709551616", &val)); // 2**64 181 assert(!safe_strtoull("-1", &val)); // negative 182 return TEST_PASS; 183 } 184 185 static enum test_return test_safe_strtoll(void) { 186 int64_t val; 187 assert(safe_strtoll("123", &val)); 188 assert(val == 123); 189 assert(safe_strtoll("+123", &val)); 190 assert(val == 123); 191 assert(safe_strtoll("-123", &val)); 192 assert(val == -123); 193 assert(!safe_strtoll("", &val)); // empty 194 assert(!safe_strtoll("123BOGUS", &val)); // non-numeric 195 assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range 196 197 // extremes: 198 assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1 199 assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1 200 assert(val == 9223372036854775807LL); 201 /* 202 assert(safe_strtoll("-9223372036854775808", &val)); // -2**63 203 assert(val == -9223372036854775808LL); 204 */ 205 assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1 206 207 // We'll allow space to terminate the string. And leading space. 208 assert(safe_strtoll(" 123 foo", &val)); 209 assert(val == 123); 210 return TEST_PASS; 211 } 212 213 static enum test_return test_safe_strtol(void) { 214 int32_t val; 215 assert(safe_strtol("123", &val)); 216 assert(val == 123); 217 assert(safe_strtol("+123", &val)); 218 assert(val == 123); 219 assert(safe_strtol("-123", &val)); 220 assert(val == -123); 221 assert(!safe_strtol("", &val)); // empty 222 assert(!safe_strtol("123BOGUS", &val)); // non-numeric 223 assert(!safe_strtol("92837498237498237498029383", &val)); // out of range 224 225 // extremes: 226 /* This actually works on 64-bit ubuntu 227 assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0) 228 */ 229 assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1) 230 assert(val == 2147483647L); 231 /* This actually works on 64-bit ubuntu 232 assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1) 233 */ 234 235 // We'll allow space to terminate the string. And leading space. 236 assert(safe_strtol(" 123 foo", &val)); 237 assert(val == 123); 238 return TEST_PASS; 239 } 240 241 /** 242 * Function to start the server and let it listen on a random port 243 * 244 * @param port_out where to store the TCP port number the server is 245 * listening on 246 * @param daemon set to true if you want to run the memcached server 247 * as a daemon process 248 * @return the pid of the memcached server 249 */ 250 static pid_t start_server(in_port_t *port_out, bool daemon) { 251 char environment[80]; 252 snprintf(environment, sizeof(environment), 253 "MEMCACHED_PORT_FILENAME=/tmp/ports.%u", getpid()); 254 char *filename= environment + strlen("MEMCACHED_PORT_FILENAME="); 255 char pid_file[80]; 256 snprintf(pid_file, sizeof(pid_file), "/tmp/pid.%u", getpid()); 257 258 remove(filename); 259 remove(pid_file); 260 261 pid_t pid = fork(); 262 assert(pid != -1); 263 264 if (pid == 0) { 265 /* Child */ 266 char *argv[20]; 267 int arg = 0; 268 putenv(environment); 269 if (!daemon) { 270 argv[arg++] = "./timedrun"; 271 argv[arg++] = "15"; 272 } 273 argv[arg++] = "./memcached-debug"; 274 argv[arg++] = "-p"; 275 argv[arg++] = "-1"; 276 argv[arg++] = "-U"; 277 argv[arg++] = "0"; 278 if (daemon) { 279 argv[arg++] = "-d"; 280 argv[arg++] = "-P"; 281 argv[arg++] = pid_file; 282 } 283 argv[arg++] = NULL; 284 assert(execv(argv[0], argv) != -1); 285 } 286 287 /* Yeah just let us "busy-wait" for the file to be created ;-) */ 288 while (access(filename, F_OK) == -1) { 289 usleep(10); 290 } 291 292 FILE *fp = fopen(filename, "r"); 293 if (fp == NULL) { 294 fprintf(stderr, "Failed to open the file containing port numbers: %s\n", 295 strerror(errno)); 296 assert(false); 297 } 298 299 *port_out = (in_port_t)-1; 300 char buffer[80]; 301 while ((fgets(buffer, sizeof(buffer), fp)) != NULL) { 302 if (strncmp(buffer, "TCP INET: ", 10) == 0) { 303 int32_t val; 304 assert(safe_strtol(buffer + 10, &val)); 305 *port_out = (in_port_t)val; 306 } 307 } 308 fclose(fp); 309 assert(remove(filename) == 0); 310 311 if (daemon) { 312 /* loop and wait for the pid file.. There is a potential race 313 * condition that the server just created the file but isn't 314 * finished writing the content, but I'll take the chance.... 315 */ 316 while (access(pid_file, F_OK) == -1) { 317 usleep(10); 318 } 319 320 fp = fopen(pid_file, "r"); 321 if (fp == NULL) { 322 fprintf(stderr, "Failed to open pid file: %s\n", 323 strerror(errno)); 324 assert(false); 325 } 326 assert(fgets(buffer, sizeof(buffer), fp) != NULL); 327 fclose(fp); 328 329 int32_t val; 330 assert(safe_strtol(buffer, &val)); 331 pid = (pid_t)val; 332 } 333 334 return pid; 335 } 336 337 static enum test_return test_issue_44(void) { 338 in_port_t port; 339 pid_t pid = start_server(&port, true); 340 assert(kill(pid, SIGHUP) == 0); 341 sleep(1); 342 assert(kill(pid, SIGTERM) == 0); 343 344 return TEST_PASS; 345 } 346 347 static struct addrinfo *lookuphost(const char *hostname, in_port_t port) 348 { 349 struct addrinfo *ai = 0; 350 struct addrinfo hints = { .ai_family = AF_UNSPEC, 351 .ai_protocol = IPPROTO_TCP, 352 .ai_socktype = SOCK_STREAM }; 353 char service[NI_MAXSERV]; 354 int error; 355 356 (void)snprintf(service, NI_MAXSERV, "%d", port); 357 if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) { 358 if (error != EAI_SYSTEM) { 359 fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error)); 360 } else { 361 perror("getaddrinfo()"); 362 } 363 } 364 365 return ai; 366 } 367 368 static int connect_server(const char *hostname, in_port_t port) 369 { 370 struct addrinfo *ai = lookuphost(hostname, port); 371 int sock = -1; 372 if (ai != NULL) { 373 if ((sock = socket(ai->ai_family, ai->ai_socktype, 374 ai->ai_protocol)) != -1) { 375 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 376 fprintf(stderr, "Failed to connect socket: %s\n", 377 strerror(errno)); 378 close(sock); 379 sock = -1; 380 } 381 } else { 382 fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); 383 } 384 385 freeaddrinfo(ai); 386 } 387 return sock; 388 } 389 390 391 static enum test_return test_issue_72(void) { 392 in_port_t port; 393 pid_t pid = start_server(&port, false); 394 int sock = connect_server("127.0.0.1", port); 395 assert(sock != -1); 396 397 char data[sizeof(protocol_binary_request_set) + 2048] = { 0 }; 398 protocol_binary_request_set *request = (protocol_binary_request_set*)data; 399 request->message.header.request.magic = PROTOCOL_BINARY_REQ; 400 request->message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; 401 uint16_t keylen = 2048; 402 request->message.header.request.keylen = htons(keylen); 403 request->message.header.request.extlen = 8; 404 request->message.header.request.bodylen = htonl(keylen + 8); 405 406 assert(write(sock, data, 2000) == 2000); 407 usleep(250); 408 assert(write(sock, data, sizeof(data) - 2000) == sizeof(data) - 2000); 409 410 protocol_binary_response_set response; 411 assert(read(sock, &response, sizeof(response)) == sizeof(response)); 412 assert(response.message.header.response.magic == PROTOCOL_BINARY_RES); 413 assert(response.message.header.response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS); 414 close(sock); 415 assert(kill(pid, SIGTERM) == 0); 416 return TEST_PASS; 417 } 418 419 typedef enum test_return (*TEST_FUNC)(void); 420 struct testcase { 421 const char *description; 422 TEST_FUNC function; 423 }; 424 425 struct testcase testcases[] = { 426 { "cache_create", cache_create_test }, 427 { "cache_constructor", cache_constructor_test }, 428 { "cache_constructor_fail", cache_fail_constructor_test }, 429 { "cache_destructor", cache_destructor_test }, 430 { "cache_reuse", cache_reuse_test }, 431 { "cache_redzone", cache_redzone_test }, 432 { "strtol", test_safe_strtol }, 433 { "strtoll", test_safe_strtoll }, 434 { "strtoul", test_safe_strtoul }, 435 { "strtoull", test_safe_strtoull }, 436 { "issue_44", test_issue_44 }, 437 { "issue_72", test_issue_72 }, 438 { NULL, NULL } 439 }; 440 441 int main(int argc, char **argv) 442 { 443 int exitcode = 0; 444 int ii = 0, num_cases = 0; 445 446 for (num_cases = 0; testcases[num_cases].description; num_cases++) { 447 /* Just counting */ 448 } 449 450 printf("1..%d\n", num_cases); 451 452 for (ii = 0; testcases[ii].description != NULL; ++ii) { 453 fflush(stdout); 454 alarm(60); 455 enum test_return ret = testcases[ii].function(); 456 if (ret == TEST_SKIP) { 457 fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description); 458 } else if (ret == TEST_PASS) { 459 fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description); 460 } else { 461 fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description); 462 exitcode = 1; 463 } 464 fflush(stdout); 465 } 466 467 return exitcode; 468 } 469