1 // Copyright 2018 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 "test_library.h"
6 
7 #define BORINGSSL_NO_CXX
8 #include <openssl/sha.h>
9 
10 #include <regex>
11 
12 #include <unittest/unittest.h>
13 
14 namespace {
15 
16 // Some of the tests below required generating strings offline until their
17 // SHA-256 sums had particular properties.  The code used to calculate a
18 // collision in the first 32 bits is included below, in case it proves useful in
19 // the future.
20 
21 // #include <climits>
22 // #include <iostream>
23 // #include <openssl/sha.h>
24 // #include <stdio.h>
25 // #include <string.h>
26 // #include <string>
27 
28 // std::string next_name(std::string& curr) {
29 //     std::string next = curr;
30 //     int i = next.length() - 1;
31 //     for (; i >= 0; i--) {
32 //         if (next[i] < 'z') {
33 //             next[i]++;
34 //             break;
35 //         } else {
36 //             next[i] = 'a';
37 //         }
38 //     }
39 //     if (i == -1) {
40 //         next = 'a' + next;
41 //     }
42 //     return next;
43 // }
44 
45 // int main(int argc, char** argv) {
46 //     uint8_t* bitvec = new uint8_t[UINT_MAX];
47 //     std::string base("a.b/");
48 //     memset(bitvec, 0, UINT_MAX);
49 //     bool keep_going = true;
50 //     std::string curr_name = "a";
51 //     uint32_t ordinal = 0;
52 //     uint64_t iterations = 0;
53 //     do {
54 //         uint8_t digest[SHA256_DIGEST_LENGTH];
55 //         curr_name = next_name(curr_name);
56 //         std::string full_name = base + curr_name;
57 //         SHA256(reinterpret_cast<const uint8_t*>(full_name.data()), full_name.size(), digest);
58 //         ordinal = *(reinterpret_cast<uint32_t*>(digest)) & 0x7fffffff;
59 //         keep_going = bitvec[ordinal] == 0;
60 //         bitvec[ordinal] = 1;
61 //     } while (keep_going);
62 //     fprintf(stderr, "ordinal = %d name = %s\n", ordinal, curr_name.c_str());
63 // }
64 
ordinal_cannot_be_zero()65 bool ordinal_cannot_be_zero() {
66     BEGIN_TEST;
67 
68     TestLibrary library(R"FIDL(
69 library a;
70 
71 // The first 32 bits of the SHA256 hash of a.b/fcuvhse are 0.
72 interface b {
73     fcuvhse() -> (int64 i);
74 };
75 
76 )FIDL");
77     ASSERT_FALSE(library.Compile());
78     const auto& errors = library.errors();
79     ASSERT_EQ(1, errors.size(), "Ordinal value 0 should be disallowed");
80 
81     END_TEST;
82 }
83 
test_clashing_ordinal_values()84 bool test_clashing_ordinal_values() {
85     BEGIN_TEST;
86 
87     TestLibrary library(R"FIDL(
88 library a;
89 
90 // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are
91 // the same.  This will trigger an error when ordinals are generated.
92 interface b {
93     ljz(string s, bool b) -> (int32 i);
94     clgn(string s) -> (handle<channel> r);
95 };
96 
97 )FIDL");
98     ASSERT_FALSE(library.Compile());
99     const auto& errors = library.errors();
100     ASSERT_EQ(1, errors.size());
101 
102     // The FTP requires the error message as follows
103     const std::regex pattern(R"REGEX(\[\s*Selector\s*=\s*"(ljz|clgn)_"\s*\])REGEX");
104     std::smatch sm;
105     ASSERT_TRUE(std::regex_search(errors[0], sm, pattern),
106                 ("Selector pattern not found in error: " + errors[0]).c_str());
107 
108     END_TEST;
109 }
110 
test_clashing_ordinal_values_with_attribute()111 bool test_clashing_ordinal_values_with_attribute() {
112     BEGIN_TEST;
113 
114     TestLibrary library(R"FIDL(
115 library a;
116 
117 // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are
118 // the same.  This will trigger an error when ordinals are generated.
119 interface b {
120     [Selector = "ljz"]
121     foo(string s, bool b) -> (int32 i);
122     [Selector = "clgn"]
123     bar(string s) -> (handle<channel> r);
124 };
125 
126 )FIDL");
127     ASSERT_FALSE(library.Compile());
128     const auto& errors = library.errors();
129     ASSERT_EQ(1, errors.size());
130 
131     // The FTP requires the error message as follows
132     const std::regex pattern(R"REGEX(\[\s*Selector\s*=\s*"(ljz|clgn)_"\s*\])REGEX");
133     std::smatch sm;
134     ASSERT_TRUE(std::regex_search(errors[0], sm, pattern),
135                 ("Selector pattern not found in error: " + errors[0]).c_str());
136 
137     END_TEST;
138 }
139 
attribute_resolves_clashes()140 bool attribute_resolves_clashes() {
141     BEGIN_TEST;
142 
143     TestLibrary library(R"FIDL(
144 library a;
145 
146 // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are
147 // the same.  This will trigger an error when ordinals are generated.
148 interface b {
149     [Selector = "ljz_"]
150     ljz(string s, bool b) -> (int32 i);
151     clgn(string s) -> (handle<channel> r);
152 };
153 
154 )FIDL");
155     ASSERT_TRUE(library.Compile());
156 
157     END_TEST;
158 }
159 
test_ordinal_value_is_sha256()160 bool test_ordinal_value_is_sha256() {
161     BEGIN_TEST;
162     TestLibrary library(R"FIDL(
163 library a;
164 
165 interface b {
166     potato(string s, bool b) -> (int32 i);
167 };
168 )FIDL");
169     ASSERT_TRUE(library.Compile());
170 
171     const char hash_name[] = "a.b/potato";
172     uint8_t digest[SHA256_DIGEST_LENGTH];
173     SHA256(reinterpret_cast<const uint8_t*>(hash_name), strlen(hash_name), digest);
174     uint32_t expected_hash = *(reinterpret_cast<uint32_t*>(digest)) & 0x7fffffff;
175 
176     const fidl::flat::Interface* iface = library.LookupInterface("b");
177     uint32_t actual_hash = iface->methods[0].ordinal->value;
178     ASSERT_EQ(actual_hash, expected_hash, "Expected hash is not correct");
179     END_TEST;
180 }
181 
182 } // namespace
183 
184 BEGIN_TEST_CASE(ordinals_test);
185 RUN_TEST(ordinal_cannot_be_zero);
186 RUN_TEST(test_clashing_ordinal_values);
187 RUN_TEST(test_clashing_ordinal_values_with_attribute);
188 RUN_TEST(attribute_resolves_clashes);
189 RUN_TEST(test_ordinal_value_is_sha256);
190 END_TEST_CASE(ordinals_test);
191