1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/sandbox2/util/pid_waiter.h"
16
17 #include <cerrno>
18 #include <ctime>
19 #include <memory>
20 #include <utility>
21
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "absl/time/clock.h"
25 #include "absl/time/time.h"
26 #include "sandboxed_api/util/thread.h"
27
28 namespace sandbox2 {
29 namespace {
30
31 using ::testing::_;
32 using ::testing::DoAll;
33 using ::testing::Return;
34 using ::testing::SetArgPointee;
35 using ::testing::SetErrnoAndReturn;
36
37 constexpr int kPrioStatus = 7 << 8;
38 constexpr int kFirstStatus = 5 << 8;
39 constexpr int kSecondStatus = 8 << 8;
40 constexpr pid_t kPrioPid = 1;
41 constexpr pid_t kFirstPid = 2;
42 constexpr pid_t kSecondPid = 3;
43
44 class MockWaitPid : public PidWaiter::WaitPidInterface {
45 public:
46 MOCK_METHOD(int, WaitPid, (pid_t, int*, int), (override));
47 };
48
TEST(PidWaiterTest,NoEvents)49 TEST(PidWaiterTest, NoEvents) {
50 auto mock_wait_pid = std::make_unique<MockWaitPid>();
51 EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, _)).WillRepeatedly(Return(0));
52 PidWaiter waiter(1, std::move(mock_wait_pid));
53 int status;
54 EXPECT_EQ(waiter.Wait(&status), 0);
55 }
56
TEST(PidWaiterTest,NoProcess)57 TEST(PidWaiterTest, NoProcess) {
58 auto mock_wait_pid = std::make_unique<MockWaitPid>();
59 EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, _))
60 .WillRepeatedly(SetErrnoAndReturn(ECHILD, -1));
61 PidWaiter waiter(1, std::move(mock_wait_pid));
62 int status;
63 EXPECT_EQ(waiter.Wait(&status), -1);
64 EXPECT_EQ(errno, ECHILD);
65 }
66
TEST(PidWaiterTest,PriorityRespected)67 TEST(PidWaiterTest, PriorityRespected) {
68 auto mock_wait_pid = std::make_unique<MockWaitPid>();
69 EXPECT_CALL(*mock_wait_pid, WaitPid(-1, _, _))
70 .WillOnce(DoAll(SetArgPointee<1>(kFirstStatus), Return(kFirstPid)))
71 .WillRepeatedly(Return(0));
72 EXPECT_CALL(*mock_wait_pid, WaitPid(kPrioPid, _, _))
73 .WillOnce(DoAll(SetArgPointee<1>(kPrioStatus), Return(kPrioPid)))
74 .WillOnce(Return(0))
75 .WillRepeatedly(DoAll(SetArgPointee<1>(kPrioStatus), Return(kPrioPid)));
76 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
77 int status;
78 EXPECT_EQ(waiter.Wait(&status), kPrioPid);
79 EXPECT_EQ(status, kPrioStatus);
80 EXPECT_EQ(waiter.Wait(&status), kFirstPid);
81 EXPECT_EQ(status, kFirstStatus);
82 EXPECT_EQ(waiter.Wait(&status), kPrioPid);
83 EXPECT_EQ(status, kPrioStatus);
84 }
85
TEST(PidWaiterTest,BatchesWaits)86 TEST(PidWaiterTest, BatchesWaits) {
87 auto mock_wait_pid = std::make_unique<MockWaitPid>();
88 EXPECT_CALL(*mock_wait_pid, WaitPid(kPrioPid, _, _))
89 .WillRepeatedly(Return(0));
90 EXPECT_CALL(*mock_wait_pid, WaitPid(-1, _, _))
91 .Times(3)
92 .WillOnce(DoAll(SetArgPointee<1>(kFirstStatus), Return(kFirstPid)))
93 .WillOnce(DoAll(SetArgPointee<1>(kSecondStatus), Return(kSecondPid)))
94 .WillRepeatedly(Return(0));
95 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
96 int status;
97 EXPECT_EQ(waiter.Wait(&status), kFirstPid);
98 EXPECT_EQ(status, kFirstStatus);
99 }
100
TEST(PidWaiterTest,ReturnsFromBatch)101 TEST(PidWaiterTest, ReturnsFromBatch) {
102 auto mock_wait_pid = std::make_unique<MockWaitPid>();
103 EXPECT_CALL(*mock_wait_pid, WaitPid(kPrioPid, _, _))
104 .WillRepeatedly(Return(0));
105 EXPECT_CALL(*mock_wait_pid, WaitPid(-1, _, _))
106 .Times(3)
107 .WillOnce(DoAll(SetArgPointee<1>(kFirstStatus), Return(kFirstPid)))
108 .WillOnce(DoAll(SetArgPointee<1>(kSecondStatus), Return(kSecondPid)))
109 .WillRepeatedly(Return(0));
110 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
111 int status;
112 EXPECT_EQ(waiter.Wait(&status), kFirstPid);
113 EXPECT_EQ(status, kFirstStatus);
114 EXPECT_EQ(waiter.Wait(&status), kSecondPid);
115 EXPECT_EQ(status, kSecondStatus);
116 }
117
TEST(PidWaiterTest,ChangePriority)118 TEST(PidWaiterTest, ChangePriority) {
119 auto mock_wait_pid = std::make_unique<MockWaitPid>();
120 EXPECT_CALL(*mock_wait_pid, WaitPid(kFirstPid, _, _))
121 .WillRepeatedly(DoAll(SetArgPointee<1>(kFirstStatus), Return(kFirstPid)));
122 EXPECT_CALL(*mock_wait_pid, WaitPid(kSecondPid, _, _))
123 .WillRepeatedly(
124 DoAll(SetArgPointee<1>(kSecondStatus), Return(kSecondPid)));
125 PidWaiter waiter(kFirstPid, std::move(mock_wait_pid));
126 int status;
127 EXPECT_EQ(waiter.Wait(&status), kFirstPid);
128 EXPECT_EQ(status, kFirstStatus);
129 EXPECT_EQ(waiter.Wait(&status), kFirstPid);
130 EXPECT_EQ(status, kFirstStatus);
131 waiter.SetPriorityPid(kSecondPid);
132 EXPECT_EQ(waiter.Wait(&status), kSecondPid);
133 EXPECT_EQ(status, kSecondStatus);
134 }
135
TEST(PidWaiterTest,DeadlineRespected)136 TEST(PidWaiterTest, DeadlineRespected) {
137 auto mock_wait_pid = std::make_unique<MockWaitPid>();
138 EXPECT_CALL(*mock_wait_pid,
139 WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
140 .WillRepeatedly(Return(0));
141 EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
142 .WillRepeatedly([](int pid, int* status, int flags) {
143 struct timespec ts = absl::ToTimespec(absl::Seconds(1));
144 if (nanosleep(&ts, nullptr) == -1) {
145 return -1;
146 }
147 *status = kFirstPid;
148 return kFirstPid;
149 });
150 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
151 waiter.SetDeadline(absl::Now() + absl::Milliseconds(100));
152 int status;
153 EXPECT_EQ(waiter.Wait(&status), -1);
154 EXPECT_EQ(errno, EINTR);
155 }
156
TEST(PidWaiterTest,NotifyConcurrent)157 TEST(PidWaiterTest, NotifyConcurrent) {
158 auto mock_wait_pid = std::make_unique<MockWaitPid>();
159 EXPECT_CALL(*mock_wait_pid,
160 WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
161 .WillRepeatedly(Return(0));
162 EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
163 .WillRepeatedly([](int pid, int* status, int flags) {
164 struct timespec ts = absl::ToTimespec(absl::Seconds(2));
165 if (nanosleep(&ts, nullptr) == -1) {
166 return -1;
167 }
168 *status = kFirstPid;
169 return kFirstPid;
170 });
171 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
172 waiter.SetDeadline(absl::Now() + absl::Seconds(1));
173 sapi::Thread notify_thread([&] {
174 absl::SleepFor(absl::Milliseconds(100));
175 waiter.Notify();
176 });
177 int status;
178 absl::Time start = absl::Now();
179 EXPECT_EQ(waiter.Wait(&status), -1);
180 EXPECT_LT(absl::Now() - start, absl::Milliseconds(500));
181 EXPECT_EQ(errno, EINTR);
182 }
183
TEST(PidWaiterTest,NotifyNext)184 TEST(PidWaiterTest, NotifyNext) {
185 auto mock_wait_pid = std::make_unique<MockWaitPid>();
186 EXPECT_CALL(*mock_wait_pid,
187 WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
188 .WillRepeatedly(Return(0));
189 EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
190 .Times(0);
191 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
192 waiter.SetDeadline(absl::Now() + absl::Seconds(1));
193 waiter.Notify();
194 int status;
195 absl::Time start = absl::Now();
196 EXPECT_EQ(waiter.Wait(&status), 0);
197 EXPECT_LT(absl::Now() - start, absl::Milliseconds(500));
198 }
199
TEST(PidWaiterTest,DeadlineUnchangedAfterNotify)200 TEST(PidWaiterTest, DeadlineUnchangedAfterNotify) {
201 auto mock_wait_pid = std::make_unique<MockWaitPid>();
202 EXPECT_CALL(*mock_wait_pid,
203 WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED | WNOHANG))
204 .WillRepeatedly(Return(0));
205 EXPECT_CALL(*mock_wait_pid, WaitPid(_, _, __WNOTHREAD | __WALL | WUNTRACED))
206 .WillRepeatedly([](int pid, int* status, int flags) {
207 struct timespec ts = absl::ToTimespec(absl::Milliseconds(500));
208 if (nanosleep(&ts, nullptr) == -1) {
209 return -1;
210 }
211 *status = kFirstPid;
212 return kFirstPid;
213 });
214 PidWaiter waiter(kPrioPid, std::move(mock_wait_pid));
215 waiter.SetDeadline(absl::Now() + absl::Milliseconds(900));
216 waiter.Notify();
217 int status;
218 EXPECT_EQ(waiter.Wait(&status), 0);
219 absl::SleepFor(absl::Milliseconds(500));
220 EXPECT_EQ(waiter.Wait(&status), -1);
221 EXPECT_EQ(errno, EINTR);
222 }
223
224 } // namespace
225 } // namespace sandbox2
226