• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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