platform_system_core/base/cmsg_test.cpp
Josh Gao 14f9500a35 base: add helpers for sending/receiving file descriptors.
Almost all of the uses of cmsg(3) in our source tree are wrong in at
least one of the following ways:

  - not aligning the cmsg buffer
  - leaking fds if more fds are received than expected
  - blindly dereferencing CMSG_DATA without checking the header
  - using CMSG_SPACE instead of CMSG_LEN for .cmsg_len
  - using CMSG_LEN instead of CMSG_SPACE for .msg_controllen
  - using a length specified in number of fds instead of bytes

Implement wrapper functions that implement this dumpster fire of an
API correctly.

Bug: http://b/122047630
Test: libbase_test32
Change-Id: I6ac34d67bbbf1bfa9727ab598248fc178ea19df9
2019-02-13 13:21:54 -08:00

202 lines
5.6 KiB
C++

/*
* Copyright (C) 2019 The Android Open Source Project
*
* 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
*
* http://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.
*/
#include <android-base/cmsg.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#if !defined(_WIN32)
using android::base::ReceiveFileDescriptors;
using android::base::SendFileDescriptors;
using android::base::unique_fd;
static ino_t GetInode(int fd) {
struct stat st;
if (fstat(fd, &st) != 0) {
PLOG(FATAL) << "fstat failed";
}
return st.st_ino;
}
struct CmsgTest : ::testing::TestWithParam<bool> {
bool Seqpacket() { return GetParam(); }
void SetUp() override {
ASSERT_TRUE(
android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv));
int dup1 = dup(tmp1.fd);
ASSERT_NE(-1, dup1);
int dup2 = dup(tmp2.fd);
ASSERT_NE(-1, dup2);
fd1.reset(dup1);
fd2.reset(dup2);
ino1 = GetInode(dup1);
ino2 = GetInode(dup2);
}
unique_fd send;
unique_fd recv;
TemporaryFile tmp1;
TemporaryFile tmp2;
unique_fd fd1;
unique_fd fd2;
ino_t ino1;
ino_t ino2;
};
TEST_P(CmsgTest, smoke) {
ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get()));
char buf[2];
unique_fd received;
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received));
ASSERT_EQ('x', buf[0]);
ASSERT_NE(-1, received.get());
ASSERT_EQ(ino1, GetInode(received.get()));
}
TEST_P(CmsgTest, msg_trunc) {
ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get()));
char buf[2];
unique_fd received1, received2;
ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2);
if (Seqpacket()) {
ASSERT_EQ(-1, rc);
ASSERT_EQ(EMSGSIZE, errno);
ASSERT_EQ(-1, received1.get());
ASSERT_EQ(-1, received2.get());
} else {
ASSERT_EQ(1, rc);
ASSERT_NE(-1, received1.get());
ASSERT_NE(-1, received2.get());
ASSERT_EQ(ino1, GetInode(received1.get()));
ASSERT_EQ(ino2, GetInode(received2.get()));
ASSERT_EQ(1, read(recv.get(), buf, 2));
}
}
TEST_P(CmsgTest, msg_ctrunc) {
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
char buf[2];
unique_fd received;
ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
ASSERT_EQ(EMSGSIZE, errno);
ASSERT_EQ(-1, received.get());
}
TEST_P(CmsgTest, peek) {
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
char buf[2];
ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK));
ASSERT_EQ('a', buf[0]);
unique_fd received;
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received));
ASSERT_EQ(ino1, GetInode(received.get()));
}
TEST_P(CmsgTest, stream_fd_association) {
if (Seqpacket()) {
return;
}
// fds are associated with the first byte of the write.
ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1)));
ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get()));
ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get()));
char buf[2];
ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2)));
ASSERT_EQ(0, memcmp(buf, "ab", 2));
std::vector<unique_fd> received1;
ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1);
ASSERT_EQ(1, rc);
ASSERT_EQ('c', buf[0]);
ASSERT_TRUE(received1.empty());
unique_fd received2;
rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2);
ASSERT_EQ(1, rc);
ASSERT_EQ('d', buf[0]);
ASSERT_EQ(ino2, GetInode(received2.get()));
}
TEST_P(CmsgTest, multiple_fd_ordering) {
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get()));
char buf[2];
unique_fd received1, received2;
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2));
ASSERT_NE(-1, received1.get());
ASSERT_NE(-1, received2.get());
ASSERT_EQ(ino1, GetInode(received1.get()));
ASSERT_EQ(ino2, GetInode(received2.get()));
}
TEST_P(CmsgTest, separate_fd_ordering) {
ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get()));
ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get()));
char buf[2];
unique_fd received1, received2;
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1));
ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2));
ASSERT_NE(-1, received1.get());
ASSERT_NE(-1, received2.get());
ASSERT_EQ(ino1, GetInode(received1.get()));
ASSERT_EQ(ino2, GetInode(received2.get()));
}
TEST_P(CmsgTest, separate_fds_no_coalescing) {
unique_fd sent1(dup(tmp1.fd));
unique_fd sent2(dup(tmp2.fd));
ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get()));
ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get()));
char buf[2];
std::vector<unique_fd> received;
ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
ASSERT_EQ(1U, received.size());
ASSERT_EQ(ino1, GetInode(received[0].get()));
ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received));
ASSERT_EQ(1U, received.size());
ASSERT_EQ(ino2, GetInode(received[0].get()));
}
INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool());
#endif