Home | History | Annotate | Download | only in base
      1 
      2 /*
      3  * Copyright (C) 2017 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #include "perfetto/base/scoped_file.h"
     19 #include "perfetto/base/build_config.h"
     20 
     21 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
     22 #include <corecrt_io.h>
     23 #else
     24 #include <fcntl.h>
     25 #include <unistd.h>
     26 // Double closing of file handles on Windows leads to invocation of the invalid
     27 // parameter handler or asserts and therefore it cannot be tested, but it can
     28 // be tested on other platforms.
     29 #define TEST_INVALID_CLOSE
     30 #endif
     31 
     32 #include "gtest/gtest.h"
     33 
     34 namespace perfetto {
     35 namespace base {
     36 namespace {
     37 
     38 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
     39 const char kNullFilename[] = "NUL";
     40 const char kZeroFilename[] = "NUL";
     41 #else
     42 const char kNullFilename[] = "/dev/null";
     43 const char kZeroFilename[] = "/dev/zero";
     44 #endif
     45 
     46 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
     47   TEST(ScopedDirTest, CloseOutOfScope) {
     48   DIR* dir_handle = opendir(".");
     49   ASSERT_NE(nullptr, dir_handle);
     50   int dir_handle_fd = dirfd(dir_handle);
     51   ASSERT_GE(dir_handle_fd, 0);
     52   {
     53     ScopedDir scoped_dir(dir_handle);
     54     ASSERT_EQ(dir_handle, scoped_dir.get());
     55     ASSERT_TRUE(scoped_dir);
     56   }
     57   ASSERT_NE(0, close(dir_handle_fd));  // Should fail when closing twice.
     58 }
     59 #endif
     60 
     61 TEST(ScopedFileTest, CloseOutOfScope) {
     62   int raw_fd = open(kNullFilename, O_RDONLY);
     63   ASSERT_GE(raw_fd, 0);
     64   {
     65     ScopedFile scoped_file(raw_fd);
     66     ASSERT_EQ(raw_fd, scoped_file.get());
     67     ASSERT_EQ(raw_fd, *scoped_file);
     68     ASSERT_TRUE(scoped_file);
     69   }
     70 #ifdef TEST_INVALID_CLOSE
     71   ASSERT_NE(0, close(raw_fd));  // Should fail when closing twice.
     72 #endif
     73 }
     74 
     75 TEST(ScopedFstreamTest, CloseOutOfScope) {
     76   FILE* raw_stream = fopen(kNullFilename, "r");
     77   ASSERT_NE(nullptr, raw_stream);
     78   {
     79     ScopedFstream scoped_stream(raw_stream);
     80     ASSERT_EQ(raw_stream, scoped_stream.get());
     81     ASSERT_EQ(raw_stream, *scoped_stream);
     82     ASSERT_TRUE(scoped_stream);
     83   }
     84   // We don't have a direct way to see that the file was closed.
     85 }
     86 
     87 TEST(ScopedFileTest, Reset) {
     88   int raw_fd1 = open(kNullFilename, O_RDONLY);
     89   int raw_fd2 = open(kZeroFilename, O_RDONLY);
     90   ASSERT_GE(raw_fd1, 0);
     91   ASSERT_GE(raw_fd2, 0);
     92   {
     93     ScopedFile scoped_file(raw_fd1);
     94     ASSERT_EQ(raw_fd1, scoped_file.get());
     95     scoped_file.reset(raw_fd2);
     96     ASSERT_EQ(raw_fd2, scoped_file.get());
     97 #ifdef TEST_INVALID_CLOSE
     98     ASSERT_NE(0, close(raw_fd1));  // Should fail when closing twice.
     99 #endif
    100     scoped_file.reset();
    101 #ifdef TEST_INVALID_CLOSE
    102     ASSERT_NE(0, close(raw_fd2));
    103 #endif
    104     scoped_file.reset(open(kNullFilename, O_RDONLY));
    105     ASSERT_GE(scoped_file.get(), 0);
    106   }
    107 }
    108 
    109 TEST(ScopedFileTest, Release) {
    110   int raw_fd = open(kNullFilename, O_RDONLY);
    111   ASSERT_GE(raw_fd, 0);
    112   {
    113     ScopedFile scoped_file(raw_fd);
    114     ASSERT_EQ(raw_fd, scoped_file.release());
    115     ASSERT_FALSE(scoped_file);
    116   }
    117   ASSERT_EQ(0, close(raw_fd));
    118 }
    119 
    120 TEST(ScopedFileTest, MoveCtor) {
    121   int raw_fd1 = open(kNullFilename, O_RDONLY);
    122   int raw_fd2 = open(kZeroFilename, O_RDONLY);
    123   ASSERT_GE(raw_fd1, 0);
    124   ASSERT_GE(raw_fd2, 0);
    125   {
    126     ScopedFile scoped_file1(ScopedFile{raw_fd1});
    127     ScopedFile scoped_file2(std::move(scoped_file1));
    128     ASSERT_EQ(-1, scoped_file1.get());
    129     ASSERT_EQ(-1, *scoped_file1);
    130     ASSERT_FALSE(scoped_file1);
    131     ASSERT_EQ(raw_fd1, scoped_file2.get());
    132 
    133     scoped_file1.reset(raw_fd2);
    134     ASSERT_EQ(raw_fd2, scoped_file1.get());
    135   }
    136 #ifdef TEST_INVALID_CLOSE
    137   ASSERT_NE(0, close(raw_fd1));  // Should fail when closing twice.
    138   ASSERT_NE(0, close(raw_fd2));
    139 #endif
    140 }
    141 
    142 TEST(ScopedFileTest, MoveAssignment) {
    143   int raw_fd1 = open(kNullFilename, O_RDONLY);
    144   int raw_fd2 = open(kZeroFilename, O_RDONLY);
    145   ASSERT_GE(raw_fd1, 0);
    146   ASSERT_GE(raw_fd2, 0);
    147   {
    148     ScopedFile scoped_file1(raw_fd1);
    149     ScopedFile scoped_file2(raw_fd2);
    150     scoped_file2 = std::move(scoped_file1);
    151     ASSERT_EQ(-1, scoped_file1.get());
    152     ASSERT_FALSE(scoped_file1);
    153     ASSERT_EQ(raw_fd1, scoped_file2.get());
    154 #ifdef TEST_INVALID_CLOSE
    155     ASSERT_NE(0, close(raw_fd2));
    156 #endif
    157 
    158     scoped_file1 = std::move(scoped_file2);
    159     ASSERT_EQ(raw_fd1, scoped_file1.get());
    160     ASSERT_EQ(-1, scoped_file2.get());
    161   }
    162 #ifdef TEST_INVALID_CLOSE
    163   ASSERT_NE(0, close(raw_fd1));
    164 #endif
    165 }
    166 
    167 // File descriptors are capabilities and hence can be security critical. A
    168 // failed close() suggests the memory ownership of the file is wrong and we
    169 // might have leaked a capability.
    170 #ifdef TEST_INVALID_CLOSE
    171 TEST(ScopedFileTest, CloseFailureIsFatal) {
    172   int raw_fd = open(kNullFilename, O_RDONLY);
    173   ASSERT_DEATH(
    174       {
    175         ScopedFile scoped_file(raw_fd);
    176         ASSERT_EQ(0, close(raw_fd));
    177       },
    178       "");
    179 }
    180 #endif
    181 
    182 }  // namespace
    183 }  // namespace base
    184 }  // namespace perfetto
    185