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 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
26 #include <lib/fdio/fdio.h>
27 #else
28 #include <unistd.h>
29 // Double closing of file handles on Windows leads to invocation of the invalid
30 // parameter handler or asserts and therefore it cannot be tested, but it can
31 // be tested on other platforms.
32 #define TEST_INVALID_CLOSE
33 #endif
34
35 #include "test/gtest_and_gmock.h"
36
37 namespace perfetto {
38 namespace base {
39 namespace {
40
OpenDevNull()41 int OpenDevNull() {
42 #if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
43 return fdio_fd_create_null();
44 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
45 return open("NUL", O_RDONLY);
46 #else
47 return open("/dev/null", O_RDONLY);
48 #endif
49 }
50
OpenDevNullStream()51 FILE* OpenDevNullStream() {
52 #if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
53 return fdopen(fdio_fd_create_null(), "r");
54 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
55 return fopen("NUL", "r");
56 #else
57 return fopen("/dev/null", "r");
58 #endif
59 }
60
61 // Returns a file descriptor to some file. On Fuchsia: returns a descriptor of a
62 // file in /tmp. On other platforms: returns a descriptor of /dev/zero.
MakeSecondFileDescriptor()63 int MakeSecondFileDescriptor() {
64 #if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
65 // Create a random file in /tmp and unlink it straight away since its name
66 // never need be known or uttered.
67 char path[] = "/tmp/sfuXXXXXX";
68 const int fd = mkstemp(&path[0]);
69 if (fd >= 0)
70 unlink(path);
71 return fd;
72 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
73 return open("NUL", O_RDONLY);
74 #else
75 return open("/dev/zero", O_RDONLY);
76 #endif
77 }
78
79 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
TEST(ScopedDirTest,CloseOutOfScope)80 TEST(ScopedDirTest, CloseOutOfScope) {
81 DIR* dir_handle = opendir(".");
82 ASSERT_NE(nullptr, dir_handle);
83 int dir_handle_fd = dirfd(dir_handle);
84 ASSERT_GE(dir_handle_fd, 0);
85 {
86 ScopedDir scoped_dir(dir_handle);
87 ASSERT_EQ(dir_handle, scoped_dir.get());
88 ASSERT_TRUE(scoped_dir);
89 }
90 ASSERT_NE(0, close(dir_handle_fd)); // Should fail when closing twice.
91 }
92 #endif
93
TEST(ScopedFileTest,CloseOutOfScope)94 TEST(ScopedFileTest, CloseOutOfScope) {
95 int raw_fd = OpenDevNull();
96 ASSERT_GE(raw_fd, 0);
97 {
98 ScopedFile scoped_file(raw_fd);
99 ASSERT_EQ(raw_fd, scoped_file.get());
100 ASSERT_EQ(raw_fd, *scoped_file);
101 ASSERT_TRUE(scoped_file);
102 }
103 #ifdef TEST_INVALID_CLOSE
104 ASSERT_NE(0, close(raw_fd)); // Should fail when closing twice.
105 #endif
106 }
107
TEST(ScopedFstreamTest,CloseOutOfScope)108 TEST(ScopedFstreamTest, CloseOutOfScope) {
109 FILE* raw_stream = OpenDevNullStream();
110 ASSERT_NE(nullptr, raw_stream);
111 {
112 ScopedFstream scoped_stream(raw_stream);
113 ASSERT_EQ(raw_stream, scoped_stream.get());
114 ASSERT_EQ(raw_stream, *scoped_stream);
115 ASSERT_TRUE(scoped_stream);
116 }
117 // We don't have a direct way to see that the file was closed.
118 }
119
TEST(ScopedFileTest,Reset)120 TEST(ScopedFileTest, Reset) {
121 int raw_fd1 = OpenDevNull();
122 int raw_fd2 = MakeSecondFileDescriptor();
123 ASSERT_GE(raw_fd1, 0);
124 ASSERT_GE(raw_fd2, 0);
125 {
126 ScopedFile scoped_file(raw_fd1);
127 ASSERT_EQ(raw_fd1, scoped_file.get());
128 scoped_file.reset(raw_fd2);
129 ASSERT_EQ(raw_fd2, scoped_file.get());
130 #ifdef TEST_INVALID_CLOSE
131 ASSERT_NE(0, close(raw_fd1)); // Should fail when closing twice.
132 #endif
133 scoped_file.reset();
134 #ifdef TEST_INVALID_CLOSE
135 ASSERT_NE(0, close(raw_fd2));
136 #endif
137 scoped_file.reset(OpenDevNull());
138 ASSERT_GE(scoped_file.get(), 0);
139 }
140 }
141
TEST(ScopedFileTest,Release)142 TEST(ScopedFileTest, Release) {
143 int raw_fd = OpenDevNull();
144 ASSERT_GE(raw_fd, 0);
145 {
146 ScopedFile scoped_file(raw_fd);
147 ASSERT_EQ(raw_fd, scoped_file.release());
148 ASSERT_FALSE(scoped_file);
149 }
150 ASSERT_EQ(0, close(raw_fd));
151 }
152
TEST(ScopedFileTest,MoveCtor)153 TEST(ScopedFileTest, MoveCtor) {
154 int raw_fd1 = OpenDevNull();
155 int raw_fd2 = MakeSecondFileDescriptor();
156 ASSERT_GE(raw_fd1, 0);
157 ASSERT_GE(raw_fd2, 0);
158 {
159 ScopedFile scoped_file1(ScopedFile{raw_fd1});
160 ScopedFile scoped_file2(std::move(scoped_file1));
161 ASSERT_EQ(-1, scoped_file1.get());
162 ASSERT_EQ(-1, *scoped_file1);
163 ASSERT_FALSE(scoped_file1);
164 ASSERT_EQ(raw_fd1, scoped_file2.get());
165
166 scoped_file1.reset(raw_fd2);
167 ASSERT_EQ(raw_fd2, scoped_file1.get());
168 }
169 #ifdef TEST_INVALID_CLOSE
170 ASSERT_NE(0, close(raw_fd1)); // Should fail when closing twice.
171 ASSERT_NE(0, close(raw_fd2));
172 #endif
173 }
174
TEST(ScopedFileTest,MoveAssignment)175 TEST(ScopedFileTest, MoveAssignment) {
176 int raw_fd1 = OpenDevNull();
177 int raw_fd2 = MakeSecondFileDescriptor();
178 ASSERT_GE(raw_fd1, 0);
179 ASSERT_GE(raw_fd2, 0);
180 {
181 ScopedFile scoped_file1(raw_fd1);
182 ScopedFile scoped_file2(raw_fd2);
183 scoped_file2 = std::move(scoped_file1);
184 ASSERT_EQ(-1, scoped_file1.get());
185 ASSERT_FALSE(scoped_file1);
186 ASSERT_EQ(raw_fd1, scoped_file2.get());
187 #ifdef TEST_INVALID_CLOSE
188 ASSERT_NE(0, close(raw_fd2));
189 #endif
190
191 scoped_file1 = std::move(scoped_file2);
192 ASSERT_EQ(raw_fd1, scoped_file1.get());
193 ASSERT_EQ(-1, scoped_file2.get());
194 }
195 #ifdef TEST_INVALID_CLOSE
196 ASSERT_NE(0, close(raw_fd1));
197 #endif
198 }
199
200 // File descriptors are capabilities and hence can be security critical. A
201 // failed close() suggests the memory ownership of the file is wrong and we
202 // might have leaked a capability.
203 #ifdef TEST_INVALID_CLOSE
TEST(ScopedFileTest,CloseFailureIsFatal)204 TEST(ScopedFileTest, CloseFailureIsFatal) {
205 int raw_fd = OpenDevNull();
206 ASSERT_DEATH_IF_SUPPORTED(
207 {
208 ScopedFile scoped_file(raw_fd);
209 ASSERT_EQ(0, close(raw_fd));
210 },
211 "");
212 }
213 #endif
214
215 } // namespace
216 } // namespace base
217 } // namespace perfetto
218