SM: Change process label generation
[platform/core/test/security-tests.git] / src / security-manager-tests / common / sm_commons.cpp
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 #include <algorithm>
18 #include <cstring>
19 #include <ftw.h>
20 #include <grp.h>
21 #include <string>
22 #include <sys/capability.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <cstdlib>
27
28 #include <unordered_set>
29 #include <vector>
30
31 #include <security-manager-types.h>
32 #include <sys/smack.h>
33
34 #include <cynara_test_client.h>
35 #include <dpl/test/test_runner.h>
36 #include <memory.h>
37 #include <sm_api.h>
38 #include <sm_commons.h>
39 #include <sm_db.h>
40 #include <synchronization_pipe.h>
41 #include <sm_request.h>
42 #include <tests_common.h>
43 #include "tzplatform.h"
44
45 using namespace SecurityManagerTest;
46
47 // Common const values
48
49 const privileges_t SM_ALLOWED_PRIVILEGES = {
50     "http://tizen.org/privilege/display",
51     "http://tizen.org/privilege/nfc"
52 };
53
54 const privileges_t SM_DENIED_PRIVILEGES  = {
55     "http://tizen.org/privilege/bluetooth",
56     "http://tizen.org/privilege/power"
57 };
58
59 const privileges_t SM_NO_PRIVILEGES  = {
60 };
61
62 const std::vector<std::string> SM_ALLOWED_GROUPS = {"db_browser", "db_alarm"};
63
64 const std::string uidToStr(const uid_t uid)
65 {
66     return std::to_string(static_cast<unsigned int>(uid));
67 }
68
69 // Common implementation details
70
71 std::string generateProcessLabel(const std::string &appId, const std::string &pkgId, bool isHybrid)
72 {
73     std::string label = "User::Pkg::" + pkgId;
74     if (isHybrid) {
75         label += "::App::" + appId;
76     }
77     return label;
78 }
79
80 std::string generatePathRWLabel(const std::string &pkgId)
81 {
82     return "User::Pkg::" + pkgId;
83 }
84
85 static std::string genPath(int app_num, const char *postfix) {
86     char buf[16];
87     sprintf(buf, "%02d", app_num);
88     return TzPlatformConfig::globalAppDir() + "/sm_test_" + std::string(buf) + "_pkg_id_full/" + std::string(postfix);
89 }
90
91 std::string genRWPath(int app_num) {
92     return genPath(app_num, "app_dir");
93 }
94 std::string genROPath(int app_num) {
95     return genPath(app_num, "app_dir_ro");
96 }
97 std::string genPublicROPath(int app_num) {
98     return genPath(app_num, "app_dir_public_ro");
99 }
100
101 std::string genOwnerRWOthersROPath(int app_num) {
102     return genPath(app_num, "app_dir_rw_others_ro");
103 }
104
105 static std::string generatePkgLabelOwnerRWothersRO(const std::string &pkgId)
106 {
107     return "User::Pkg::" + pkgId + "::SharedRO";
108 }
109
110 // Common DB/nftw checks
111
112 // nftw doesn't allow passing user data to functions. Work around by using global variable
113 static std::string nftw_expected_label;
114 bool nftw_expected_transmute;
115 bool nftw_expected_exec;
116
117 static int nftw_check_sm_labels_app_dir(const char *fpath, const struct stat *sb,
118                               const char* correctLabel, bool transmute_test, bool exec_test)
119 {
120     int result;
121     CStringPtr labelPtr;
122     char* label = nullptr;
123
124     /* ACCESS */
125     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
126     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
127     labelPtr.reset(label);
128     RUNNER_ASSERT_MSG(label != nullptr, "ACCESS label on " << fpath << " is not set");
129     result = strcmp(correctLabel, label);
130     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is incorrect"
131             " (should be '" << correctLabel << "' and is '" << label << "')");
132
133
134     /* EXEC */
135     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
136     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
137     labelPtr.reset(label);
138
139     if (S_ISREG(sb->st_mode) && (sb->st_mode & S_IXUSR) && exec_test) {
140         RUNNER_ASSERT_MSG(label != nullptr, "EXEC label on " << fpath << " is not set");
141         result = strcmp(correctLabel, label);
142         RUNNER_ASSERT_MSG(result == 0, "Incorrect EXEC label on executable file " << fpath);
143     } else
144         RUNNER_ASSERT_MSG(label == nullptr, "EXEC label on " << fpath << " is set");
145
146
147     /* TRANSMUTE */
148     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
149     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
150     labelPtr.reset(label);
151
152     if (S_ISDIR(sb->st_mode) && transmute_test == true) {
153         RUNNER_ASSERT_MSG(label != nullptr, "TRANSMUTE label on " << fpath << " is not set at all");
154         RUNNER_ASSERT_MSG(strcmp(label,"TRUE") == 0,
155                 "TRANSMUTE label on " << fpath << " is not set properly: '"<<label<<"'");
156     } else {
157         RUNNER_ASSERT_MSG(label == nullptr, "TRANSMUTE label on " << fpath << " is set");
158     }
159
160     return 0;
161 }
162
163 static int nftw_check_sm_labels(const char *fpath, const struct stat *sb,
164                                int /*typeflag*/, struct FTW* /*ftwbuf*/)
165 {
166     return nftw_check_sm_labels_app_dir(fpath, sb,
167         nftw_expected_label.c_str(), nftw_expected_transmute, nftw_expected_exec);
168 }
169
170 int nftw_check_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
171                                   int /*typeflag*/, struct FTW* /*ftwbuf*/)
172 {
173     int result;
174     CStringPtr labelPtr;
175     char* label = nullptr;
176
177     /* ACCESS */
178     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_ACCESS);
179     labelPtr.reset(label);
180     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
181     result = strcmp("canary_label", labelPtr.get());
182     RUNNER_ASSERT_MSG(result == 0, "ACCESS label on " << fpath << " is overwritten");
183
184     /* EXEC */
185     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_EXEC);
186     labelPtr.reset(label);
187     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
188     result = strcmp("canary_label", labelPtr.get());
189     RUNNER_ASSERT_MSG(result == 0, "EXEC label on " << fpath << " is overwritten");
190
191     /* TRANSMUTE */
192     result = smack_lgetlabel(fpath, &label, SMACK_LABEL_TRANSMUTE);
193     labelPtr.reset(label);
194     RUNNER_ASSERT_MSG(result == 0, "Could not get label for the path");
195     RUNNER_ASSERT_MSG(labelPtr.get() == nullptr, "TRANSMUTE label on " << fpath << " is set");
196
197     return 0;
198 }
199
200 static int nftw_set_labels_non_app_dir(const char *fpath, const struct stat* /*sb*/,
201                                 int /*typeflag*/, struct FTW* /*ftwbuf*/)
202 {
203     smack_lsetlabel(fpath, "canary_label", SMACK_LABEL_ACCESS);
204     smack_lsetlabel(fpath, "canary_label", SMACK_LABEL_EXEC);
205     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
206
207     return 0;
208 }
209
210 int nftw_remove_labels(const char *fpath, const struct stat* /*sb*/,
211                        int /*typeflag*/, struct FTW* /*ftwbuf*/)
212 {
213     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_ACCESS);
214     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_EXEC);
215     smack_lsetlabel(fpath, nullptr, SMACK_LABEL_TRANSMUTE);
216
217     return 0;
218 }
219
220 static const std::string SM_DENIED_PATH = TzPlatformConfig::globalAppDir() + "/non_app_dir";
221
222 void check_app_permissions(const char *const app_id, const char *const pkg_id,
223                            const char *const user, const privileges_t &allowed_privs,
224                            const privileges_t &denied_privs)
225 {
226     (void) pkg_id;
227     std::string smackLabel = generateProcessLabel(app_id, pkg_id);
228
229     CynaraTestClient::Client ctc;
230
231     for (auto &priv : allowed_privs) {
232         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_ALLOWED);
233     }
234
235     for (auto &priv : denied_privs) {
236         ctc.check(smackLabel.c_str(), "", user, priv.c_str(), CYNARA_API_ACCESS_DENIED);
237     }
238 }
239
240
241 void check_app_after_install(const char *const app_id, const char *const pkg_id)
242 {
243     TestSecurityManagerDatabase dbtest;
244     dbtest.test_db_after__app_install(app_id, pkg_id);
245 }
246
247 static void check_app_gids(const char *const app_id, const std::vector<gid_t> &allowed_gids)
248 {
249     int ret;
250     gid_t main_gid = getgid();
251     std::unordered_set<gid_t> reference_gids(allowed_gids.begin(), allowed_gids.end());
252
253     // Reset supplementary groups
254     ret = setgroups(0, NULL);
255     RUNNER_ASSERT_MSG(ret != -1, "Unable to set supplementary groups");
256
257     Api::setProcessGroups(app_id);
258
259     ret = getgroups(0, nullptr);
260     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
261
262     std::vector<gid_t> actual_gids(ret);
263     ret = getgroups(ret, actual_gids.data());
264     RUNNER_ASSERT_MSG(ret != -1, "Unable to get supplementary groups");
265
266     for (const auto &gid : actual_gids) {
267         RUNNER_ASSERT_MSG(gid == main_gid || reference_gids.count(gid) > 0,
268             "Application shouldn't get access to group " << gid);
269         reference_gids.erase(gid);
270     }
271
272     RUNNER_ASSERT_MSG(reference_gids.empty(), "Application didn't get access to some groups");
273 }
274
275 static const char *const ANY_USER_REPRESENTATION = "anyuser";/*this may be actually any string*/
276
277 void check_app_after_install(const char *const app_id, const char *const pkg_id,
278                              const privileges_t &allowed_privs,
279                              const privileges_t &denied_privs,
280                              const std::vector<std::string> &allowed_groups)
281 {
282     TestSecurityManagerDatabase dbtest;
283     dbtest.test_db_after__app_install(app_id, pkg_id);
284
285     /*Privileges should be granted to all users if root installs app*/
286     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, allowed_privs, denied_privs);
287
288     /* Setup mapping of gids to privileges */
289     /* Do this for each privilege for extra check */
290     for (const auto &privilege : allowed_privs) {
291         dbtest.setup_privilege_groups(privilege, allowed_groups);
292     }
293
294     std::vector<gid_t> allowed_gids;
295
296     for (const auto &groupName : allowed_groups) {
297         errno = 0;
298         struct group* grp = getgrnam(groupName.c_str());
299         RUNNER_ASSERT_ERRNO_MSG(grp, "Group: " << groupName << " not found");
300         allowed_gids.push_back(grp->gr_gid);
301     }
302
303     check_app_gids(app_id, allowed_gids);
304 }
305
306 void check_path(const std::string &path, const std::string &label) {
307     nftw_expected_label = label;
308     nftw_expected_transmute = true;
309     nftw_expected_exec = false;
310
311     // check labels
312     int result = nftw(path.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
313     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << path);
314 }
315
316 void check_app_path_after_install(int app_num, const char *pkgId, bool others_enabled)
317 {
318     std::string SM_RW_PATH = genRWPath(app_num);
319     std::string SM_RO_PATH = genROPath(app_num);
320     std::string SM_PUBLIC_RO_PATH = genPublicROPath(app_num);
321     int result;
322
323     nftw_expected_label = generatePathRWLabel(pkgId);
324     nftw_expected_transmute = true;
325     nftw_expected_exec = false;
326
327     result = nftw(SM_RW_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
328     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_RW_PATH);
329
330     nftw_expected_label = generatePathRWLabel(pkgId) + "::RO";
331     nftw_expected_transmute = false;
332     nftw_expected_exec = false;
333
334     result = nftw(SM_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
335     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_RO_PATH);
336
337     nftw_expected_label = "User::Home";
338     nftw_expected_transmute = true;
339     nftw_expected_exec = false;
340
341     result = nftw(SM_PUBLIC_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
342     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_PUBLIC_RO_PATH);
343
344     result = nftw(SM_DENIED_PATH.c_str(), &nftw_check_labels_non_app_dir, FTW_MAX_FDS, FTW_PHYS);
345     RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_DENIED_PATH);
346
347     // owner RW, others RO
348     if(others_enabled) {
349         std::string SM_OWNER_RW_OTHERS_RO_PATH = genOwnerRWOthersROPath(app_num);
350         nftw_expected_label = generatePkgLabelOwnerRWothersRO(pkgId);
351         nftw_expected_transmute = true;
352         nftw_expected_exec = false;
353
354         result = nftw(SM_OWNER_RW_OTHERS_RO_PATH.c_str(), &nftw_check_sm_labels, FTW_MAX_FDS, FTW_PHYS);
355         RUNNER_ASSERT_MSG(result == 0, "Unable to check Smack labels for " << SM_OWNER_RW_OTHERS_RO_PATH);
356     }
357 }
358
359 void check_app_after_uninstall(const char *const app_id, const char *const pkg_id,
360                                const bool is_pkg_removed)
361 {
362     TestSecurityManagerDatabase dbtest;
363     dbtest.test_db_after__app_uninstall(app_id, pkg_id, is_pkg_removed);
364 }
365
366 void check_app_after_uninstall(const char *const app_id, const char *const pkg_id,
367                                const privileges_t &privileges, const bool is_pkg_removed)
368 {
369     TestSecurityManagerDatabase dbtest;
370     dbtest.test_db_after__app_uninstall(app_id, pkg_id, is_pkg_removed);
371
372
373     /*Privileges should not be granted anymore to any user*/
374     check_app_permissions(app_id, pkg_id, ANY_USER_REPRESENTATION, SM_NO_PRIVILEGES, privileges);
375 }
376
377 std::string access_opposite(std::string &access) {
378     static const std::map<char, int> access_mapping = {{'r', 0}, {'w', 1}, {'x', 2}, {'a', 3},
379                                                        {'t', 4}, {'l', 5}};
380     //May write implies may lock
381     if (access.find('w') != std::string::npos && access.find('l') == std::string::npos) {
382         access.append("l");
383     }
384     std::string access_opposite = "rwxatl";
385     for (char c : access) {
386         access_opposite[access_mapping.at(c)] = '-';
387     }
388     auto it = std::remove_if(access_opposite.begin(), access_opposite.end(), [](char c) {return c == '-';});
389     access_opposite.erase(it, access_opposite.end());
390     return access_opposite;
391 }
392
393 void check_exact_smack_accesses(const std::string &subject, const std::string &object,
394                                 const std::string &access) {
395     std::string access_str(access);
396     auto no_access = access_opposite(access_str);
397     for (char c : access_str) {
398         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
399         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
400                           << ">, <" << c << "> errno=" << strerror(errno));
401         RUNNER_ASSERT_MSG(ret == 1, "Access " << c << " from " << subject << " to "
402                           << object << " not given");
403     }
404
405     for (char c : no_access) {
406         int ret = smack_have_access(subject.c_str(), object.c_str(), std::string(1, c).c_str());
407         RUNNER_ASSERT_MSG(ret >= 0, "smack_have_access failed: <" << subject << ">, <" << object
408                           << ">, <" << c << "> errno=" << strerror(errno));
409         RUNNER_ASSERT_MSG(ret == 0, "Access " << c << " from " << subject << " to "
410                           << object << " unnecessarily given");
411     }
412 }
413
414 CapsSetsUniquePtr setCaps(const char *cap_string)
415 {
416     CapsSetsUniquePtr caps(cap_init());
417     caps.reset(cap_from_text(cap_string));
418     RUNNER_ASSERT_MSG(caps, "can't convert capabilities from text");
419     int result = cap_set_proc(caps.get());
420     RUNNER_ASSERT_MSG(result == 0, "can't set capabilities. Result: " << result);
421     return caps;
422 }
423
424 static void prepare_app_path(int app_num, bool others_enabled = false)
425 {
426     std::string SM_RW_PATH = genRWPath(app_num);
427     std::string SM_RO_PATH = genROPath(app_num);
428     std::string SM_PUBLIC_RO_PATH = genPublicROPath(app_num);
429     int result;
430
431     result = nftw(SM_RW_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
432     RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_RW_PATH);
433
434     result = nftw(SM_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
435     RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_RO_PATH);
436
437     result = nftw(SM_PUBLIC_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
438     RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_PUBLIC_RO_PATH);
439
440     if(others_enabled) {
441         std::string SM_OWNER_RW_OTHERS_RO_PATH = genOwnerRWOthersROPath(app_num);
442         result = nftw(SM_OWNER_RW_OTHERS_RO_PATH.c_str(), &nftw_remove_labels, FTW_MAX_FDS, FTW_PHYS);
443         RUNNER_ASSERT_MSG(result == 0, "Unable to clean Smack labels in " << SM_OWNER_RW_OTHERS_RO_PATH);
444     }
445
446     result = nftw(SM_DENIED_PATH.c_str(), &nftw_set_labels_non_app_dir, FTW_MAX_FDS, FTW_PHYS);
447     RUNNER_ASSERT_MSG(result == 0, "Unable to set Smack labels in " << SM_DENIED_PATH);
448 }
449
450 void prepare_app_env(int app_num, bool others_enabled)
451 {
452     prepare_app_path(app_num, others_enabled);
453 }
454
455 void install_app(const char *app_id, const char *pkg_id, uid_t uid, app_install_type type,
456                  bool check_after)
457 {
458     InstallRequest request;
459     request.setAppId(app_id);
460     request.setPkgId(pkg_id);
461     request.setUid(uid);
462     if (type != SM_APP_INSTALL_NONE)
463         request.setInstallType(type);
464     Api::install(request);
465
466     if (check_after)
467         check_app_after_install(app_id, pkg_id);
468 }
469
470 void uninstall_app(const char *app_id, const char *pkg_id, bool expect_pkg_removed,
471                    app_install_type type, bool check_after)
472 {
473     InstallRequest request;
474     request.setAppId(app_id);
475     if (type != SM_APP_INSTALL_NONE)
476         request.setInstallType(type);
477     Api::uninstall(request);
478
479     if (check_after)
480         check_app_after_uninstall(app_id, pkg_id, expect_pkg_removed);
481 }
482
483 static const std::string EXEC_FILE("exec");
484 static const std::string NORMAL_FILE("normal");
485 static const std::string LINK_PREFIX("link_to_");
486
487 static void createTestDir(const std::string &dir)
488 {
489     mode_t dirMode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
490     mode_t execFileMode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
491     mode_t normalFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
492
493     mktreeSafe(dir, dirMode);
494     creatSafe(dir + "/" + EXEC_FILE, execFileMode);
495     creatSafe(dir + "/" + NORMAL_FILE, normalFileMode);
496     symlinkSafe(dir + "/" + EXEC_FILE, dir + "/" + LINK_PREFIX + EXEC_FILE);
497     symlinkSafe(dir + "/" + NORMAL_FILE, dir + "/" + LINK_PREFIX + NORMAL_FILE);
498 }
499
500 static void createInnerAppDir(const std::string &dir, const std::string &nonAppDir)
501 {
502     createTestDir(dir);
503
504     symlinkSafe(nonAppDir, dir + "/" + LINK_PREFIX + "non_app_dir");
505     symlinkSafe(nonAppDir + "/" + EXEC_FILE,
506                 dir + "/" + LINK_PREFIX + "non_app_" + EXEC_FILE);
507     symlinkSafe(nonAppDir + "/" + NORMAL_FILE,
508                 dir + "/" + LINK_PREFIX + "non_app_" + NORMAL_FILE);
509 }
510
511 static const std::string nonAppDirPath(const TemporaryTestUser &user)
512 {
513     return TMP_DIR + "/" + user.getUserName();
514 }
515
516 static void generateAppDir(const TemporaryTestUser &user,
517        const std::string &appId, const std::string &pkgId)
518 {
519     const std::string dir = TzPlatformConfig::appDirPath(user, appId, pkgId);
520     const std::string nonAppDir = nonAppDirPath(user);
521
522     createInnerAppDir(dir, nonAppDir);
523     createInnerAppDir(dir + "/.inner_dir", nonAppDir);
524     createInnerAppDir(dir + "/inner_dir", nonAppDir);
525 }
526
527 static void generateNonAppDir(const TemporaryTestUser &user)
528 {
529     const std::string dir = nonAppDirPath(user);
530
531     createTestDir(dir);
532     createTestDir(dir + "/.inner_dir");
533     createTestDir(dir + "/inner_dir");
534 }
535
536 void createTestDirs(const TemporaryTestUser &user,
537                     const std::string &appId, const std::string &pkgId)
538 {
539     generateAppDir(user, appId, pkgId);
540     generateNonAppDir(user);
541 }
542
543 void removeTestDirs(const TemporaryTestUser &user,
544                     const std::string &appId, const std::string &pkgId)
545 {
546     removeDir(TzPlatformConfig::appDirPath(user, appId, pkgId));
547     removeDir(nonAppDirPath(user));
548 }
549
550 pid_t runInChild(const std::function<void(void)> &process) {
551     pid_t pid = fork();
552     RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "fork failed");
553
554     if (pid == 0) {
555         process();
556         exit(EXIT_SUCCESS);
557     }
558     return pid;
559 }
560
561 void runInChildParentWait(const std::function<void(void)> &process) {
562     SynchronizationPipe pipe;
563     pid_t pid = fork();
564     RUNNER_ASSERT_ERRNO_MSG(pid >= 0, "fork failed");
565
566     if (pid == 0) {
567         pipe.claimChildEp();
568
569         process();
570
571         pipe.post();
572         exit(EXIT_SUCCESS);
573     } else {
574         pipe.claimParentEp();
575         pipe.wait();
576     }
577 }