1 /*
2 * Copyright (C) 2023 The Android Open Source Project
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 "gtest/gtest.h"
18
19 #include <linux/sched.h>
20 #include <sched.h>
21 #include <sys/wait.h>
22
23 #include <atomic>
24 #include <csignal>
25 #include <cstdlib>
26
27 #include "berberis/ndk_program_tests/scoped_sigaction.h"
28
29 namespace {
30
31 constexpr size_t kChildStack = 1024;
32
33 bool g_parent_handler_called;
34 bool g_child_handler_called;
35 bool g_grandchild_handler_called;
36
VerifySignalHandler(bool * flag)37 void VerifySignalHandler(bool* flag) {
38 *flag = false;
39 raise(SIGPWR);
40 ASSERT_TRUE(*flag);
41 }
42
43 template <size_t kStackSize, typename Runner>
CloneVMAndWait(Runner runner,int extra_flags,int expect_return)44 void CloneVMAndWait(Runner runner, int extra_flags, int expect_return) {
45 void* child_stack[kStackSize];
46 pid_t tid = clone(runner, &child_stack[kStackSize], CLONE_VM | extra_flags, nullptr);
47 int status;
48 ASSERT_EQ(tid, TEMP_FAILURE_RETRY(waitpid(tid, &status, __WCLONE)));
49 ASSERT_TRUE(WIFEXITED(status));
50 ASSERT_EQ(WEXITSTATUS(status), expect_return);
51 }
52
SharedSighandRunner(void *)53 int SharedSighandRunner(void*) {
54 // Grandchild shared handlers with child.
55 VerifySignalHandler(&g_child_handler_called);
56 struct sigaction sa {
57 .sa_handler = +[](int) { g_grandchild_handler_called = true; }
58 };
59 // We intentionally do not restore sigaction to verify that this change
60 // will also change the handler in child (parent of grandchild).
61 EXPECT_EQ(sigaction(SIGPWR, &sa, nullptr), 0);
62 VerifySignalHandler(&g_grandchild_handler_called);
63 return 21;
64 }
65
UnsharedSighandRunner(void *)66 int UnsharedSighandRunner(void*) {
67 // Child inherits a copy of parent handlers.
68 VerifySignalHandler(&g_parent_handler_called);
69 struct sigaction sa {
70 .sa_handler = +[](int) { g_child_handler_called = true; }
71 };
72 // We intentionally do not restore sigaction to verify that this change
73 // doesn't affect signal handlers in parent.
74 EXPECT_EQ(sigaction(SIGPWR, &sa, nullptr), 0);
75 VerifySignalHandler(&g_child_handler_called);
76 // Now clone with shared handlers.
77 CloneVMAndWait<kChildStack>(SharedSighandRunner, CLONE_SIGHAND, 21);
78 VerifySignalHandler(&g_grandchild_handler_called);
79 return 42;
80 }
81
TEST(Clone,CloneVMSighandSharing)82 TEST(Clone, CloneVMSighandSharing) {
83 struct sigaction sa {
84 .sa_handler = +[](int) { g_parent_handler_called = true; }
85 };
86 ScopedSigaction scoped_sa(SIGPWR, &sa);
87 // Clone a child with non-shared signal handlers.
88 // Note that child's stack contains grandchild's stack, so should be larger.
89 CloneVMAndWait<kChildStack * 2>(UnsharedSighandRunner, 0, 42);
90 // Verify that children didn't alter parent's signal handlers.
91 VerifySignalHandler(&g_parent_handler_called);
92 }
93
94 // We cannot accurately detect when grandchild stack can be free'd. So
95 // we just keep it in a global variable and never free.
96 void* g_grandchild_stack[kChildStack];
97 std::atomic<bool> g_child_finished;
98 std::atomic<bool> g_grandchild_finished;
99
WaitUntilParentExitsAndVerifySignalHandlers(void *)100 int WaitUntilParentExitsAndVerifySignalHandlers(void*) {
101 while (!g_child_finished) {
102 sched_yield();
103 }
104
105 // Grandchild shares handlers with child and should still
106 // be able to use them after child terminated.
107 VerifySignalHandler(&g_child_handler_called);
108
109 g_grandchild_finished = true;
110 return 0;
111 }
112
CloneOutlivingChild(void *)113 int CloneOutlivingChild(void*) {
114 struct sigaction sa {
115 .sa_handler = +[](int) { g_child_handler_called = true; }
116 };
117 EXPECT_EQ(sigaction(SIGPWR, &sa, nullptr), 0);
118
119 clone(WaitUntilParentExitsAndVerifySignalHandlers,
120 &g_grandchild_stack[kChildStack],
121 CLONE_VM | CLONE_SIGHAND,
122 nullptr);
123 return 42;
124 }
125
TEST(Clone,CloneVMChildOutlivingParent)126 TEST(Clone, CloneVMChildOutlivingParent) {
127 // We'll test grandchild outliving child.
128 g_child_finished = false;
129 g_grandchild_finished = false;
130
131 CloneVMAndWait<kChildStack>(CloneOutlivingChild, 0, 42);
132
133 g_child_finished = true;
134
135 // Wait for grandchild to finish.
136 while (!g_grandchild_finished) {
137 sched_yield();
138 }
139 }
140
141 } // namespace
142