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