1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <zircon/status.h> 6#include <zircon/syscalls.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <unittest/unittest.h> 11 12// How many times to try a given window size. 13#define NUM_PASSES_PER_WINDOW 100 14 15// qsort comparison function for zx_handle_t. 16static int handle_cmp(const void* left, const void* right) { 17 return *(const zx_handle_t*)left - *(const zx_handle_t*)right; 18} 19 20// Prints a message and exits the process with a non-zero status. 21// This will stop any further tests in this file from running. 22#define FATALF(str, x...) \ 23 do { \ 24 unittest_printf_critical( \ 25 "\nFATAL:%s:%d: " str, __FILE__, __LINE__, ##x); \ 26 exit(-2); \ 27 } while (false) 28 29// Creates/closes |window_size| handles as quickly as possible and looks 30// for aliases. Returns true if any aliases were found. 31static bool find_handle_value_aliases(const size_t window_size) { 32 zx_handle_t event; 33 zx_status_t s = zx_event_create(0, &event); 34 if (s != ZX_OK) { 35 FATALF("Can't create event: %s\n", zx_status_get_string(s)); 36 } 37 zx_handle_t* handle_log = 38 (zx_handle_t*)malloc(window_size * sizeof(zx_handle_t)); 39 40 bool saw_aliases = false; 41 int pass = 0; 42 while (pass++ < NUM_PASSES_PER_WINDOW && !saw_aliases) { 43 // Create and close a bunch of handles as quickly as possible. 44 memset(handle_log, 0, window_size * sizeof(zx_handle_t)); 45 for (size_t i = 0; i < window_size; i++) { 46 s = zx_handle_duplicate(event, ZX_RIGHT_SAME_RIGHTS, &handle_log[i]); 47 if (s != ZX_OK) { 48 FATALF("[i == %zd] Can't duplicate event: %s\n", 49 i, zx_status_get_string(s)); 50 } 51 if (handle_log[i] <= 0) { 52 FATALF("[i == %zd] Got bad handle %d\n", i, handle_log[i]); 53 } 54 s = zx_handle_close(handle_log[i]); 55 if (s != ZX_OK) { 56 FATALF("[i == %zd] Can't close handle %d: %s\n", 57 i, handle_log[i], zx_status_get_string(s)); 58 } 59 } 60 61 // Look for any aliases. 62 qsort(handle_log, window_size, sizeof(zx_handle_t), handle_cmp); 63 for (size_t i = 1; i < window_size; i++) { 64 if (handle_log[i] == handle_log[i - 1]) { 65 saw_aliases = true; 66 break; 67 } 68 } 69 } 70 71 free(handle_log); 72 zx_handle_close(event); 73 return saw_aliases; 74} 75 76// Searches for the largest window size that doesn't contain 77// handle value aliases. 78static size_t find_handle_alias_window_size(void) { 79 size_t min_fail = 8192; // "fail" meaning "aliases found" 80 size_t max_pass = 1; // "pass" meaning "no aliases found" 81 while (true) { 82 size_t cur_win = (min_fail - 1 + max_pass + 1) / 2; 83 if (cur_win <= max_pass) { 84 return max_pass; 85 } 86 unittest_printf(" window_size %4zd: ", cur_win); 87 fflush(stdout); 88 if (find_handle_value_aliases(cur_win)) { 89 unittest_printf("ALIAS FOUND\n"); 90 min_fail = cur_win; 91 } else { 92 unittest_printf("no alias found\n"); 93 max_pass = cur_win; 94 } 95 } 96} 97 98// This test isn't deterministic, because its behavior depends on the 99// system-wide usage of the kernel's handle arena. 100// It can produce a false failure if someone else consumes/recycles handle 101// slots in the same way this test does. 102// It can produce a false success if someone else consumes and holds onto 103// handle slots, so that this test never gets a chance to see the same 104// slot each time. 105static bool handle_value_alias_test(void) { 106 BEGIN_TEST; 107 unittest_printf("\n"); 108 size_t window_size = find_handle_alias_window_size(); 109 unittest_printf( 110 " Converged at %zd (largest window_size with no aliases)\n", 111 window_size); 112 113 // The handle table as of 13 Mar 2017 should let us re-use a handle 114 // slot 4096 times before producing an alias. Use half that as our 115 // target to bias the test away from false failures. 116 const size_t min_window_size = 2048; 117 EXPECT_GE(window_size, min_window_size, ""); 118 END_TEST; 119} 120 121BEGIN_TEST_CASE(handle_reuse) 122RUN_TEST_LARGE(handle_value_alias_test); // Potentially flaky => large test 123END_TEST_CASE(handle_reuse) 124 125int main(int argc, char** argv) { 126 bool success = unittest_run_all_tests(argc, argv); 127 return success ? 0 : -1; 128} 129