blob: cd6ac22570ade44bd8d3a4806e5b1c7c09584d49 [file] [log] [blame]
// 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)