Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <errno.h>
     18 #include <linux/fs.h>
     19 #include <stdint.h>
     20 #include <string.h>
     21 #include <sys/mman.h>
     22 #include <sys/types.h>
     23 #include <unistd.h>
     24 
     25 #include <android-base/macros.h>
     26 #include <android-base/unique_fd.h>
     27 #include <cutils/ashmem.h>
     28 #include <gtest/gtest.h>
     29 
     30 using android::base::unique_fd;
     31 
     32 void TestCreateRegion(size_t size, unique_fd &fd, int prot) {
     33     fd = unique_fd(ashmem_create_region(nullptr, size));
     34     ASSERT_TRUE(fd >= 0);
     35     ASSERT_TRUE(ashmem_valid(fd));
     36     ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));
     37     ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
     38 }
     39 
     40 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
     41     ASSERT_TRUE(fd >= 0);
     42     ASSERT_TRUE(ashmem_valid(fd));
     43     *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
     44     ASSERT_NE(MAP_FAILED, *region);
     45 }
     46 
     47 void TestProtDenied(const unique_fd &fd, size_t size, int prot) {
     48     ASSERT_TRUE(fd >= 0);
     49     ASSERT_TRUE(ashmem_valid(fd));
     50     EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
     51 }
     52 
     53 void TestProtIs(const unique_fd& fd, int prot) {
     54     ASSERT_TRUE(fd >= 0);
     55     ASSERT_TRUE(ashmem_valid(fd));
     56     EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
     57 }
     58 
     59 void FillData(uint8_t* data, size_t dataLen) {
     60     for (size_t i = 0; i < dataLen; i++) {
     61         data[i] = i & 0xFF;
     62     }
     63 }
     64 
     65 TEST(AshmemTest, BasicTest) {
     66     constexpr size_t size = PAGE_SIZE;
     67     uint8_t data[size];
     68     FillData(data, size);
     69 
     70     unique_fd fd;
     71     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
     72 
     73     void *region1;
     74     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
     75 
     76     memcpy(region1, &data, size);
     77     ASSERT_EQ(0, memcmp(region1, &data, size));
     78 
     79     EXPECT_EQ(0, munmap(region1, size));
     80 
     81     void *region2;
     82     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region2));
     83     ASSERT_EQ(0, memcmp(region2, &data, size));
     84     EXPECT_EQ(0, munmap(region2, size));
     85 }
     86 
     87 TEST(AshmemTest, ForkTest) {
     88     constexpr size_t size = PAGE_SIZE;
     89     uint8_t data[size];
     90     FillData(data, size);
     91 
     92     unique_fd fd;
     93     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
     94 
     95     void *region1;
     96     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
     97 
     98     memcpy(region1, &data, size);
     99     ASSERT_EQ(0, memcmp(region1, &data, size));
    100     EXPECT_EQ(0, munmap(region1, size));
    101 
    102     ASSERT_EXIT(
    103         {
    104             if (!ashmem_valid(fd)) {
    105                 _exit(3);
    106             }
    107             void* region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    108             if (region2 == MAP_FAILED) {
    109                 _exit(1);
    110             }
    111             if (memcmp(region2, &data, size) != 0) {
    112                 _exit(2);
    113             }
    114             memset(region2, 0, size);
    115             munmap(region2, size);
    116             _exit(0);
    117         },
    118         ::testing::ExitedWithCode(0), "");
    119 
    120     memset(&data, 0, size);
    121     void *region2;
    122     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region2));
    123     ASSERT_EQ(0, memcmp(region2, &data, size));
    124     EXPECT_EQ(0, munmap(region2, size));
    125 }
    126 
    127 TEST(AshmemTest, FileOperationsTest) {
    128     unique_fd fd;
    129     void* region;
    130 
    131     // Allocate a 4-page buffer, but leave page-sized holes on either side
    132     constexpr size_t size = PAGE_SIZE * 4;
    133     constexpr size_t dataSize = PAGE_SIZE * 2;
    134     constexpr size_t holeSize = PAGE_SIZE;
    135     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
    136     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));
    137 
    138     uint8_t data[dataSize];
    139     FillData(data, dataSize);
    140     memcpy(region, data, dataSize);
    141 
    142     constexpr off_t dataStart = holeSize;
    143     constexpr off_t dataEnd = dataStart + dataSize;
    144 
    145     // The sequence of seeks below looks something like this:
    146     //
    147     // [    ][data][data][    ]
    148     // --^                          lseek(99, SEEK_SET)
    149     //   ------^                    lseek(dataStart, SEEK_CUR)
    150     // ------^                      lseek(0, SEEK_DATA)
    151     //       ------------^          lseek(dataStart, SEEK_HOLE)
    152     //                      ^--     lseek(-99, SEEK_END)
    153     //                ^------       lseek(-dataStart, SEEK_CUR)
    154     const struct {
    155         // lseek() parameters
    156         off_t offset;
    157         int whence;
    158         // Expected lseek() return value
    159         off_t ret;
    160     } seeks[] = {
    161         {99, SEEK_SET, 99},         {dataStart, SEEK_CUR, dataStart + 99},
    162         {0, SEEK_DATA, dataStart},  {dataStart, SEEK_HOLE, dataEnd},
    163         {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
    164     };
    165     for (const auto& cfg : seeks) {
    166         errno = 0;
    167         ASSERT_TRUE(ashmem_valid(fd));
    168         auto off = lseek(fd, cfg.offset, cfg.whence);
    169         ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
    170                                 << (errno ? ": " : "") << (errno ? strerror(errno) : "");
    171 
    172         if (off >= dataStart && off < dataEnd) {
    173             off_t dataOff = off - dataStart;
    174             ssize_t readSize = dataSize - dataOff;
    175             uint8_t buf[readSize];
    176 
    177             ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
    178             EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
    179         }
    180     }
    181 
    182     EXPECT_EQ(0, munmap(region, dataSize));
    183 }
    184 
    185 TEST(AshmemTest, ProtTest) {
    186     unique_fd fd;
    187     constexpr size_t size = PAGE_SIZE;
    188     void *region;
    189 
    190     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
    191     TestProtDenied(fd, size, PROT_WRITE);
    192     TestProtIs(fd, PROT_READ);
    193     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
    194     EXPECT_EQ(0, munmap(region, size));
    195 
    196     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
    197     TestProtDenied(fd, size, PROT_READ);
    198     TestProtIs(fd, PROT_WRITE);
    199     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
    200     EXPECT_EQ(0, munmap(region, size));
    201 
    202     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
    203     TestProtIs(fd, PROT_READ | PROT_WRITE);
    204     ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
    205     errno = 0;
    206     ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
    207         << "kernel shouldn't allow adding protection bits";
    208     EXPECT_EQ(EINVAL, errno);
    209     TestProtIs(fd, PROT_READ);
    210     TestProtDenied(fd, size, PROT_WRITE);
    211 }
    212 
    213 TEST(AshmemTest, ForkProtTest) {
    214     unique_fd fd;
    215     constexpr size_t size = PAGE_SIZE;
    216 
    217     int protFlags[] = { PROT_READ, PROT_WRITE };
    218     for (size_t i = 0; i < arraysize(protFlags); i++) {
    219         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
    220         ASSERT_EXIT(
    221             {
    222                 if (!ashmem_valid(fd)) {
    223                     _exit(3);
    224                 } else if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
    225                     _exit(0);
    226                 } else {
    227                     _exit(1);
    228                 }
    229             },
    230             ::testing::ExitedWithCode(0), "");
    231         ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i]));
    232     }
    233 }
    234 
    235 TEST(AshmemTest, ForkMultiRegionTest) {
    236     constexpr size_t size = PAGE_SIZE;
    237     uint8_t data[size];
    238     FillData(data, size);
    239 
    240     constexpr int nRegions = 16;
    241     unique_fd fd[nRegions];
    242     for (int i = 0; i < nRegions; i++) {
    243         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE));
    244         void *region;
    245         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
    246         memcpy(region, &data, size);
    247         ASSERT_EQ(0, memcmp(region, &data, size));
    248         EXPECT_EQ(0, munmap(region, size));
    249     }
    250 
    251     ASSERT_EXIT({
    252         for (int i = 0; i < nRegions; i++) {
    253             if (!ashmem_valid(fd[i])) {
    254                 _exit(3);
    255             }
    256             void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
    257             if (region == MAP_FAILED) {
    258                 _exit(1);
    259             }
    260             if (memcmp(region, &data, size) != 0) {
    261                 munmap(region, size);
    262                 _exit(2);
    263             }
    264             memset(region, 0, size);
    265             munmap(region, size);
    266         }
    267         _exit(0);
    268     }, ::testing::ExitedWithCode(0), "");
    269 
    270     memset(&data, 0, size);
    271     for (int i = 0; i < nRegions; i++) {
    272         void *region;
    273         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
    274         ASSERT_EQ(0, memcmp(region, &data, size));
    275         EXPECT_EQ(0, munmap(region, size));
    276     }
    277 }
    278