blob: 41b23e101f229bdb109cb32ed56ae8e98c40e5c6 [file] [log] [blame]
// Copyright 2022 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 <lib/standalone-test/standalone.h>
#include <lib/stdcompat/array.h>
#include <lib/zx/channel.h>
#include <lib/zx/eventpair.h>
#include <zircon/errors.h>
#include <zircon/rights.h>
#include <zircon/sanitizer.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <cstdint>
#include <string_view>
#include <zxtest/zxtest.h>
#include "helper.h"
namespace {
class SvcSingleProcessTest : public zxtest::Test {
public:
void SetUp() final {
static zx::channel svc_stash;
static zx::channel svc_server;
// Prevents multiple iterations from discarding this.
if (!svc_stash) {
svc_stash = GetSvcStash();
ASSERT_TRUE(svc_stash);
}
// Order matters, provider is stored first.
if (!svc_server) {
ASSERT_NO_FATAL_FAILURE(GetStashedSvc(svc_stash.borrow(), svc_server));
}
svc_stash_ = svc_stash.borrow();
svc_ = standalone::GetNsDir("/svc");
stashed_svc_ = svc_server.borrow();
// Drain any preexisting messages from stashed svc, such that we can properly watch the state
// transition. In some instrumented builds, we may have provided actual debug data.
// This test expect that there is 'nothing' in the stash, so lets pretend that.
uint32_t actual_bytes = 0;
uint32_t actual_handles = 0;
zx_signals_t observed = 0;
while (stashed_svc()->wait_one(ZX_CHANNEL_READABLE, zx::time::infinite_past(), &observed) !=
ZX_ERR_TIMED_OUT) {
ASSERT_TRUE((observed & ZX_CHANNEL_READABLE) != 0);
// We are peeking into the channel.
ASSERT_NOT_OK(stashed_svc()->read(0, nullptr, nullptr, 0, 0, &actual_bytes, &actual_handles));
std::vector<uint8_t> bytes(actual_bytes, 0);
std::vector<zx_handle_t> handles(actual_handles, ZX_HANDLE_INVALID);
ASSERT_OK(stashed_svc()->read(
0, bytes.data(), handles.data(), static_cast<uint32_t>(bytes.size()),
static_cast<uint32_t>(handles.size()), &actual_bytes, &actual_handles));
observed = 0;
}
}
zx::unowned_channel svc_stash() { return svc_stash_->borrow(); }
zx::unowned_channel local_svc() { return svc_->borrow(); }
zx::unowned_channel stashed_svc() { return stashed_svc_->borrow(); }
private:
zx::unowned_channel svc_stash_;
zx::unowned_channel svc_;
zx::unowned_channel stashed_svc_;
};
TEST_F(SvcSingleProcessTest, SvcStubIsValidHandle) { ASSERT_TRUE(local_svc()->is_valid()); }
TEST_F(SvcSingleProcessTest, SvcStashIsValidHandle) {
ASSERT_TRUE(svc_stash()->is_valid());
ASSERT_TRUE(stashed_svc()->is_valid());
}
TEST_F(SvcSingleProcessTest, WritingIntoSvcShowsUpInStashHandle) {
ASSERT_TRUE(local_svc()->is_valid());
ASSERT_TRUE(stashed_svc()->is_valid());
auto written_message = cpp20::to_array("Hello World!");
ASSERT_OK(local_svc()->write(0, written_message.data(), written_message.size(), nullptr, 0));
decltype(written_message) read_message = {};
uint32_t actual_bytes, actual_handles;
ASSERT_OK(stashed_svc()->read(0, read_message.data(), nullptr, read_message.size(), 0,
&actual_bytes, &actual_handles));
EXPECT_EQ(read_message, written_message);
}
TEST_F(SvcSingleProcessTest, SanitizerPublishDataShowsUpInStashedHandle) {
ASSERT_TRUE(local_svc()->is_valid());
ASSERT_TRUE(stashed_svc()->is_valid());
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(124, 0, &vmo));
// Channel transitions from not readable to readable.
zx_signals_t observed = 0;
ASSERT_STATUS(stashed_svc()->wait_one(ZX_CHANNEL_READABLE, zx::time::infinite_past(), &observed),
ZX_ERR_TIMED_OUT);
constexpr const char* kDataSink = "some_sink_name";
zx_koid_t vmo_koid = GetKoid(vmo.get());
zx::eventpair token_1(__sanitizer_publish_data(kDataSink, vmo.release()));
zx_koid_t token_koid = GetPeerKoid(token_1.get());
ASSERT_NE(token_koid, ZX_KOID_INVALID);
ASSERT_NE(vmo_koid, ZX_KOID_INVALID);
bool found = false;
OnEachMessage(stashed_svc(), [&](zx::unowned_channel stashed_svc, bool& keep_going) {
keep_going = false;
Message debug_msg;
ASSERT_NO_FAILURES(GetDebugDataMessage(std::move(stashed_svc), debug_msg));
DebugDataMessageView view(debug_msg);
if (view.sink() != kDataSink) {
keep_going = true;
return;
}
ASSERT_EQ(GetKoid(view.vmo()->get()), vmo_koid);
ASSERT_EQ(GetKoid(view.token()->get()), token_koid);
found = true;
});
ASSERT_TRUE(found);
}
} // namespace