sandboxed-api/sandboxed_api/sandbox2/comms_test.cc

383 lines
11 KiB
C++
Raw Normal View History

//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Unittest for the sandbox2::Comms class.
#include "sandboxed_api/sandbox2/comms.h"
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdint>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <thread> // NOLINT(build/c++11)
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/fixed_array.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "sandboxed_api/sandbox2/comms_test.pb.h"
#include "sandboxed_api/util/status_matchers.h"
using ::sapi::IsOk;
using ::sapi::StatusIs;
using ::testing::Eq;
using ::testing::IsFalse;
using ::testing::IsTrue;
namespace sandbox2 {
using CommunicationHandler = std::function<void(Comms* comms)>;
constexpr char kProtoStr[] = "ABCD";
static absl::string_view NullTestString() {
static constexpr char kHelperStr[] = "test\0\n\r\t\x01\x02";
return absl::string_view(kHelperStr, sizeof(kHelperStr) - 1);
}
// Helper function that handles the communication between the two handler
// functions.
void HandleCommunication(const CommunicationHandler& a,
const CommunicationHandler& b) {
int sv[2];
CHECK_NE(socketpair(AF_UNIX, SOCK_STREAM, 0, sv), -1);
Comms comms(sv[0]);
// Start handler a.
std::thread remote([sv, &a]() {
Comms my_comms(sv[1]);
a(&my_comms);
});
// Accept connection and run handler b.
b(&comms);
remote.join();
}
TEST(CommsTest, TestSendRecv8) {
auto a = [](Comms* comms) {
// Send Uint8.
ASSERT_THAT(comms->SendUint8(192), IsTrue());
// Recv Int8.
int8_t tmp8;
ASSERT_THAT(comms->RecvInt8(&tmp8), IsTrue());
EXPECT_THAT(tmp8, Eq(-7));
};
auto b = [](Comms* comms) {
// Recv Uint8.
uint8_t tmpu8;
ASSERT_THAT(comms->RecvUint8(&tmpu8), IsTrue());
EXPECT_THAT(tmpu8, Eq(192));
// Send Int8.
ASSERT_THAT(comms->SendInt8(-7), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecv16) {
auto a = [](Comms* comms) {
// Send Uint16.
ASSERT_THAT(comms->SendUint16(40001), IsTrue());
// Recv Int16.
int16_t tmp16;
ASSERT_THAT(comms->RecvInt16(&tmp16), IsTrue());
EXPECT_THAT(tmp16, Eq(-22050));
};
auto b = [](Comms* comms) {
// Recv Uint16.
uint16_t tmpu16;
ASSERT_THAT(comms->RecvUint16(&tmpu16), IsTrue());
EXPECT_THAT(tmpu16, Eq(40001));
// Send Int16.
ASSERT_THAT(comms->SendInt16(-22050), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecv32) {
auto a = [](Comms* comms) {
// SendUint32.
ASSERT_THAT(comms->SendUint32(3221225472UL), IsTrue());
// Recv Int32.
int32_t tmp32;
ASSERT_THAT(comms->RecvInt32(&tmp32), IsTrue());
EXPECT_THAT(tmp32, Eq(-1073741824));
};
auto b = [](Comms* comms) {
// Recv Uint32.
uint32_t tmpu32;
ASSERT_THAT(comms->RecvUint32(&tmpu32), IsTrue());
EXPECT_THAT(tmpu32, Eq(3221225472UL));
// Send Int32.
ASSERT_THAT(comms->SendInt32(-1073741824), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecv64) {
auto a = [](Comms* comms) {
// SendUint64.
ASSERT_THAT(comms->SendUint64(1099511627776ULL), IsTrue());
// Recv Int64.
int64_t tmp64;
ASSERT_THAT(comms->RecvInt64(&tmp64), IsTrue());
EXPECT_THAT(tmp64, Eq(-1099511627776LL));
};
auto b = [](Comms* comms) {
// Recv Uint64.
uint64_t tmpu64;
ASSERT_THAT(comms->RecvUint64(&tmpu64), IsTrue());
EXPECT_THAT(tmpu64, Eq(1099511627776ULL));
// Send Int64.
ASSERT_THAT(comms->SendInt64(-1099511627776LL), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestTypeMismatch) {
auto a = [](Comms* comms) {
uint8_t tmpu8;
// Receive Int8 (but Uint8 expected).
EXPECT_THAT(comms->RecvUint8(&tmpu8), IsFalse());
};
auto b = [](Comms* comms) {
// Send Int8 (but Uint8 expected).
ASSERT_THAT(comms->SendInt8(-93), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvString) {
auto a = [](Comms* comms) {
std::string tmps;
ASSERT_THAT(comms->RecvString(&tmps), IsTrue());
EXPECT_TRUE(tmps == NullTestString());
EXPECT_THAT(tmps.size(), Eq(NullTestString().size()));
};
auto b = [](Comms* comms) {
ASSERT_THAT(comms->SendString(std::string(NullTestString())), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvArray) {
auto a = [](Comms* comms) {
// Receive 1M bytes.
std::vector<uint8_t> buffer;
ASSERT_THAT(comms->RecvBytes(&buffer), IsTrue());
EXPECT_THAT(buffer.size(), Eq(1024 * 1024));
};
auto b = [](Comms* comms) {
// Send 1M bytes.
std::vector<uint8_t> buffer(1024 * 1024);
memset(buffer.data(), 0, buffer.size());
ASSERT_THAT(comms->SendBytes(buffer), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvFD) {
auto a = [](Comms* comms) {
// Receive FD and test it.
int fd = -1;
ASSERT_THAT(comms->RecvFD(&fd), IsTrue());
EXPECT_GE(fd, 0);
EXPECT_NE(fcntl(fd, F_GETFD), -1);
};
auto b = [](Comms* comms) {
// Send our STDERR to the thread.
ASSERT_THAT(comms->SendFD(STDERR_FILENO), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvEmptyTLV) {
auto a = [](Comms* comms) {
// Receive TLV without a value.
uint32_t tag;
std::vector<uint8_t> value;
ASSERT_THAT(comms->RecvTLV(&tag, &value), IsTrue()); // NOLINT
EXPECT_THAT(tag, Eq(0x00DEADBE));
EXPECT_THAT(value.size(), Eq(0));
};
auto b = [](Comms* comms) {
// Send TLV without a value.
ASSERT_THAT(comms->SendTLV(0x00DEADBE, 0, nullptr), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvEmptyTLV2) {
auto a = [](Comms* comms) {
// Receive TLV without a value.
uint32_t tag;
std::vector<uint8_t> data;
ASSERT_THAT(comms->RecvTLV(&tag, &data), IsTrue());
EXPECT_THAT(tag, Eq(0x00DEADBE));
EXPECT_THAT(data.size(), Eq(0));
};
auto b = [](Comms* comms) {
// Send TLV without a value.
ASSERT_THAT(comms->SendTLV(0x00DEADBE, 0, nullptr), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvProto) {
auto a = [](Comms* comms) {
// Receive a ProtoBuf.
std::unique_ptr<CommsTestMsg> comms_msg(new CommsTestMsg());
ASSERT_THAT(comms->RecvProtoBuf(comms_msg.get()), IsTrue());
ASSERT_THAT(comms_msg->value_size(), Eq(1));
EXPECT_THAT(comms_msg->value(0), Eq(kProtoStr));
};
auto b = [](Comms* comms) {
// Send a ProtoBuf.
std::unique_ptr<CommsTestMsg> comms_msg(new CommsTestMsg());
comms_msg->add_value(kProtoStr);
ASSERT_THAT(comms_msg->value_size(), Eq(1));
ASSERT_THAT(comms->SendProtoBuf(*comms_msg), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvStatusOK) {
auto a = [](Comms* comms) {
// Receive a good status.
absl::Status status;
ASSERT_THAT(comms->RecvStatus(&status), IsTrue());
EXPECT_THAT(status, IsOk());
};
auto b = [](Comms* comms) {
// Send a good status.
ASSERT_THAT(comms->SendStatus(absl::OkStatus()), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvStatusFailing) {
auto a = [](Comms* comms) {
// Receive a failing status.
absl::Status status;
ASSERT_THAT(comms->RecvStatus(&status), IsTrue());
EXPECT_THAT(status, Not(IsOk()));
EXPECT_THAT(status, StatusIs(absl::StatusCode::kInternal, "something odd"));
};
auto b = [](Comms* comms) {
// Send a failing status.
ASSERT_THAT(comms->SendStatus(
absl::Status{absl::StatusCode::kInternal, "something odd"}),
IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestUsesDistinctBuffers) {
auto a = [](Comms* comms) {
// Receive 1M bytes.
std::vector<uint8_t> buffer1, buffer2;
ASSERT_THAT(comms->RecvBytes(&buffer1), IsTrue()); // NOLINT
EXPECT_THAT(buffer1.size(), Eq(1024 * 1024));
ASSERT_THAT(comms->RecvBytes(&buffer2), IsTrue()); // NOLINT
EXPECT_THAT(buffer2.size(), Eq(1024 * 1024));
// Make sure we can access the buffer (memory was not free'd).
// Probably only useful when running with ASAN/MSAN.
EXPECT_THAT(buffer1[1024 * 1024 - 1], Eq(buffer1[1024 * 1024 - 1]));
EXPECT_THAT(buffer2[1024 * 1024 - 1], Eq(buffer2[1024 * 1024 - 1]));
EXPECT_NE(buffer1.data(), buffer2.data());
};
auto b = [](Comms* comms) {
// Send 1M bytes.
absl::FixedArray<uint8_t> buf(1024 * 1024);
memset(buf.data(), 0, buf.size());
ASSERT_THAT(comms->SendBytes(buf.data(), buf.size()), IsTrue());
ASSERT_THAT(comms->SendBytes(buf.data(), buf.size()), IsTrue());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvCredentials) {
auto a = [](Comms* comms) {
// Check credentials.
pid_t pid;
uid_t uid;
gid_t gid;
ASSERT_THAT(comms->RecvCreds(&pid, &uid, &gid), IsTrue());
EXPECT_THAT(pid, Eq(getpid()));
EXPECT_THAT(uid, Eq(getuid()));
EXPECT_THAT(gid, Eq(getgid()));
};
auto b = [](Comms* comms) {
// Nothing to do here.
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendTooMuchData) {
auto a = [](Comms* comms) {
// Nothing to do here.
};
auto b = [](Comms* comms) {
// Send too much data.
ASSERT_THAT(comms->SendBytes(nullptr, comms->GetMaxMsgSize() + 1),
IsFalse());
};
HandleCommunication(a, b);
}
TEST(CommsTest, TestSendRecvBytes) {
auto a = [](Comms* comms) {
std::vector<uint8_t> buffer;
ASSERT_THAT(comms->RecvBytes(&buffer), IsTrue());
ASSERT_THAT(comms->SendBytes(buffer), IsTrue());
};
auto b = [](Comms* comms) {
const std::vector<uint8_t> request = {0, 1, 2, 3, 7};
ASSERT_THAT(comms->SendBytes(request), IsTrue());
std::vector<uint8_t> response;
ASSERT_THAT(comms->RecvBytes(&response), IsTrue());
EXPECT_THAT(request, Eq(response));
};
HandleCommunication(a, b);
}
// We cannot test this in the Client or Server tests, as the endpoint needs to
// be unconnected.
TEST(CommsTest, TestMsgSize) {
// There will be no actual connection to this socket.
const std::string socket_name = "sandbox2_comms_msg_size_test";
Comms c(socket_name);
}
} // namespace sandbox2