diff options
author | Josh Gao <jmgao@google.com> | 2019-01-07 19:16:21 -0800 |
---|---|---|
committer | Josh Gao <jmgao@google.com> | 2019-02-13 13:21:54 -0800 |
commit | 14f9500a35390e2e0252ccc1c03c9c850444e2b6 (patch) | |
tree | f077e9888c7faeea00fc2e8dc532152e772a7fce /base/cmsg_test.cpp | |
parent | 5272f9b017e2e32551ec53c680b9869b0ec135d2 (diff) | |
download | system_core-14f9500a35390e2e0252ccc1c03c9c850444e2b6.tar.gz system_core-14f9500a35390e2e0252ccc1c03c9c850444e2b6.tar.bz2 system_core-14f9500a35390e2e0252ccc1c03c9c850444e2b6.zip |
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
Diffstat (limited to 'base/cmsg_test.cpp')
-rw-r--r-- | base/cmsg_test.cpp | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/base/cmsg_test.cpp b/base/cmsg_test.cpp new file mode 100644 index 000000000..9ee5c8253 --- /dev/null +++ b/base/cmsg_test.cpp @@ -0,0 +1,202 @@ +/* + * 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 |