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 <android-base/unique_fd.h>
     18 #include <cutils/ashmem.h>
     19 #include <gtest/gtest.h>
     20 #include <linux/fs.h>
     21 #include <sys/mman.h>
     22 
     23 using android::base::unique_fd;
     24 
     25 void TestCreateRegion(size_t size, unique_fd &fd, int prot) {
     26     fd = unique_fd(ashmem_create_region(nullptr, size));
     27     ASSERT_TRUE(fd >= 0);
     28     ASSERT_TRUE(ashmem_valid(fd));
     29     ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));
     30     ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
     31 }
     32 
     33 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
     34     *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);
     35     ASSERT_NE(MAP_FAILED, *region);
     36 }
     37 
     38 void TestProtDenied(const unique_fd &fd, size_t size, int prot) {
     39     EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
     40 }
     41 
     42 void TestProtIs(const unique_fd& fd, int prot) {
     43     EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));
     44 }
     45 
     46 void FillData(uint8_t* data, size_t dataLen) {
     47     for (size_t i = 0; i < dataLen; i++) {
     48         data[i] = i & 0xFF;
     49     }
     50 }
     51 
     52 TEST(AshmemTest, BasicTest) {
     53     constexpr size_t size = PAGE_SIZE;
     54     uint8_t data[size];
     55     FillData(data, size);
     56 
     57     unique_fd fd;
     58     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
     59 
     60     void *region1;
     61     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
     62 
     63     memcpy(region1, &data, size);
     64     ASSERT_EQ(0, memcmp(region1, &data, size));
     65 
     66     EXPECT_EQ(0, munmap(region1, size));
     67 
     68     void *region2;
     69     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region2));
     70     ASSERT_EQ(0, memcmp(region2, &data, size));
     71     EXPECT_EQ(0, munmap(region2, size));
     72 }
     73 
     74 TEST(AshmemTest, ForkTest) {
     75     constexpr size_t size = PAGE_SIZE;
     76     uint8_t data[size];
     77     FillData(data, size);
     78 
     79     unique_fd fd;
     80     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
     81 
     82     void *region1;
     83     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
     84 
     85     memcpy(region1, &data, size);
     86     ASSERT_EQ(0, memcmp(region1, &data, size));
     87     EXPECT_EQ(0, munmap(region1, size));
     88 
     89     ASSERT_EXIT({
     90         void *region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     91         if (region2 == MAP_FAILED) {
     92             _exit(1);
     93         }
     94         if (memcmp(region2, &data, size) != 0) {
     95             _exit(2);
     96         }
     97         memset(region2, 0, size);
     98         munmap(region2, size);
     99         _exit(0);
    100     }, ::testing::ExitedWithCode(0),"");
    101 
    102     memset(&data, 0, size);
    103     void *region2;
    104     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region2));
    105     ASSERT_EQ(0, memcmp(region2, &data, size));
    106     EXPECT_EQ(0, munmap(region2, size));
    107 }
    108 
    109 TEST(AshmemTest, FileOperationsTest) {
    110     unique_fd fd;
    111     void* region;
    112 
    113     // Allocate a 4-page buffer, but leave page-sized holes on either side
    114     constexpr size_t size = PAGE_SIZE * 4;
    115     constexpr size_t dataSize = PAGE_SIZE * 2;
    116     constexpr size_t holeSize = PAGE_SIZE;
    117     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
    118     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));
    119 
    120     uint8_t data[dataSize];
    121     FillData(data, dataSize);
    122     memcpy(region, data, dataSize);
    123 
    124     constexpr off_t dataStart = holeSize;
    125     constexpr off_t dataEnd = dataStart + dataSize;
    126 
    127     // The sequence of seeks below looks something like this:
    128     //
    129     // [    ][data][data][    ]
    130     // --^                          lseek(99, SEEK_SET)
    131     //   ------^                    lseek(dataStart, SEEK_CUR)
    132     // ------^                      lseek(0, SEEK_DATA)
    133     //       ------------^          lseek(dataStart, SEEK_HOLE)
    134     //                      ^--     lseek(-99, SEEK_END)
    135     //                ^------       lseek(-dataStart, SEEK_CUR)
    136     const struct {
    137         // lseek() parameters
    138         off_t offset;
    139         int whence;
    140         // Expected lseek() return value
    141         off_t ret;
    142     } seeks[] = {
    143         {99, SEEK_SET, 99},         {dataStart, SEEK_CUR, dataStart + 99},
    144         {0, SEEK_DATA, dataStart},  {dataStart, SEEK_HOLE, dataEnd},
    145         {-99, SEEK_END, size - 99}, {-dataStart, SEEK_CUR, dataEnd - 99},
    146     };
    147     for (const auto& cfg : seeks) {
    148         errno = 0;
    149         auto off = lseek(fd, cfg.offset, cfg.whence);
    150         ASSERT_EQ(cfg.ret, off) << "lseek(" << cfg.offset << ", " << cfg.whence << ") failed"
    151                                 << (errno ? ": " : "") << (errno ? strerror(errno) : "");
    152 
    153         if (off >= dataStart && off < dataEnd) {
    154             off_t dataOff = off - dataStart;
    155             ssize_t readSize = dataSize - dataOff;
    156             uint8_t buf[readSize];
    157 
    158             ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));
    159             EXPECT_EQ(0, memcmp(buf, data + dataOff, readSize));
    160         }
    161     }
    162 
    163     EXPECT_EQ(0, munmap(region, dataSize));
    164 }
    165 
    166 TEST(AshmemTest, ProtTest) {
    167     unique_fd fd;
    168     constexpr size_t size = PAGE_SIZE;
    169     void *region;
    170 
    171     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ));
    172     TestProtDenied(fd, size, PROT_WRITE);
    173     TestProtIs(fd, PROT_READ);
    174     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));
    175     EXPECT_EQ(0, munmap(region, size));
    176 
    177     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_WRITE));
    178     TestProtDenied(fd, size, PROT_READ);
    179     TestProtIs(fd, PROT_WRITE);
    180     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_WRITE, &region));
    181     EXPECT_EQ(0, munmap(region, size));
    182 
    183     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
    184     TestProtIs(fd, PROT_READ | PROT_WRITE);
    185     ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ));
    186     errno = 0;
    187     ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE))
    188         << "kernel shouldn't allow adding protection bits";
    189     EXPECT_EQ(EINVAL, errno);
    190     TestProtIs(fd, PROT_READ);
    191     TestProtDenied(fd, size, PROT_WRITE);
    192 }
    193 
    194 TEST(AshmemTest, ForkProtTest) {
    195     unique_fd fd;
    196     constexpr size_t size = PAGE_SIZE;
    197 
    198     int protFlags[] = { PROT_READ, PROT_WRITE };
    199     for (int i = 0; i < 2; i++) {
    200         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
    201         ASSERT_EXIT({
    202             if (ashmem_set_prot_region(fd, protFlags[i]) >= 0) {
    203                 _exit(0);
    204             } else {
    205                 _exit(1);
    206             }
    207         }, ::testing::ExitedWithCode(0), "");
    208         ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, protFlags[1-i]));
    209     }
    210 }
    211 
    212 TEST(AshmemTest, ForkMultiRegionTest) {
    213     constexpr size_t size = PAGE_SIZE;
    214     uint8_t data[size];
    215     FillData(data, size);
    216 
    217     constexpr int nRegions = 16;
    218     unique_fd fd[nRegions];
    219     for (int i = 0; i < nRegions; i++) {
    220         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE));
    221         void *region;
    222         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
    223         memcpy(region, &data, size);
    224         ASSERT_EQ(0, memcmp(region, &data, size));
    225         EXPECT_EQ(0, munmap(region, size));
    226     }
    227 
    228     ASSERT_EXIT({
    229         for (int i = 0; i < nRegions; i++) {
    230             void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd[i], 0);
    231             if (region == MAP_FAILED) {
    232                 _exit(1);
    233             }
    234             if (memcmp(region, &data, size) != 0) {
    235                 munmap(region, size);
    236                 _exit(2);
    237             }
    238             memset(region, 0, size);
    239             munmap(region, size);
    240         }
    241         _exit(0);
    242     }, ::testing::ExitedWithCode(0), "");
    243 
    244     memset(&data, 0, size);
    245     for (int i = 0; i < nRegions; i++) {
    246         void *region;
    247         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
    248         ASSERT_EQ(0, memcmp(region, &data, size));
    249         EXPECT_EQ(0, munmap(region, size));
    250     }
    251 }
    252