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