| // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "test_library.h" | 
 |  | 
 | #define BORINGSSL_NO_CXX | 
 | #include <cinttypes> | 
 | #include <regex> | 
 |  | 
 | #include <openssl/sha.h> | 
 | #include <unittest/unittest.h> | 
 |  | 
 | namespace { | 
 |  | 
 | // Some of the tests below required generating strings offline until their | 
 | // SHA-256 sums had particular properties.  The code used to calculate a | 
 | // collision in the first 32 bits is included below, in case it proves useful in | 
 | // the future. | 
 |  | 
 | // #include <climits> | 
 | // #include <iostream> | 
 | // #include <openssl/sha.h> | 
 | // #include <stdio.h> | 
 | // #include <string.h> | 
 | // #include <string> | 
 |  | 
 | // std::string next_name(std::string& curr) { | 
 | //     std::string next = curr; | 
 | //     int i = next.length() - 1; | 
 | //     for (; i >= 0; i--) { | 
 | //         if (next[i] < 'z') { | 
 | //             next[i]++; | 
 | //             break; | 
 | //         } else { | 
 | //             next[i] = 'a'; | 
 | //         } | 
 | //     } | 
 | //     if (i == -1) { | 
 | //         next = 'a' + next; | 
 | //     } | 
 | //     return next; | 
 | // } | 
 |  | 
 | // int main(int argc, char** argv) { | 
 | //     uint8_t* bitvec = new uint8_t[UINT_MAX]; | 
 | //     std::string base("a.b/"); | 
 | //     memset(bitvec, 0, UINT_MAX); | 
 | //     bool keep_going = true; | 
 | //     std::string curr_name = "a"; | 
 | //     uint32_t ordinal = 0; | 
 | //     uint64_t iterations = 0; | 
 | //     do { | 
 | //         uint8_t digest[SHA256_DIGEST_LENGTH]; | 
 | //         curr_name = next_name(curr_name); | 
 | //         std::string full_name = base + curr_name; | 
 | //         SHA256(reinterpret_cast<const uint8_t*>(full_name.data()), full_name.size(), digest); | 
 | //         ordinal = *(reinterpret_cast<uint32_t*>(digest)) & 0x7fffffff; | 
 | //         keep_going = bitvec[ordinal] == 0; | 
 | //         bitvec[ordinal] = 1; | 
 | //     } while (keep_going); | 
 | //     fprintf(stderr, "ordinal = %d name = %s\n", ordinal, curr_name.c_str()); | 
 | // } | 
 |  | 
 | bool ordinal_cannot_be_zero() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   TestLibrary library(R"FIDL( | 
 | library a; | 
 |  | 
 | // The first 32 bits of the SHA256 hash of a.b/fcuvhse are 0. | 
 | protocol b { | 
 |     fcuvhse() -> (int64 i); | 
 | }; | 
 |  | 
 | )FIDL"); | 
 |   ASSERT_FALSE(library.Compile()); | 
 |   const auto& errors = library.errors(); | 
 |   ASSERT_EQ(1, errors.size(), "Ordinal value 0 should be disallowed"); | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | bool clashing_ordinal_values() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   TestLibrary library(R"FIDL( | 
 | library a; | 
 |  | 
 | // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are | 
 | // the same.  This will trigger an error when ordinals are generated. | 
 | protocol b { | 
 |     ljz(string s, bool b) -> (int32 i); | 
 |     clgn(string s) -> (handle<channel> r); | 
 | }; | 
 |  | 
 | )FIDL"); | 
 |   EXPECT_FALSE(library.Compile()); | 
 |   const auto& errors = library.errors(); | 
 |  | 
 |   EXPECT_EQ(1, errors.size()); | 
 |  | 
 |   // TODO(FLK-435): Print some basic diagnostic info if this test flakes, so that we can try to | 
 |   // track down the root cause. | 
 |   if (errors.size() != 1) { | 
 |     fprintf(stderr, "=== BEGIN DEBUG INFO FOR FLK-435 ===\n"); | 
 |  | 
 |     auto b = library.LookupProtocol("b"); | 
 |  | 
 |     fprintf(stderr, "b->methods.size() = %zu\n", b->methods.size()); | 
 |  | 
 |     for (size_t i = 0; i < b->methods.size(); i++) { | 
 |       const auto& method = b->methods.at(i); | 
 |       fprintf(stderr, "[%zu] name=%s ordinal32=%x, ordinal64=%" PRIx64 "\n", i, | 
 |               std::string(method.name.data()).c_str(), method.generated_ordinal32->value, | 
 |               method.generated_ordinal64->value); | 
 |     } | 
 |  | 
 |     fprintf(stderr, "errors.size() = %zu\n", errors.size()); | 
 |     for (size_t i = 0; i < errors.size(); i++) { | 
 |       fprintf(stderr, "error[%zu] = %s\n", i, errors.at(i).c_str()); | 
 |     } | 
 |  | 
 |     fprintf(stderr, "=== END DEBUG INFO FOR FLK-435 ===\n"); | 
 |   } | 
 |  | 
 |   // The FTP requires the error message as follows | 
 |   const std::regex pattern(R"REGEX(\[\s*Selector\s*=\s*"(ljz|clgn)_"\s*\])REGEX"); | 
 |   std::smatch sm; | 
 |   EXPECT_TRUE(std::regex_search(errors[0], sm, pattern), | 
 |               ("Selector pattern not found in error: " + errors[0]).c_str()); | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | bool clashing_ordinal_values_with_attribute() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   TestLibrary library(R"FIDL( | 
 | library a; | 
 |  | 
 | // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are | 
 | // the same.  This will trigger an error when ordinals are generated. | 
 | protocol b { | 
 |     [Selector = "ljz"] | 
 |     foo(string s, bool b) -> (int32 i); | 
 |     [Selector = "clgn"] | 
 |     bar(string s) -> (handle<channel> r); | 
 | }; | 
 |  | 
 | )FIDL"); | 
 |   ASSERT_FALSE(library.Compile()); | 
 |   const auto& errors = library.errors(); | 
 |   ASSERT_EQ(1, errors.size()); | 
 |  | 
 |   // The FTP requires the error message as follows | 
 |   const std::regex pattern(R"REGEX(\[\s*Selector\s*=\s*"(ljz|clgn)_"\s*\])REGEX"); | 
 |   std::smatch sm; | 
 |   ASSERT_TRUE(std::regex_search(errors[0], sm, pattern), | 
 |               ("Selector pattern not found in error: " + errors[0]).c_str()); | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | bool attribute_resolves_clashes() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   TestLibrary library(R"FIDL( | 
 | library a; | 
 |  | 
 | // The first 32 bits of the SHA256 hash of a.b/ljz and a.b/clgn are | 
 | // the same.  This will trigger an error when ordinals are generated. | 
 | protocol b { | 
 |     [Selector = "ljz_"] | 
 |     ljz(string s, bool b) -> (int32 i); | 
 |     clgn(string s) -> (handle<channel> r); | 
 | }; | 
 |  | 
 | )FIDL"); | 
 |   ASSERT_TRUE(library.Compile()); | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | bool ordinal_value_is_sha256() { | 
 |   BEGIN_TEST; | 
 |   TestLibrary library(R"FIDL( | 
 | library a.b.c; | 
 |  | 
 | protocol protocol { | 
 |     selector(string s, bool b) -> (int32 i); | 
 | }; | 
 | )FIDL"); | 
 |   ASSERT_TRUE(library.Compile()); | 
 |  | 
 |   const char hash_name32[] = "a.b.c.protocol/selector"; | 
 |   uint8_t digest32[SHA256_DIGEST_LENGTH]; | 
 |   SHA256(reinterpret_cast<const uint8_t*>(hash_name32), strlen(hash_name32), digest32); | 
 |   uint64_t expected_hash32 = *(reinterpret_cast<uint64_t*>(digest32)) & 0x7fffffff; | 
 |  | 
 |   const char hash_name64[] = "a.b.c/protocol.selector"; | 
 |   uint8_t digest64[SHA256_DIGEST_LENGTH]; | 
 |   SHA256(reinterpret_cast<const uint8_t*>(hash_name64), strlen(hash_name64), digest64); | 
 |   uint64_t expected_hash64 = *(reinterpret_cast<uint64_t*>(digest64)) & 0x7fffffffffffffff; | 
 |  | 
 |   const fidl::flat::Protocol* iface = library.LookupProtocol("protocol"); | 
 |   uint64_t actual_hash32 = iface->methods[0].generated_ordinal32->value; | 
 |   ASSERT_EQ(actual_hash32, expected_hash32, "Expected 32bits hash is not correct"); | 
 |   uint64_t actual_hash64 = iface->methods[0].generated_ordinal64->value; | 
 |   ASSERT_EQ(actual_hash64, expected_hash64, "Expected 64bits hash is not correct"); | 
 |   END_TEST; | 
 | } | 
 |  | 
 | // generated by gen_ordinal_value_is_first64bits_of_sha256_test.sh | 
 | bool ordinal_value_is_first64bits_of_sha256() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   TestLibrary library(R"FIDL( | 
 | library a.b.c; | 
 |  | 
 | protocol protocol { | 
 |     s0(); | 
 |     s1(); | 
 |     s2(); | 
 |     s3(); | 
 |     s4(); | 
 |     s5(); | 
 |     s6(); | 
 |     s7(); | 
 |     s8(); | 
 |     s9(); | 
 |     s10(); | 
 |     s11(); | 
 |     s12(); | 
 |     s13(); | 
 |     s14(); | 
 |     s15(); | 
 |     s16(); | 
 |     s17(); | 
 |     s18(); | 
 |     s19(); | 
 |     s20(); | 
 |     s21(); | 
 |     s22(); | 
 |     s23(); | 
 |     s24(); | 
 |     s25(); | 
 |     s26(); | 
 |     s27(); | 
 |     s28(); | 
 |     s29(); | 
 |     s30(); | 
 |     s31(); | 
 | }; | 
 | )FIDL"); | 
 |   ASSERT_TRUE(library.Compile()); | 
 |  | 
 |   const fidl::flat::Protocol* iface = library.LookupProtocol("protocol"); | 
 |   EXPECT_EQ(iface->methods[0].generated_ordinal64->value, 0x3b1625372e15f1ae); | 
 |   EXPECT_EQ(iface->methods[1].generated_ordinal64->value, 0x4199e504fa71b5a4); | 
 |   EXPECT_EQ(iface->methods[2].generated_ordinal64->value, 0x247ca8a890628135); | 
 |   EXPECT_EQ(iface->methods[3].generated_ordinal64->value, 0x64f7c02cfffb7846); | 
 |   EXPECT_EQ(iface->methods[4].generated_ordinal64->value, 0x20d3f06c598f0cc3); | 
 |   EXPECT_EQ(iface->methods[5].generated_ordinal64->value, 0x1ce13806085dac7a); | 
 |   EXPECT_EQ(iface->methods[6].generated_ordinal64->value, 0x09e1d4b200770def); | 
 |   EXPECT_EQ(iface->methods[7].generated_ordinal64->value, 0x53df65d26411d8ee); | 
 |   EXPECT_EQ(iface->methods[8].generated_ordinal64->value, 0x690c3617405590c7); | 
 |   EXPECT_EQ(iface->methods[9].generated_ordinal64->value, 0x4ff9ef5fb170f550); | 
 |   EXPECT_EQ(iface->methods[10].generated_ordinal64->value, 0x1542d4c21d8a6c00); | 
 |   EXPECT_EQ(iface->methods[11].generated_ordinal64->value, 0x564e9e47f7418e0f); | 
 |   EXPECT_EQ(iface->methods[12].generated_ordinal64->value, 0x29681e66f3506231); | 
 |   EXPECT_EQ(iface->methods[13].generated_ordinal64->value, 0x5ee63b26268f7760); | 
 |   EXPECT_EQ(iface->methods[14].generated_ordinal64->value, 0x256950edf00aac63); | 
 |   EXPECT_EQ(iface->methods[15].generated_ordinal64->value, 0x6b21c0ff1aa02896); | 
 |   EXPECT_EQ(iface->methods[16].generated_ordinal64->value, 0x5a54f3dca00089e9); | 
 |   EXPECT_EQ(iface->methods[17].generated_ordinal64->value, 0x772476706fa4be0e); | 
 |   EXPECT_EQ(iface->methods[18].generated_ordinal64->value, 0x294e338bf71a773b); | 
 |   EXPECT_EQ(iface->methods[19].generated_ordinal64->value, 0x5a6aa228cfb68d16); | 
 |   EXPECT_EQ(iface->methods[20].generated_ordinal64->value, 0x55a09c6b033f3f98); | 
 |   EXPECT_EQ(iface->methods[21].generated_ordinal64->value, 0x1192d5b856d22cd8); | 
 |   EXPECT_EQ(iface->methods[22].generated_ordinal64->value, 0x2e68bdea28f9ce7b); | 
 |   EXPECT_EQ(iface->methods[23].generated_ordinal64->value, 0x4c8ebf26900e4451); | 
 |   EXPECT_EQ(iface->methods[24].generated_ordinal64->value, 0x3df0dbe9378c4fd3); | 
 |   EXPECT_EQ(iface->methods[25].generated_ordinal64->value, 0x087268657bb0cad1); | 
 |   EXPECT_EQ(iface->methods[26].generated_ordinal64->value, 0x0aee6ad161a90ae1); | 
 |   EXPECT_EQ(iface->methods[27].generated_ordinal64->value, 0x44e6f2282baf727a); | 
 |   EXPECT_EQ(iface->methods[28].generated_ordinal64->value, 0x3e8984f57ab5830d); | 
 |   EXPECT_EQ(iface->methods[29].generated_ordinal64->value, 0x696f9f73a5cabd21); | 
 |   EXPECT_EQ(iface->methods[30].generated_ordinal64->value, 0x327d7b0d2389e054); | 
 |   EXPECT_EQ(iface->methods[31].generated_ordinal64->value, 0x54fd307bb5bfab2d); | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | bool hack_to_rename_fuchsia_io_to_fuchsia_io_one() { | 
 |   BEGIN_TEST; | 
 |  | 
 |   TestLibrary library_io(R"FIDL( | 
 | library fuchsia.io; | 
 |  | 
 | protocol SomeProtocol { | 
 |     SomeMethod(); | 
 | }; | 
 | )FIDL"); | 
 |   ASSERT_TRUE(library_io.Compile()); | 
 |  | 
 |   TestLibrary library_io_one(R"FIDL( | 
 | library fuchsia.io1; | 
 |  | 
 | protocol SomeProtocol { | 
 |     SomeMethod(); | 
 | }; | 
 | )FIDL"); | 
 |   ASSERT_TRUE(library_io_one.Compile()); | 
 |  | 
 |   const fidl::flat::Protocol* io_protocol = library_io.LookupProtocol("SomeProtocol"); | 
 |   uint64_t io_hash64 = io_protocol->methods[0].generated_ordinal64->value; | 
 |  | 
 |   const fidl::flat::Protocol* io_one_protocol = library_io_one.LookupProtocol("SomeProtocol"); | 
 |   uint64_t io_one_hash64 = io_one_protocol->methods[0].generated_ordinal64->value; | 
 |  | 
 |   ASSERT_EQ(io_hash64, io_one_hash64); | 
 |  | 
 |   END_TEST; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | BEGIN_TEST_CASE(ordinals_test) | 
 | RUN_TEST(ordinal_cannot_be_zero) | 
 | RUN_TEST(clashing_ordinal_values) | 
 | RUN_TEST(clashing_ordinal_values_with_attribute) | 
 | RUN_TEST(attribute_resolves_clashes) | 
 | RUN_TEST(ordinal_value_is_sha256) | 
 | RUN_TEST(ordinal_value_is_first64bits_of_sha256) | 
 | RUN_TEST(hack_to_rename_fuchsia_io_to_fuchsia_io_one) | 
 | END_TEST_CASE(ordinals_test) |