| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
| 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 // http://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 "util/test/multiprocess.h" | |
| 16 | |
| 17 #include <stdlib.h> | |
| 18 #include <sys/signal.h> | |
| 19 #include <unistd.h> | |
| 20 | |
| 21 #include "base/basictypes.h" | |
| 22 #include "gtest/gtest.h" | |
| 23 #include "util/file/file_io.h" | |
| 24 #include "util/test/gtest_death_check.h" | |
| 25 | |
| 26 namespace crashpad { | |
| 27 namespace test { | |
| 28 namespace { | |
| 29 | |
| 30 class TestMultiprocess final : public Multiprocess { | |
| 31 public: | |
| 32 TestMultiprocess() : Multiprocess() {} | |
| 33 | |
| 34 ~TestMultiprocess() {} | |
| 35 | |
| 36 private: | |
| 37 // Multiprocess: | |
| 38 | |
| 39 void MultiprocessParent() override { | |
| 40 FileHandle read_handle = ReadPipeHandle(); | |
| 41 char c; | |
| 42 CheckedReadFile(read_handle, &c, 1); | |
| 43 EXPECT_EQ('M', c); | |
| 44 | |
| 45 pid_t pid; | |
| 46 CheckedReadFile(read_handle, &pid, sizeof(pid)); | |
| 47 EXPECT_EQ(pid, ChildPID()); | |
| 48 | |
| 49 c = 'm'; | |
| 50 CheckedWriteFile(WritePipeHandle(), &c, 1); | |
| 51 | |
| 52 // The child will close its end of the pipe and exit. Make sure that the | |
| 53 // parent sees EOF. | |
| 54 CheckedReadFileAtEOF(read_handle); | |
| 55 } | |
| 56 | |
| 57 void MultiprocessChild() override { | |
| 58 FileHandle write_handle = WritePipeHandle(); | |
| 59 | |
| 60 char c = 'M'; | |
| 61 CheckedWriteFile(write_handle, &c, 1); | |
| 62 | |
| 63 pid_t pid = getpid(); | |
| 64 CheckedWriteFile(write_handle, &pid, sizeof(pid)); | |
| 65 | |
| 66 CheckedReadFile(ReadPipeHandle(), &c, 1); | |
| 67 EXPECT_EQ('m', c); | |
| 68 } | |
| 69 | |
| 70 DISALLOW_COPY_AND_ASSIGN(TestMultiprocess); | |
| 71 }; | |
| 72 | |
| 73 TEST(Multiprocess, Multiprocess) { | |
| 74 TestMultiprocess multiprocess; | |
| 75 multiprocess.Run(); | |
| 76 } | |
| 77 | |
| 78 class TestMultiprocessUnclean final : public Multiprocess { | |
| 79 public: | |
| 80 enum TerminationType { | |
| 81 kExitSuccess = 0, | |
| 82 kExitFailure, | |
| 83 kExit2, | |
| 84 kAbort, | |
| 85 }; | |
| 86 | |
| 87 explicit TestMultiprocessUnclean(TerminationType type) | |
| 88 : Multiprocess(), | |
| 89 type_(type) { | |
| 90 if (type_ == kAbort) { | |
| 91 SetExpectedChildTermination(kTerminationSignal, SIGABRT); | |
| 92 } else { | |
| 93 SetExpectedChildTermination(kTerminationNormal, ExitCode()); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 ~TestMultiprocessUnclean() {} | |
| 98 | |
| 99 private: | |
| 100 int ExitCode() const { | |
| 101 return type_; | |
| 102 } | |
| 103 | |
| 104 // Multiprocess: | |
| 105 | |
| 106 void MultiprocessParent() override { | |
| 107 } | |
| 108 | |
| 109 void MultiprocessChild() override { | |
| 110 if (type_ == kAbort) { | |
| 111 abort(); | |
| 112 } else { | |
| 113 _exit(ExitCode()); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 TerminationType type_; | |
| 118 | |
| 119 DISALLOW_COPY_AND_ASSIGN(TestMultiprocessUnclean); | |
| 120 }; | |
| 121 | |
| 122 TEST(Multiprocess, SuccessfulExit) { | |
| 123 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitSuccess); | |
| 124 multiprocess.Run(); | |
| 125 } | |
| 126 | |
| 127 TEST(Multiprocess, UnsuccessfulExit) { | |
| 128 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitFailure); | |
| 129 multiprocess.Run(); | |
| 130 } | |
| 131 | |
| 132 TEST(Multiprocess, Exit2) { | |
| 133 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExit2); | |
| 134 multiprocess.Run(); | |
| 135 } | |
| 136 | |
| 137 TEST(Multiprocess, AbortSignal) { | |
| 138 TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kAbort); | |
| 139 multiprocess.Run(); | |
| 140 } | |
| 141 | |
| 142 class TestMultiprocessClosePipe final : public Multiprocess { | |
| 143 public: | |
| 144 enum WhoCloses { | |
| 145 kParentCloses = 0, | |
| 146 kChildCloses, | |
| 147 }; | |
| 148 enum WhatCloses { | |
| 149 kReadCloses = 0, | |
| 150 kWriteCloses, | |
| 151 kReadAndWriteClose, | |
| 152 }; | |
| 153 | |
| 154 TestMultiprocessClosePipe(WhoCloses who_closes, WhatCloses what_closes) | |
| 155 : Multiprocess(), | |
| 156 who_closes_(who_closes), | |
| 157 what_closes_(what_closes) { | |
| 158 } | |
| 159 | |
| 160 ~TestMultiprocessClosePipe() {} | |
| 161 | |
| 162 private: | |
| 163 void VerifyInitial() { | |
| 164 ASSERT_NE(-1, ReadPipeHandle()); | |
| 165 ASSERT_NE(-1, WritePipeHandle()); | |
| 166 } | |
| 167 | |
| 168 // Verifies that the partner process did what it was supposed to do. This must | |
| 169 // only be called when who_closes_ names the partner process, not this | |
| 170 // process. | |
| 171 // | |
| 172 // If the partner was supposed to close its write pipe, the read pipe will be | |
| 173 // checked to ensure that it shows end-of-file. | |
| 174 // | |
| 175 // If the partner was supposed to close its read pipe, the write pipe will be | |
| 176 // checked to ensure that a checked write causes death. This can only be done | |
| 177 // if the partner also provides some type of signal when it has closed its | |
| 178 // read pipe, which is done in the form of it closing its write pipe, causing | |
| 179 // the read pipe in this process to show end-of-file. | |
| 180 void VerifyPartner() { | |
| 181 if (what_closes_ == kWriteCloses) { | |
| 182 CheckedReadFileAtEOF(ReadPipeHandle()); | |
| 183 } else if (what_closes_ == kReadAndWriteClose) { | |
| 184 CheckedReadFileAtEOF(ReadPipeHandle()); | |
| 185 char c = '\0'; | |
| 186 | |
| 187 // This will raise SIGPIPE. If fatal (the normal case), that will cause | |
| 188 // process termination. If SIGPIPE is being handled somewhere, the write | |
| 189 // will still fail and set errno to EPIPE, and CheckedWriteFile() will | |
| 190 // abort execution. Regardless of how SIGPIPE is handled, the process will | |
| 191 // be terminated. Because the actual termination mechanism is not known, | |
| 192 // no regex can be specified. | |
| 193 EXPECT_DEATH_CHECK(CheckedWriteFile(WritePipeHandle(), &c, 1), ""); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 void Close() { | |
| 198 switch (what_closes_) { | |
| 199 case kReadCloses: | |
| 200 CloseReadPipe(); | |
| 201 EXPECT_NE(-1, WritePipeHandle()); | |
| 202 EXPECT_DEATH_CHECK(ReadPipeHandle(), "fd"); | |
| 203 break; | |
| 204 case kWriteCloses: | |
| 205 CloseWritePipe(); | |
| 206 EXPECT_NE(-1, ReadPipeHandle()); | |
| 207 EXPECT_DEATH_CHECK(WritePipeHandle(), "fd"); | |
| 208 break; | |
| 209 case kReadAndWriteClose: | |
| 210 CloseReadPipe(); | |
| 211 CloseWritePipe(); | |
| 212 EXPECT_DEATH_CHECK(ReadPipeHandle(), "fd"); | |
| 213 EXPECT_DEATH_CHECK(WritePipeHandle(), "fd"); | |
| 214 break; | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 // Multiprocess: | |
| 219 | |
| 220 void MultiprocessParent() override { | |
| 221 ASSERT_NO_FATAL_FAILURE(VerifyInitial()); | |
| 222 | |
| 223 if (who_closes_ == kParentCloses) { | |
| 224 Close(); | |
| 225 } else { | |
| 226 VerifyPartner(); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void MultiprocessChild() override { | |
| 231 ASSERT_NO_FATAL_FAILURE(VerifyInitial()); | |
| 232 | |
| 233 if (who_closes_ == kChildCloses) { | |
| 234 Close(); | |
| 235 } else { | |
| 236 VerifyPartner(); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 WhoCloses who_closes_; | |
| 241 WhatCloses what_closes_; | |
| 242 | |
| 243 DISALLOW_COPY_AND_ASSIGN(TestMultiprocessClosePipe); | |
| 244 }; | |
| 245 | |
| 246 TEST(MultiprocessDeathTest, ParentClosesReadPipe) { | |
| 247 TestMultiprocessClosePipe multiprocess( | |
| 248 TestMultiprocessClosePipe::kParentCloses, | |
| 249 TestMultiprocessClosePipe::kReadCloses); | |
| 250 multiprocess.Run(); | |
| 251 } | |
| 252 | |
| 253 TEST(MultiprocessDeathTest, ParentClosesWritePipe) { | |
| 254 TestMultiprocessClosePipe multiprocess( | |
| 255 TestMultiprocessClosePipe::kParentCloses, | |
| 256 TestMultiprocessClosePipe::kWriteCloses); | |
| 257 multiprocess.Run(); | |
| 258 } | |
| 259 | |
| 260 TEST(MultiprocessDeathTest, ParentClosesReadAndWritePipe) { | |
| 261 TestMultiprocessClosePipe multiprocess( | |
| 262 TestMultiprocessClosePipe::kParentCloses, | |
| 263 TestMultiprocessClosePipe::kReadAndWriteClose); | |
| 264 multiprocess.Run(); | |
| 265 } | |
| 266 | |
| 267 TEST(MultiprocessDeathTest, ChildClosesReadPipe) { | |
| 268 TestMultiprocessClosePipe multiprocess( | |
| 269 TestMultiprocessClosePipe::kChildCloses, | |
| 270 TestMultiprocessClosePipe::kReadCloses); | |
| 271 multiprocess.Run(); | |
| 272 } | |
| 273 | |
| 274 TEST(MultiprocessDeathTest, ChildClosesWritePipe) { | |
| 275 TestMultiprocessClosePipe multiprocess( | |
| 276 TestMultiprocessClosePipe::kChildCloses, | |
| 277 TestMultiprocessClosePipe::kWriteCloses); | |
| 278 multiprocess.Run(); | |
| 279 } | |
| 280 | |
| 281 TEST(MultiprocessDeathTest, ChildClosesReadAndWritePipe) { | |
| 282 TestMultiprocessClosePipe multiprocess( | |
| 283 TestMultiprocessClosePipe::kChildCloses, | |
| 284 TestMultiprocessClosePipe::kReadAndWriteClose); | |
| 285 multiprocess.Run(); | |
| 286 } | |
| 287 | |
| 288 } // namespace | |
| 289 } // namespace test | |
| 290 } // namespace crashpad | |
| OLD | NEW |