1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/posix/file_descriptor_shuffle.h"
6
7 #include "testing/gtest/include/gtest/gtest.h"
8
9 namespace {
10
11 // 'Duplicated' file descriptors start at this number
12 const int kDuplicateBase = 1000;
13
14 } // namespace
15
16 namespace base {
17
18 struct Action {
19 enum Type {
20 CLOSE,
21 MOVE,
22 DUPLICATE,
23 };
24
Actionbase::Action25 Action(Type in_type, int in_fd1, int in_fd2 = -1)
26 : type(in_type),
27 fd1(in_fd1),
28 fd2(in_fd2) {
29 }
30
operator ==base::Action31 bool operator==(const Action& other) const {
32 return other.type == type &&
33 other.fd1 == fd1 &&
34 other.fd2 == fd2;
35 }
36
37 Type type;
38 int fd1;
39 int fd2;
40 };
41
42 class InjectionTracer : public InjectionDelegate {
43 public:
InjectionTracer()44 InjectionTracer()
45 : next_duplicate_(kDuplicateBase) {
46 }
47
Duplicate(int * result,int fd)48 bool Duplicate(int* result, int fd) override {
49 *result = next_duplicate_++;
50 actions_.push_back(Action(Action::DUPLICATE, *result, fd));
51 return true;
52 }
53
Move(int src,int dest)54 bool Move(int src, int dest) override {
55 actions_.push_back(Action(Action::MOVE, src, dest));
56 return true;
57 }
58
Close(int fd)59 void Close(int fd) override { actions_.push_back(Action(Action::CLOSE, fd)); }
60
actions() const61 const std::vector<Action>& actions() const { return actions_; }
62
63 private:
64 int next_duplicate_;
65 std::vector<Action> actions_;
66 };
67
TEST(FileDescriptorShuffleTest,Empty)68 TEST(FileDescriptorShuffleTest, Empty) {
69 InjectiveMultimap map;
70 InjectionTracer tracer;
71
72 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
73 EXPECT_EQ(0u, tracer.actions().size());
74 }
75
TEST(FileDescriptorShuffleTest,Noop)76 TEST(FileDescriptorShuffleTest, Noop) {
77 InjectiveMultimap map;
78 InjectionTracer tracer;
79 map.push_back(InjectionArc(0, 0, false));
80
81 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
82 EXPECT_EQ(0u, tracer.actions().size());
83 }
84
TEST(FileDescriptorShuffleTest,NoopAndClose)85 TEST(FileDescriptorShuffleTest, NoopAndClose) {
86 InjectiveMultimap map;
87 InjectionTracer tracer;
88 map.push_back(InjectionArc(0, 0, true));
89
90 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
91 EXPECT_EQ(0u, tracer.actions().size());
92 }
93
TEST(FileDescriptorShuffleTest,Simple1)94 TEST(FileDescriptorShuffleTest, Simple1) {
95 InjectiveMultimap map;
96 InjectionTracer tracer;
97 map.push_back(InjectionArc(0, 1, false));
98
99 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
100 ASSERT_EQ(1u, tracer.actions().size());
101 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
102 }
103
TEST(FileDescriptorShuffleTest,Simple2)104 TEST(FileDescriptorShuffleTest, Simple2) {
105 InjectiveMultimap map;
106 InjectionTracer tracer;
107 map.push_back(InjectionArc(0, 1, false));
108 map.push_back(InjectionArc(2, 3, false));
109
110 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
111 ASSERT_EQ(2u, tracer.actions().size());
112 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
113 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 2, 3));
114 }
115
TEST(FileDescriptorShuffleTest,Simple3)116 TEST(FileDescriptorShuffleTest, Simple3) {
117 InjectiveMultimap map;
118 InjectionTracer tracer;
119 map.push_back(InjectionArc(0, 1, true));
120
121 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
122 ASSERT_EQ(2u, tracer.actions().size());
123 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
124 EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 0));
125 }
126
TEST(FileDescriptorShuffleTest,Simple4)127 TEST(FileDescriptorShuffleTest, Simple4) {
128 InjectiveMultimap map;
129 InjectionTracer tracer;
130 map.push_back(InjectionArc(10, 0, true));
131 map.push_back(InjectionArc(1, 1, true));
132
133 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
134 ASSERT_EQ(2u, tracer.actions().size());
135 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 10, 0));
136 EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 10));
137 }
138
TEST(FileDescriptorShuffleTest,Cycle)139 TEST(FileDescriptorShuffleTest, Cycle) {
140 InjectiveMultimap map;
141 InjectionTracer tracer;
142 map.push_back(InjectionArc(0, 1, false));
143 map.push_back(InjectionArc(1, 0, false));
144
145 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
146 ASSERT_EQ(4u, tracer.actions().size());
147 EXPECT_TRUE(tracer.actions()[0] ==
148 Action(Action::DUPLICATE, kDuplicateBase, 1));
149 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
150 EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
151 EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
152 }
153
TEST(FileDescriptorShuffleTest,CycleAndClose1)154 TEST(FileDescriptorShuffleTest, CycleAndClose1) {
155 InjectiveMultimap map;
156 InjectionTracer tracer;
157 map.push_back(InjectionArc(0, 1, true));
158 map.push_back(InjectionArc(1, 0, false));
159
160 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
161 ASSERT_EQ(4u, tracer.actions().size());
162 EXPECT_TRUE(tracer.actions()[0] ==
163 Action(Action::DUPLICATE, kDuplicateBase, 1));
164 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
165 EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
166 EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
167 }
168
TEST(FileDescriptorShuffleTest,CycleAndClose2)169 TEST(FileDescriptorShuffleTest, CycleAndClose2) {
170 InjectiveMultimap map;
171 InjectionTracer tracer;
172 map.push_back(InjectionArc(0, 1, false));
173 map.push_back(InjectionArc(1, 0, true));
174
175 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
176 ASSERT_EQ(4u, tracer.actions().size());
177 EXPECT_TRUE(tracer.actions()[0] ==
178 Action(Action::DUPLICATE, kDuplicateBase, 1));
179 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
180 EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
181 EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
182 }
183
TEST(FileDescriptorShuffleTest,CycleAndClose3)184 TEST(FileDescriptorShuffleTest, CycleAndClose3) {
185 InjectiveMultimap map;
186 InjectionTracer tracer;
187 map.push_back(InjectionArc(0, 1, true));
188 map.push_back(InjectionArc(1, 0, true));
189
190 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
191 ASSERT_EQ(4u, tracer.actions().size());
192 EXPECT_TRUE(tracer.actions()[0] ==
193 Action(Action::DUPLICATE, kDuplicateBase, 1));
194 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
195 EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0));
196 EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase));
197 }
198
TEST(FileDescriptorShuffleTest,Fanout)199 TEST(FileDescriptorShuffleTest, Fanout) {
200 InjectiveMultimap map;
201 InjectionTracer tracer;
202 map.push_back(InjectionArc(0, 1, false));
203 map.push_back(InjectionArc(0, 2, false));
204
205 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
206 ASSERT_EQ(2u, tracer.actions().size());
207 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
208 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
209 }
210
TEST(FileDescriptorShuffleTest,FanoutAndClose1)211 TEST(FileDescriptorShuffleTest, FanoutAndClose1) {
212 InjectiveMultimap map;
213 InjectionTracer tracer;
214 map.push_back(InjectionArc(0, 1, true));
215 map.push_back(InjectionArc(0, 2, false));
216
217 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
218 ASSERT_EQ(3u, tracer.actions().size());
219 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
220 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
221 EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
222 }
223
TEST(FileDescriptorShuffleTest,FanoutAndClose2)224 TEST(FileDescriptorShuffleTest, FanoutAndClose2) {
225 InjectiveMultimap map;
226 InjectionTracer tracer;
227 map.push_back(InjectionArc(0, 1, false));
228 map.push_back(InjectionArc(0, 2, true));
229
230 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
231 ASSERT_EQ(3u, tracer.actions().size());
232 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
233 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
234 EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
235 }
236
TEST(FileDescriptorShuffleTest,FanoutAndClose3)237 TEST(FileDescriptorShuffleTest, FanoutAndClose3) {
238 InjectiveMultimap map;
239 InjectionTracer tracer;
240 map.push_back(InjectionArc(0, 1, true));
241 map.push_back(InjectionArc(0, 2, true));
242
243 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
244 ASSERT_EQ(3u, tracer.actions().size());
245 EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1));
246 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
247 EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
248 }
249
TEST(FileDescriptorShuffleTest,DuplicateClash)250 TEST(FileDescriptorShuffleTest, DuplicateClash) {
251 InjectiveMultimap map;
252 InjectionTracer tracer;
253 map.push_back(InjectionArc(0, 1, false));
254 // Duplicating 1 puts the fd in the spot it's supposed to go already.
255 map.push_back(InjectionArc(1, kDuplicateBase, false));
256
257 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
258 ASSERT_EQ(2u, tracer.actions().size());
259 // This duplication "accidentally" fulfills the second mapping.
260 EXPECT_TRUE(tracer.actions()[0] ==
261 Action(Action::DUPLICATE, kDuplicateBase, 1));
262 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1));
263 // There should be no extra MOVE or CLOSE as the mapping is done and there are
264 // no superfluous fds left around.
265 }
266
TEST(FileDescriptorShuffleTest,DuplicateClashBad)267 TEST(FileDescriptorShuffleTest, DuplicateClashBad) {
268 InjectiveMultimap map;
269 InjectionTracer tracer;
270 map.push_back(InjectionArc(0, 2, false));
271 map.push_back(InjectionArc(1, kDuplicateBase, false));
272 map.push_back(InjectionArc(2, 3, false));
273
274 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
275 ASSERT_EQ(6u, tracer.actions().size());
276 // Clashes with the second mapping.
277 EXPECT_TRUE(tracer.actions()[0] ==
278 Action(Action::DUPLICATE, kDuplicateBase, 2));
279 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
280 EXPECT_TRUE(tracer.actions()[2] ==
281 Action(Action::DUPLICATE, kDuplicateBase + 1, kDuplicateBase));
282 EXPECT_TRUE(tracer.actions()[3] == Action(Action::MOVE, 1, kDuplicateBase));
283 EXPECT_TRUE(tracer.actions()[4] ==
284 Action(Action::MOVE, kDuplicateBase + 1, 3));
285 EXPECT_TRUE(tracer.actions()[5] == Action(Action::CLOSE, kDuplicateBase + 1));
286 }
287
TEST(FileDescriptorShuffleTest,DuplicateClashBadWithClose)288 TEST(FileDescriptorShuffleTest, DuplicateClashBadWithClose) {
289 InjectiveMultimap map;
290 InjectionTracer tracer;
291 map.push_back(InjectionArc(0, 2, true));
292 map.push_back(InjectionArc(1, kDuplicateBase, true));
293 map.push_back(InjectionArc(2, 3, true));
294
295 EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer));
296 ASSERT_EQ(8u, tracer.actions().size());
297 // Clashes with the second mapping.
298 EXPECT_TRUE(tracer.actions()[0] ==
299 Action(Action::DUPLICATE, kDuplicateBase, 2));
300 EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2));
301 EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0));
302 EXPECT_TRUE(tracer.actions()[3] ==
303 Action(Action::DUPLICATE, kDuplicateBase + 1, kDuplicateBase));
304 EXPECT_TRUE(tracer.actions()[4] == Action(Action::MOVE, 1, kDuplicateBase));
305 EXPECT_TRUE(tracer.actions()[5] == Action(Action::CLOSE, 1));
306 EXPECT_TRUE(tracer.actions()[6] ==
307 Action(Action::MOVE, kDuplicateBase + 1, 3));
308 EXPECT_TRUE(tracer.actions()[7] == Action(Action::CLOSE, kDuplicateBase + 1));
309 }
310
311 class FailingDelegate : public InjectionDelegate {
312 public:
Duplicate(int * result,int fd)313 bool Duplicate(int* result, int fd) override { return false; }
314
Move(int src,int dest)315 bool Move(int src, int dest) override { return false; }
316
Close(int fd)317 void Close(int fd) override {}
318 };
319
TEST(FileDescriptorShuffleTest,EmptyWithFailure)320 TEST(FileDescriptorShuffleTest, EmptyWithFailure) {
321 InjectiveMultimap map;
322 FailingDelegate failing;
323
324 EXPECT_TRUE(PerformInjectiveMultimap(map, &failing));
325 }
326
TEST(FileDescriptorShuffleTest,NoopWithFailure)327 TEST(FileDescriptorShuffleTest, NoopWithFailure) {
328 InjectiveMultimap map;
329 FailingDelegate failing;
330 map.push_back(InjectionArc(0, 0, false));
331
332 EXPECT_TRUE(PerformInjectiveMultimap(map, &failing));
333 }
334
TEST(FileDescriptorShuffleTest,Simple1WithFailure)335 TEST(FileDescriptorShuffleTest, Simple1WithFailure) {
336 InjectiveMultimap map;
337 FailingDelegate failing;
338 map.push_back(InjectionArc(0, 1, false));
339
340 EXPECT_FALSE(PerformInjectiveMultimap(map, &failing));
341 }
342
343 } // namespace base
344