blob: db734d7b148f8f82068d05343b4c7b9b2d787917 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "chrome_elf/third_party_dlls/logs.h"
#include <windows.h>
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "chrome_elf/sha1/sha1.h"
#include "chrome_elf/third_party_dlls/logging_api.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace third_party_dlls {
namespace {
enum { kWaitTimeoutMs = 3000 };
// Argument for NotificationHandler().
struct NotificationHandlerArguments {
uint32_t logs_expected;
std::unique_ptr<base::WaitableEvent> notification_event;
};
struct TestEntry {
uint8_t basename_hash[elf_sha1::kSHA1Length];
uint8_t code_id_hash[elf_sha1::kSHA1Length];
};
// Sample TestEntries - hashes are the same, except for first bytes.
TestEntry kTestLogs[] = {
{{0x11, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x22, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x33, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x44, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x55, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x66, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x77, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0x88, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0x99, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0xaa, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
{{0xbb, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71},
{0xcc, 0xab, 0x9e, 0xa4, 0xbe, 0xf5, 0xf3, 0x6e, 0x7f, 0x20,
0xc3, 0xaf, 0x63, 0x9c, 0x6f, 0x0e, 0xfe, 0x5f, 0x27, 0x71}},
};
// Be sure to test the padding/alignment issues well here.
const std::string kTestPaths[] = {
"1", "123", "1234", "12345", "123456", "1234567",
};
static_assert(
arraysize(kTestLogs) == arraysize(kTestPaths),
"Some tests currently expect these two arrays to be the same size.");
// Ensure |buffer_size| passed in is the actual bytes written by DrainLog().
void VerifyBuffer(uint8_t* buffer, uint32_t buffer_size) {
uint32_t total_logs = 0;
size_t index = 0;
size_t array_size = arraysize(kTestLogs);
// Verify against kTestLogs/kTestPaths. Expect 2 * arraysize(kTestLogs)
// entries: first half are "blocked", second half are "allowed".
LogEntry* entry = nullptr;
uint8_t* tracker = buffer;
while (tracker < buffer + buffer_size) {
entry = reinterpret_cast<LogEntry*>(tracker);
EXPECT_EQ(elf_sha1::CompareHashes(entry->basename_hash,
kTestLogs[index].basename_hash),
0);
EXPECT_EQ(elf_sha1::CompareHashes(entry->code_id_hash,
kTestLogs[index].code_id_hash),
0);
if (entry->path_len)
EXPECT_STREQ(entry->path, kTestPaths[index].c_str());
++total_logs;
tracker += GetLogEntrySize(entry->path_len);
// Roll index back to 0 for second run through kTestLogs, else increment.
index = (index == array_size - 1) ? 0 : index + 1;
}
EXPECT_EQ(total_logs, array_size * 2);
}
// Helper function to count the number of LogEntries in a buffer returned from
// DrainLog().
uint32_t GetLogCount(uint8_t* buffer, uint32_t bytes_written) {
LogEntry* entry = nullptr;
uint8_t* tracker = buffer;
uint32_t counter = 0;
while (tracker < buffer + bytes_written) {
entry = reinterpret_cast<LogEntry*>(tracker);
++counter;
tracker += GetLogEntrySize(entry->path_len);
}
return counter;
}
// A thread function used to test log notifications.
// - |parameter| should be a NotificationHandlerArguments*.
// - Returns 0 for successful retrieval of expected number of LogEntries.
DWORD WINAPI NotificationHandler(LPVOID parameter) {
NotificationHandlerArguments* args =
reinterpret_cast<NotificationHandlerArguments*>(parameter);
uint32_t log_counter = 0;
// Make a buffer big enough for any possible DrainLog call.
uint32_t buffer_size = args->logs_expected * GetLogEntrySize(0);
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[buffer_size]);
uint32_t bytes_written = 0;
do {
if (!args->notification_event->TimedWait(
base::TimeDelta::FromMilliseconds(kWaitTimeoutMs)))
break;
bytes_written = DrainLog(&buffer[0], buffer_size, nullptr);
log_counter += GetLogCount(&buffer[0], bytes_written);
} while (log_counter < args->logs_expected);
return (log_counter == args->logs_expected) ? 0 : 1;
}
//------------------------------------------------------------------------------
// Third-party log tests
//------------------------------------------------------------------------------
// Test successful initialization and module lookup.
TEST(ThirdParty, Logs) {
// Init.
ASSERT_EQ(InitLogs(), LogStatus::kSuccess);
for (size_t i = 0; i < arraysize(kTestLogs); ++i) {
std::string fingerprint_hash(
reinterpret_cast<char*>(kTestLogs[i].code_id_hash),
elf_sha1::kSHA1Length);
std::string name_hash(reinterpret_cast<char*>(kTestLogs[i].basename_hash),
elf_sha1::kSHA1Length);
// Add some blocked entries.
LogLoadAttempt(LogType::kBlocked, name_hash, fingerprint_hash,
std::string());
// Add some allowed entries.
LogLoadAttempt(LogType::kAllowed, name_hash, fingerprint_hash,
kTestPaths[i]);
}
uint32_t initial_log = 0;
DrainLog(nullptr, 0, &initial_log);
ASSERT_TRUE(initial_log);
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[initial_log]);
uint32_t remaining_log = 0;
uint32_t bytes_written = DrainLog(&buffer[0], initial_log, &remaining_log);
EXPECT_EQ(bytes_written, initial_log);
EXPECT_EQ(remaining_log, uint32_t{0});
VerifyBuffer(&buffer[0], bytes_written);
DeinitLogs();
}
// Test notifications.
TEST(ThirdParty, LogNotifications) {
// Init.
ASSERT_EQ(InitLogs(), LogStatus::kSuccess);
uint32_t initial_log = 0;
DrainLog(nullptr, 0, &initial_log);
EXPECT_EQ(initial_log, uint32_t{0});
// Set up the required arguments for the test thread.
NotificationHandlerArguments handler_data;
handler_data.logs_expected = arraysize(kTestLogs);
handler_data.notification_event.reset(
new base::WaitableEvent(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED));
// Register the event.
ASSERT_TRUE(
RegisterLogNotification(handler_data.notification_event->handle()));
// Fire off a thread to handle the notifications.
base::win::ScopedHandle thread(::CreateThread(
nullptr, 0, &NotificationHandler, &handler_data, 0, nullptr));
for (size_t i = 0; i < handler_data.logs_expected; ++i) {
std::string fingerprint_hash(
reinterpret_cast<char*>(kTestLogs[i].code_id_hash),
elf_sha1::kSHA1Length);
std::string name_hash(reinterpret_cast<char*>(kTestLogs[i].basename_hash),
elf_sha1::kSHA1Length);
// Add blocked entries - type doesn't matter in this test.
LogLoadAttempt(LogType::kBlocked, name_hash, fingerprint_hash,
std::string());
}
EXPECT_EQ(::WaitForSingleObject(thread.Get(), kWaitTimeoutMs * 2),
WAIT_OBJECT_0);
DWORD exit_code = 1;
EXPECT_TRUE(::GetExitCodeThread(thread.Get(), &exit_code));
EXPECT_EQ(exit_code, DWORD{0});
DeinitLogs();
}
} // namespace
} // namespace third_party_dlls