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