1 // Copyright 2014 The Chromium 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 <stddef.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <algorithm>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/macros.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/numerics/safe_math.h"
16 #include "base/run_loop.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "mojo/public/c/system/macros.h"
19 #include "mojo/public/cpp/bindings/binding.h"
20 #include "mojo/public/cpp/bindings/connector.h"
21 #include "mojo/public/cpp/bindings/filter_chain.h"
22 #include "mojo/public/cpp/bindings/interface_ptr.h"
23 #include "mojo/public/cpp/bindings/lib/validation_errors.h"
24 #include "mojo/public/cpp/bindings/message.h"
25 #include "mojo/public/cpp/bindings/message_header_validator.h"
26 #include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
27 #include "mojo/public/cpp/system/core.h"
28 #include "mojo/public/cpp/system/message.h"
29 #include "mojo/public/cpp/test_support/test_support.h"
30 #include "mojo/public/interfaces/bindings/tests/validation_test_associated_interfaces.mojom.h"
31 #include "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 namespace mojo {
35 namespace test {
36 namespace {
37
CreateRawMessage(size_t size)38 Message CreateRawMessage(size_t size) {
39 ScopedMessageHandle handle;
40 MojoResult rv = CreateMessage(&handle);
41 DCHECK_EQ(MOJO_RESULT_OK, rv);
42 DCHECK(handle.is_valid());
43
44 DCHECK(base::IsValueInRangeForNumericType<uint32_t>(size));
45 MojoAppendMessageDataOptions options;
46 options.struct_size = sizeof(options);
47 options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE;
48 void* buffer;
49 uint32_t buffer_size;
50 rv = MojoAppendMessageData(handle->value(), static_cast<uint32_t>(size),
51 nullptr, 0, &options, &buffer, &buffer_size);
52 DCHECK_EQ(MOJO_RESULT_OK, rv);
53
54 return Message(std::move(handle));
55 }
56
57 template <typename T>
Append(std::vector<uint8_t> * data_vector,T data)58 void Append(std::vector<uint8_t>* data_vector, T data) {
59 size_t pos = data_vector->size();
60 data_vector->resize(pos + sizeof(T));
61 memcpy(&(*data_vector)[pos], &data, sizeof(T));
62 }
63
TestInputParser(const std::string & input,bool expected_result,const std::vector<uint8_t> & expected_data,size_t expected_num_handles)64 bool TestInputParser(const std::string& input,
65 bool expected_result,
66 const std::vector<uint8_t>& expected_data,
67 size_t expected_num_handles) {
68 std::vector<uint8_t> data;
69 size_t num_handles;
70 std::string error_message;
71
72 bool result =
73 ParseValidationTestInput(input, &data, &num_handles, &error_message);
74 if (expected_result) {
75 if (result && error_message.empty() && expected_data == data &&
76 expected_num_handles == num_handles) {
77 return true;
78 }
79
80 // Compare with an empty string instead of checking |error_message.empty()|,
81 // so that the message will be printed out if the two are not equal.
82 EXPECT_EQ(std::string(), error_message);
83 EXPECT_EQ(expected_data, data);
84 EXPECT_EQ(expected_num_handles, num_handles);
85 return false;
86 }
87
88 EXPECT_FALSE(error_message.empty());
89 return !result && !error_message.empty();
90 }
91
GetMatchingTests(const std::vector<std::string> & names,const std::string & prefix)92 std::vector<std::string> GetMatchingTests(const std::vector<std::string>& names,
93 const std::string& prefix) {
94 const std::string suffix = ".data";
95 std::vector<std::string> tests;
96 for (size_t i = 0; i < names.size(); ++i) {
97 if (names[i].size() >= suffix.size() &&
98 names[i].substr(0, prefix.size()) == prefix &&
99 names[i].substr(names[i].size() - suffix.size()) == suffix)
100 tests.push_back(names[i].substr(0, names[i].size() - suffix.size()));
101 }
102 return tests;
103 }
104
ReadFile(const std::string & path,std::string * result)105 bool ReadFile(const std::string& path, std::string* result) {
106 FILE* fp = OpenSourceRootRelativeFile(path.c_str());
107 if (!fp) {
108 ADD_FAILURE() << "File not found: " << path;
109 return false;
110 }
111 fseek(fp, 0, SEEK_END);
112 size_t size = static_cast<size_t>(ftell(fp));
113 if (size == 0) {
114 result->clear();
115 fclose(fp);
116 return true;
117 }
118 fseek(fp, 0, SEEK_SET);
119 result->resize(size);
120 size_t size_read = fread(&result->at(0), 1, size, fp);
121 fclose(fp);
122 return size == size_read;
123 }
124
ReadAndParseDataFile(const std::string & path,std::vector<uint8_t> * data,size_t * num_handles)125 bool ReadAndParseDataFile(const std::string& path,
126 std::vector<uint8_t>* data,
127 size_t* num_handles) {
128 std::string input;
129 if (!ReadFile(path, &input))
130 return false;
131
132 std::string error_message;
133 if (!ParseValidationTestInput(input, data, num_handles, &error_message)) {
134 ADD_FAILURE() << error_message;
135 return false;
136 }
137
138 return true;
139 }
140
ReadResultFile(const std::string & path,std::string * result)141 bool ReadResultFile(const std::string& path, std::string* result) {
142 if (!ReadFile(path, result))
143 return false;
144
145 // Result files are new-line delimited text files. Remove any CRs.
146 result->erase(std::remove(result->begin(), result->end(), '\r'),
147 result->end());
148
149 // Remove trailing LFs.
150 size_t pos = result->find_last_not_of('\n');
151 if (pos == std::string::npos)
152 result->clear();
153 else
154 result->resize(pos + 1);
155
156 return true;
157 }
158
GetPath(const std::string & root,const std::string & suffix)159 std::string GetPath(const std::string& root, const std::string& suffix) {
160 return "mojo/public/interfaces/bindings/tests/data/validation/" + root +
161 suffix;
162 }
163
164 // |message| should be a newly created object.
ReadTestCase(const std::string & test,Message * message,std::string * expected)165 bool ReadTestCase(const std::string& test,
166 Message* message,
167 std::string* expected) {
168 std::vector<uint8_t> data;
169 size_t num_handles;
170 if (!ReadAndParseDataFile(GetPath(test, ".data"), &data, &num_handles) ||
171 !ReadResultFile(GetPath(test, ".expected"), expected)) {
172 return false;
173 }
174
175 *message = CreateRawMessage(data.size());
176 if (!data.empty())
177 memcpy(message->mutable_data(), &data[0], data.size());
178 message->mutable_handles()->resize(num_handles);
179
180 return true;
181 }
182
RunValidationTests(const std::string & prefix,MessageReceiver * test_message_receiver)183 void RunValidationTests(const std::string& prefix,
184 MessageReceiver* test_message_receiver) {
185 std::vector<std::string> names =
186 EnumerateSourceRootRelativeDirectory(GetPath("", ""));
187 std::vector<std::string> tests = GetMatchingTests(names, prefix);
188 ASSERT_FALSE(tests.empty());
189
190 for (size_t i = 0; i < tests.size(); ++i) {
191 Message message;
192 std::string expected;
193 ASSERT_TRUE(ReadTestCase(tests[i], &message, &expected));
194
195 std::string result;
196 base::RunLoop run_loop;
197 mojo::internal::ValidationErrorObserverForTesting observer(
198 run_loop.QuitClosure());
199 ignore_result(test_message_receiver->Accept(&message));
200 if (expected != "PASS") // Observer only gets called on errors.
201 run_loop.Run();
202 if (observer.last_error() == mojo::internal::VALIDATION_ERROR_NONE)
203 result = "PASS";
204 else
205 result = mojo::internal::ValidationErrorToString(observer.last_error());
206
207 EXPECT_EQ(expected, result) << "failed test: " << tests[i];
208 }
209 }
210
211 class DummyMessageReceiver : public MessageReceiver {
212 public:
Accept(Message * message)213 bool Accept(Message* message) override {
214 return true; // Any message is OK.
215 }
216 };
217
218 class ValidationTest : public testing::Test {
219 public:
ValidationTest()220 ValidationTest() {}
221
222 protected:
223 base::MessageLoop loop_;
224 };
225
226 class ValidationIntegrationTest : public ValidationTest {
227 public:
ValidationIntegrationTest()228 ValidationIntegrationTest() : test_message_receiver_(nullptr) {}
229
~ValidationIntegrationTest()230 ~ValidationIntegrationTest() override {}
231
SetUp()232 void SetUp() override {
233 ScopedMessagePipeHandle tester_endpoint;
234 ASSERT_EQ(MOJO_RESULT_OK,
235 CreateMessagePipe(nullptr, &tester_endpoint, &testee_endpoint_));
236 test_message_receiver_ =
237 new TestMessageReceiver(this, std::move(tester_endpoint));
238 }
239
TearDown()240 void TearDown() override {
241 delete test_message_receiver_;
242 test_message_receiver_ = nullptr;
243
244 // Make sure that the other end receives the OnConnectionError()
245 // notification.
246 PumpMessages();
247 }
248
test_message_receiver()249 MessageReceiver* test_message_receiver() { return test_message_receiver_; }
250
testee_endpoint()251 ScopedMessagePipeHandle testee_endpoint() {
252 return std::move(testee_endpoint_);
253 }
254
255 private:
256 class TestMessageReceiver : public MessageReceiver {
257 public:
TestMessageReceiver(ValidationIntegrationTest * owner,ScopedMessagePipeHandle handle)258 TestMessageReceiver(ValidationIntegrationTest* owner,
259 ScopedMessagePipeHandle handle)
260 : owner_(owner),
261 connector_(std::move(handle),
262 mojo::Connector::SINGLE_THREADED_SEND,
263 base::ThreadTaskRunnerHandle::Get()) {
264 connector_.set_enforce_errors_from_incoming_receiver(false);
265 }
~TestMessageReceiver()266 ~TestMessageReceiver() override {}
267
Accept(Message * message)268 bool Accept(Message* message) override {
269 return connector_.Accept(message);
270 }
271
272 public:
273 ValidationIntegrationTest* owner_;
274 mojo::Connector connector_;
275 };
276
PumpMessages()277 void PumpMessages() { base::RunLoop().RunUntilIdle(); }
278
279 TestMessageReceiver* test_message_receiver_;
280 ScopedMessagePipeHandle testee_endpoint_;
281 };
282
283 class IntegrationTestInterfaceImpl : public IntegrationTestInterface {
284 public:
~IntegrationTestInterfaceImpl()285 ~IntegrationTestInterfaceImpl() override {}
286
Method0(BasicStructPtr param0,const Method0Callback & callback)287 void Method0(BasicStructPtr param0,
288 const Method0Callback& callback) override {
289 callback.Run(std::vector<uint8_t>());
290 }
291 };
292
TEST_F(ValidationTest,InputParser)293 TEST_F(ValidationTest, InputParser) {
294 {
295 // The parser, as well as Append() defined above, assumes that this code is
296 // running on a little-endian platform. Test whether that is true.
297 uint16_t x = 1;
298 ASSERT_EQ(1, *(reinterpret_cast<char*>(&x)));
299 }
300 {
301 // Test empty input.
302 std::string input;
303 std::vector<uint8_t> expected;
304
305 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
306 }
307 {
308 // Test input that only consists of comments and whitespaces.
309 std::string input = " \t // hello world \n\r \t// the answer is 42 ";
310 std::vector<uint8_t> expected;
311
312 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
313 }
314 {
315 std::string input =
316 "[u1]0x10// hello world !! \n\r \t [u2]65535 \n"
317 "[u4]65536 [u8]0xFFFFFFFFFFFFFFFF 0 0Xff";
318 std::vector<uint8_t> expected;
319 Append(&expected, static_cast<uint8_t>(0x10));
320 Append(&expected, static_cast<uint16_t>(65535));
321 Append(&expected, static_cast<uint32_t>(65536));
322 Append(&expected, static_cast<uint64_t>(0xffffffffffffffff));
323 Append(&expected, static_cast<uint8_t>(0));
324 Append(&expected, static_cast<uint8_t>(0xff));
325
326 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
327 }
328 {
329 std::string input = "[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40";
330 std::vector<uint8_t> expected;
331 Append(&expected, -static_cast<int64_t>(0x800));
332 Append(&expected, static_cast<int8_t>(-128));
333 Append(&expected, static_cast<int16_t>(0));
334 Append(&expected, static_cast<int32_t>(-40));
335
336 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
337 }
338 {
339 std::string input = "[b]00001011 [b]10000000 // hello world\r [b]00000000";
340 std::vector<uint8_t> expected;
341 Append(&expected, static_cast<uint8_t>(11));
342 Append(&expected, static_cast<uint8_t>(128));
343 Append(&expected, static_cast<uint8_t>(0));
344
345 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
346 }
347 {
348 std::string input = "[f]+.3e9 [d]-10.03";
349 std::vector<uint8_t> expected;
350 Append(&expected, +.3e9f);
351 Append(&expected, -10.03);
352
353 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
354 }
355 {
356 std::string input = "[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar";
357 std::vector<uint8_t> expected;
358 Append(&expected, static_cast<uint32_t>(14));
359 Append(&expected, static_cast<uint8_t>(0));
360 Append(&expected, static_cast<uint64_t>(9));
361 Append(&expected, static_cast<uint8_t>(0));
362
363 EXPECT_TRUE(TestInputParser(input, true, expected, 0));
364 }
365 {
366 std::string input = "// This message has handles! \n[handles]50 [u8]2";
367 std::vector<uint8_t> expected;
368 Append(&expected, static_cast<uint64_t>(2));
369
370 EXPECT_TRUE(TestInputParser(input, true, expected, 50));
371 }
372
373 // Test some failure cases.
374 {
375 const char* error_inputs[] = {"/ hello world",
376 "[u1]x",
377 "[u2]-1000",
378 "[u1]0x100",
379 "[s2]-0x8001",
380 "[b]1",
381 "[b]1111111k",
382 "[dist4]unmatched",
383 "[anchr]hello [dist8]hello",
384 "[dist4]a [dist4]a [anchr]a",
385 "[dist4]a [anchr]a [dist4]a [anchr]a",
386 "0 [handles]50",
387 nullptr};
388
389 for (size_t i = 0; error_inputs[i]; ++i) {
390 std::vector<uint8_t> expected;
391 if (!TestInputParser(error_inputs[i], false, expected, 0))
392 ADD_FAILURE() << "Unexpected test result for: " << error_inputs[i];
393 }
394 }
395 }
396
TEST_F(ValidationTest,Conformance)397 TEST_F(ValidationTest, Conformance) {
398 DummyMessageReceiver dummy_receiver;
399 mojo::FilterChain validators(&dummy_receiver);
400 validators.Append<mojo::MessageHeaderValidator>();
401 validators.Append<ConformanceTestInterface::RequestValidator_>();
402
403 RunValidationTests("conformance_", &validators);
404 }
405
TEST_F(ValidationTest,AssociatedConformace)406 TEST_F(ValidationTest, AssociatedConformace) {
407 DummyMessageReceiver dummy_receiver;
408 mojo::FilterChain validators(&dummy_receiver);
409 validators.Append<mojo::MessageHeaderValidator>();
410 validators.Append<AssociatedConformanceTestInterface::RequestValidator_>();
411
412 RunValidationTests("associated_conformance_", &validators);
413 }
414
415 // This test is similar to Conformance test but its goal is specifically
416 // do bounds-check testing of message validation. For example we test the
417 // detection of off-by-one errors in method ordinals.
TEST_F(ValidationTest,BoundsCheck)418 TEST_F(ValidationTest, BoundsCheck) {
419 DummyMessageReceiver dummy_receiver;
420 mojo::FilterChain validators(&dummy_receiver);
421 validators.Append<mojo::MessageHeaderValidator>();
422 validators.Append<BoundsCheckTestInterface::RequestValidator_>();
423
424 RunValidationTests("boundscheck_", &validators);
425 }
426
427 // This test is similar to the Conformance test but for responses.
TEST_F(ValidationTest,ResponseConformance)428 TEST_F(ValidationTest, ResponseConformance) {
429 DummyMessageReceiver dummy_receiver;
430 mojo::FilterChain validators(&dummy_receiver);
431 validators.Append<mojo::MessageHeaderValidator>();
432 validators.Append<ConformanceTestInterface::ResponseValidator_>();
433
434 RunValidationTests("resp_conformance_", &validators);
435 }
436
437 // This test is similar to the BoundsCheck test but for responses.
TEST_F(ValidationTest,ResponseBoundsCheck)438 TEST_F(ValidationTest, ResponseBoundsCheck) {
439 DummyMessageReceiver dummy_receiver;
440 mojo::FilterChain validators(&dummy_receiver);
441 validators.Append<mojo::MessageHeaderValidator>();
442 validators.Append<BoundsCheckTestInterface::ResponseValidator_>();
443
444 RunValidationTests("resp_boundscheck_", &validators);
445 }
446
447 // Test that InterfacePtr<X> applies the correct validators and they don't
448 // conflict with each other:
449 // - MessageHeaderValidator
450 // - X::ResponseValidator_
TEST_F(ValidationIntegrationTest,InterfacePtr)451 TEST_F(ValidationIntegrationTest, InterfacePtr) {
452 IntegrationTestInterfacePtr interface_ptr = MakeProxy(
453 InterfacePtrInfo<IntegrationTestInterface>(testee_endpoint(), 0u));
454 interface_ptr.internal_state()->EnableTestingMode();
455
456 RunValidationTests("integration_intf_resp", test_message_receiver());
457 RunValidationTests("integration_msghdr", test_message_receiver());
458 }
459
460 // Test that Binding<X> applies the correct validators and they don't
461 // conflict with each other:
462 // - MessageHeaderValidator
463 // - X::RequestValidator_
TEST_F(ValidationIntegrationTest,Binding)464 TEST_F(ValidationIntegrationTest, Binding) {
465 IntegrationTestInterfaceImpl interface_impl;
466 Binding<IntegrationTestInterface> binding(
467 &interface_impl, IntegrationTestInterfaceRequest(testee_endpoint()));
468 binding.EnableTestingMode();
469
470 RunValidationTests("integration_intf_rqst", test_message_receiver());
471 RunValidationTests("integration_msghdr", test_message_receiver());
472 }
473
474 // Test pointer validation (specifically, that the encoded offset is 32-bit)
TEST_F(ValidationTest,ValidateEncodedPointer)475 TEST_F(ValidationTest, ValidateEncodedPointer) {
476 uint64_t offset;
477
478 offset = 0ULL;
479 EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
480
481 offset = 1ULL;
482 EXPECT_TRUE(mojo::internal::ValidateEncodedPointer(&offset));
483
484 // offset must be <= 32-bit.
485 offset = std::numeric_limits<uint32_t>::max() + 1ULL;
486 EXPECT_FALSE(mojo::internal::ValidateEncodedPointer(&offset));
487 }
488
489 // Tests the IsKnownEnumValue() function generated for BasicEnum.
TEST(EnumValueValidationTest,BasicEnum)490 TEST(EnumValueValidationTest, BasicEnum) {
491 // BasicEnum can have -3,0,1,10 as possible integral values.
492 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-4)));
493 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(-3)));
494 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-2)));
495 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(-1)));
496 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(0)));
497 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(1)));
498 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(2)));
499 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(9)));
500 // In the mojom, we represent this value as hex (0xa).
501 EXPECT_TRUE(IsKnownEnumValue(static_cast<BasicEnum>(10)));
502 EXPECT_FALSE(IsKnownEnumValue(static_cast<BasicEnum>(11)));
503 }
504
505 // Tests the IsKnownEnumValue() method generated for StructWithEnum.
TEST(EnumValueValidationTest,EnumWithin)506 TEST(EnumValueValidationTest, EnumWithin) {
507 // StructWithEnum::EnumWithin can have [0,4] as possible integral values.
508 EXPECT_FALSE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(-1)));
509 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(0)));
510 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(1)));
511 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(2)));
512 EXPECT_TRUE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(3)));
513 EXPECT_FALSE(IsKnownEnumValue(static_cast<StructWithEnum::EnumWithin>(4)));
514 }
515
516 } // namespace
517 } // namespace test
518 } // namespace mojo
519