xref: /aosp_15_r20/external/perfetto/src/base/scoped_file_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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