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/ext/base/scoped_file.h"
19 #include "perfetto/base/build_config.h"
20 
21 #include <fcntl.h>
22 
23 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
24 #include <io.h>
25 #else
26 #include <unistd.h>
27 // Double closing of file handles on Windows leads to invocation of the invalid
28 // parameter handler or asserts and therefore it cannot be tested, but it can
29 // be tested on other platforms.
30 #define TEST_INVALID_CLOSE
31 #endif
32 
33 #include "test/gtest_and_gmock.h"
34 
35 namespace perfetto {
36 namespace base {
37 namespace {
38 
39 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
40 const char kNullFilename[] = "NUL";
41 const char kZeroFilename[] = "NUL";
42 #else
43 const char kNullFilename[] = "/dev/null";
44 const char kZeroFilename[] = "/dev/zero";
45 #endif
46 
47 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
TEST(ScopedDirTest,CloseOutOfScope)48   TEST(ScopedDirTest, CloseOutOfScope) {
49   DIR* dir_handle = opendir(".");
50   ASSERT_NE(nullptr, dir_handle);
51   int dir_handle_fd = dirfd(dir_handle);
52   ASSERT_GE(dir_handle_fd, 0);
53   {
54     ScopedDir scoped_dir(dir_handle);
55     ASSERT_EQ(dir_handle, scoped_dir.get());
56     ASSERT_TRUE(scoped_dir);
57   }
58   ASSERT_NE(0, close(dir_handle_fd));  // Should fail when closing twice.
59 }
60 #endif
61 
TEST(ScopedFileTest,CloseOutOfScope)62 TEST(ScopedFileTest, CloseOutOfScope) {
63   int raw_fd = open(kNullFilename, O_RDONLY);
64   ASSERT_GE(raw_fd, 0);
65   {
66     ScopedFile scoped_file(raw_fd);
67     ASSERT_EQ(raw_fd, scoped_file.get());
68     ASSERT_EQ(raw_fd, *scoped_file);
69     ASSERT_TRUE(scoped_file);
70   }
71 #ifdef TEST_INVALID_CLOSE
72   ASSERT_NE(0, close(raw_fd));  // Should fail when closing twice.
73 #endif
74 }
75 
TEST(ScopedFstreamTest,CloseOutOfScope)76 TEST(ScopedFstreamTest, CloseOutOfScope) {
77   FILE* raw_stream = fopen(kNullFilename, "r");
78   ASSERT_NE(nullptr, raw_stream);
79   {
80     ScopedFstream scoped_stream(raw_stream);
81     ASSERT_EQ(raw_stream, scoped_stream.get());
82     ASSERT_EQ(raw_stream, *scoped_stream);
83     ASSERT_TRUE(scoped_stream);
84   }
85   // We don't have a direct way to see that the file was closed.
86 }
87 
TEST(ScopedFileTest,Reset)88 TEST(ScopedFileTest, Reset) {
89   int raw_fd1 = open(kNullFilename, O_RDONLY);
90   int raw_fd2 = open(kZeroFilename, O_RDONLY);
91   ASSERT_GE(raw_fd1, 0);
92   ASSERT_GE(raw_fd2, 0);
93   {
94     ScopedFile scoped_file(raw_fd1);
95     ASSERT_EQ(raw_fd1, scoped_file.get());
96     scoped_file.reset(raw_fd2);
97     ASSERT_EQ(raw_fd2, scoped_file.get());
98 #ifdef TEST_INVALID_CLOSE
99     ASSERT_NE(0, close(raw_fd1));  // Should fail when closing twice.
100 #endif
101     scoped_file.reset();
102 #ifdef TEST_INVALID_CLOSE
103     ASSERT_NE(0, close(raw_fd2));
104 #endif
105     scoped_file.reset(open(kNullFilename, O_RDONLY));
106     ASSERT_GE(scoped_file.get(), 0);
107   }
108 }
109 
TEST(ScopedFileTest,Release)110 TEST(ScopedFileTest, Release) {
111   int raw_fd = open(kNullFilename, O_RDONLY);
112   ASSERT_GE(raw_fd, 0);
113   {
114     ScopedFile scoped_file(raw_fd);
115     ASSERT_EQ(raw_fd, scoped_file.release());
116     ASSERT_FALSE(scoped_file);
117   }
118   ASSERT_EQ(0, close(raw_fd));
119 }
120 
TEST(ScopedFileTest,MoveCtor)121 TEST(ScopedFileTest, MoveCtor) {
122   int raw_fd1 = open(kNullFilename, O_RDONLY);
123   int raw_fd2 = open(kZeroFilename, O_RDONLY);
124   ASSERT_GE(raw_fd1, 0);
125   ASSERT_GE(raw_fd2, 0);
126   {
127     ScopedFile scoped_file1(ScopedFile{raw_fd1});
128     ScopedFile scoped_file2(std::move(scoped_file1));
129     ASSERT_EQ(-1, scoped_file1.get());
130     ASSERT_EQ(-1, *scoped_file1);
131     ASSERT_FALSE(scoped_file1);
132     ASSERT_EQ(raw_fd1, scoped_file2.get());
133 
134     scoped_file1.reset(raw_fd2);
135     ASSERT_EQ(raw_fd2, scoped_file1.get());
136   }
137 #ifdef TEST_INVALID_CLOSE
138   ASSERT_NE(0, close(raw_fd1));  // Should fail when closing twice.
139   ASSERT_NE(0, close(raw_fd2));
140 #endif
141 }
142 
TEST(ScopedFileTest,MoveAssignment)143 TEST(ScopedFileTest, MoveAssignment) {
144   int raw_fd1 = open(kNullFilename, O_RDONLY);
145   int raw_fd2 = open(kZeroFilename, O_RDONLY);
146   ASSERT_GE(raw_fd1, 0);
147   ASSERT_GE(raw_fd2, 0);
148   {
149     ScopedFile scoped_file1(raw_fd1);
150     ScopedFile scoped_file2(raw_fd2);
151     scoped_file2 = std::move(scoped_file1);
152     ASSERT_EQ(-1, scoped_file1.get());
153     ASSERT_FALSE(scoped_file1);
154     ASSERT_EQ(raw_fd1, scoped_file2.get());
155 #ifdef TEST_INVALID_CLOSE
156     ASSERT_NE(0, close(raw_fd2));
157 #endif
158 
159     scoped_file1 = std::move(scoped_file2);
160     ASSERT_EQ(raw_fd1, scoped_file1.get());
161     ASSERT_EQ(-1, scoped_file2.get());
162   }
163 #ifdef TEST_INVALID_CLOSE
164   ASSERT_NE(0, close(raw_fd1));
165 #endif
166 }
167 
168 // File descriptors are capabilities and hence can be security critical. A
169 // failed close() suggests the memory ownership of the file is wrong and we
170 // might have leaked a capability.
171 #ifdef TEST_INVALID_CLOSE
TEST(ScopedFileTest,CloseFailureIsFatal)172 TEST(ScopedFileTest, CloseFailureIsFatal) {
173   int raw_fd = open(kNullFilename, O_RDONLY);
174   ASSERT_DEATH_IF_SUPPORTED(
175       {
176         ScopedFile scoped_file(raw_fd);
177         ASSERT_EQ(0, close(raw_fd));
178       },
179       "");
180 }
181 #endif
182 
183 }  // namespace
184 }  // namespace base
185 }  // namespace perfetto
186