libclues
Linux C++ Tracing Library
Loading...
Searching...
No Matches
clone.cxx
1// clues
2#include <clues/format.hxx>
3#include <clues/items/clone.hxx>
4#include <clues/syscalls/process.hxx>
5#include <clues/sysnrs/generic.hxx>
6#include <clues/SystemCall.hxx>
7#include <clues/Tracee.hxx>
8// private
9#include <clues/private/utils.hxx>
10
11namespace clues::item {
12
13namespace {
14
15std::string clone_flags_str(const cosmos::CloneFlags flags, const std::optional<cosmos::SignalNr> exit_signal = {}) {
16 BITFLAGS_FORMAT_START(flags);
17
18 BITFLAGS_ADD(CLONE_CHILD_CLEARTID);
19 BITFLAGS_ADD(CLONE_CHILD_SETTID);
20 BITFLAGS_ADD(CLONE_CLEAR_SIGHAND);
21 BITFLAGS_ADD(CLONE_DETACHED);
22 BITFLAGS_ADD(CLONE_FILES);
23 BITFLAGS_ADD(CLONE_FS);
24 BITFLAGS_ADD(CLONE_INTO_CGROUP);
25 BITFLAGS_ADD(CLONE_IO);
26 BITFLAGS_ADD(CLONE_NEWCGROUP);
27 BITFLAGS_ADD(CLONE_NEWIPC);
28 BITFLAGS_ADD(CLONE_NEWNET);
29 BITFLAGS_ADD(CLONE_NEWNS);
30 BITFLAGS_ADD(CLONE_NEWNS);
31 BITFLAGS_ADD(CLONE_NEWPID);
32 BITFLAGS_ADD(CLONE_NEWUSER);
33 BITFLAGS_ADD(CLONE_NEWUTS);
34 BITFLAGS_ADD(CLONE_PARENT);
35 BITFLAGS_ADD(CLONE_PARENT_SETTID);
36 BITFLAGS_ADD(CLONE_PIDFD);
37 BITFLAGS_ADD(CLONE_PTRACE);
38 BITFLAGS_ADD(CLONE_SETTLS);
39 BITFLAGS_ADD(CLONE_SIGHAND);
40 BITFLAGS_ADD(CLONE_SYSVSEM);
41 BITFLAGS_ADD(CLONE_THREAD);
42 BITFLAGS_ADD(CLONE_UNTRACED);
43 BITFLAGS_ADD(CLONE_VFORK);
44 BITFLAGS_ADD(CLONE_VM);
45
46 if (exit_signal) {
47 BITFLAGS_STREAM() << format::signal(*exit_signal, /*verbose=*/false);
48 }
49
50 return BITFLAGS_STR();
51}
52
53}
54
55std::string CloneFlagsValue::str() const {
56 return clone_flags_str(m_flags, exit_signal);
57}
58
60 if (m_call->callNr() == SystemCallNr::CLONE) {
61 // child exit signal is piggy-backed in the low byte of flags
62 // although clone(2) shows an `int` here, this is a 64-bit flags typetype
63 const auto raw = valueAs<uint64_t>();
64 // CloneFlags are based on uint64_t (type used in clone3()),
65 // thus cast accordingly.
66 m_flags = cosmos::CloneFlags{static_cast<uint64_t>(raw & (~0xFF))};
67 exit_signal.emplace(cosmos::SignalNr{static_cast<int>(raw) & 0xFF});
68 } else {
70 exit_signal.reset();
71 }
72}
73
74void CloneArgs::resetArgs() {
75 m_pidfd = cosmos::FileNum::INVALID;
76 m_cgroup2_fd = cosmos::FileNum::INVALID;
77 m_child_tid = cosmos::ThreadID::INVALID;
78 m_tid_array.clear();
79 m_args.reset();
80}
81
83
84 resetArgs();
85
86 if (!verifySize())
87 return;
88
89 m_args.emplace(cosmos::CloneArgs{});
90
91 // ignore the check for trivial types, cosmos::CloneArgs has a
92 // constructor to set the whole structure to zero, we can live with that
93 // not happening here.
94 if (!proc.readStruct<cosmos::CloneArgs, /*CHECK_TRIVIAL=*/false>(asPtr(), *m_args)) {
95 m_args.reset();
96 return;
97 }
98
99 const auto &args = *m_args;
100 const auto raw = args.raw();
101 const auto num_tids = raw->set_tid_size;
102
103 if (num_tids > 0) {
104 m_tid_array.resize(num_tids);
105 try {
106 proc.readBlob(
107 ForeignPtr{static_cast<uintptr_t>(raw->set_tid)},
108 reinterpret_cast<char*>(m_tid_array.data()), num_tids * sizeof(cosmos::ThreadID));
109 } catch (const std::exception &ex) {
110 // could be an invalid userspace pointer
111 m_tid_array.clear();
112 }
113 }
114
115 if (args.isSet(cosmos::CloneFlag::INTO_CGROUP)) {
116 m_cgroup2_fd = args.cgroup().raw();
117 }
118}
119
120void CloneArgs::updateData(const Tracee &proc) {
121
122 if (!m_args)
123 return;
124
125 const auto &args = *m_args;
126 const auto raw = args.raw();
127 const auto flags = args.flags();
128 using enum cosmos::CloneFlag;
129
130 if (flags[PIDFD]) {
131 // read the assigned PID file descriptor from tracee memory.
132 proc.readStruct(ForeignPtr{static_cast<uintptr_t>(raw->pidfd)}, m_pidfd);
133 }
134
135 if (flags[PARENT_SETTID]) {
136 proc.readStruct(ForeignPtr{static_cast<uintptr_t>(raw->parent_tid)}, m_child_tid);
137 }
138}
139
140bool CloneArgs::verifySize() const {
141 if (m_call->callNr() == SystemCallNr::CLONE3) {
142 /* we need to make a forward lookup of the size argument,
143 * which follows the cl_args parameter */
144 const auto info = *m_call->currentInfo()->entryInfo();
145
146 if (info.args()[1] < sizeof(cosmos::CloneArgs)) {
147 return false;
148 }
149
150 return true;
151 } else {
152 // yet unknown system call?
153 return false;
154 }
155}
156
157std::string CloneArgs::str() const {
158 if (!m_args) {
159 if (isZero())
160 return "NULL";
161 else
162 // verifySize() failed
163 return format::pointer(asPtr()) + " (size mismatch)";
164 }
165
166 auto uint2ptr = [](uint64_t val) -> ForeignPtr {
167 return ForeignPtr{static_cast<uintptr_t>(val)};
168 };
169
170 std::stringstream ss;
171 const auto &args = *m_args;
172 const auto flags = args.flags();
173 const auto raw = args.raw();
174 using enum cosmos::CloneFlag;
175
176 ss << "{";
177 ss << "flags=" << clone_flags_str(flags);
178 if (flags[PIDFD]) {
179 ss << ", pidfd=" << format::pointer(uint2ptr(raw->pidfd), std::to_string(cosmos::to_integral(m_pidfd)));
180 }
181 if (flags[CHILD_CLEARTID] || flags[CHILD_SETTID]) {
182 const auto child_tid_ptr = ForeignPtr{reinterpret_cast<uintptr_t>(args.childTID())};
183 ss << ", child_tid=" << format::pointer(child_tid_ptr);
184 }
185 if (flags[PARENT_SETTID]) {
186 const auto parent_tid_ptr = ForeignPtr{reinterpret_cast<uintptr_t>(args.parentTID())};
187 ss << ", parent_tid=" << format::pointer(parent_tid_ptr,
188 std::to_string(cosmos::to_integral(m_child_tid)));
189 }
190 ss << ", exit_signal=" << format::signal(args.exitSignal().raw(), /*verbose=*/false);
191 const auto stack_ptr = ForeignPtr{reinterpret_cast<uintptr_t>(args.stack())};
192 ss << ", stack=" << format::pointer(stack_ptr);
193 ss << ", stack_size=" << args.stackSize();
194 if (flags[SETTLS]) {
195 // this is a architecture dependent value, interpreting it as
196 // a hex pointer should be good enough for now.
197 ss << ", tls=" << format::pointer(uint2ptr(raw->tls));
198 }
199
200 const auto settid_ptr = uint2ptr(raw->set_tid);
201 if (raw->set_tid_size) {
202 std::stringstream ss2;
203 std::string sep = "";
204 for (const auto tid: m_tid_array) {
205 ss2 << sep;
206 ss2 << cosmos::to_integral(tid);
207 if (sep.empty())
208 sep = ", ";
209 }
210 ss << ", set_tid=" << format::pointer(settid_ptr, ss2.str());
211 } else {
212 /*
213 * don't evaluate the pointed-to data in this case, but it
214 * could still be interesting to know if some strange value is
215 * passed here alongside the 0 size.
216 */
217 ss << ", set_tid=" << format::pointer(settid_ptr);
218 }
219 ss << ", set_tid_size=" << raw->set_tid_size;
220
221 if (flags[INTO_CGROUP]) {
222 ss << ", cgroup=" << cosmos::to_integral(m_cgroup2_fd);
223 }
224
225 ss << "}";
226
227 return ss.str();
228}
229
230} // end ns
const SystemCall * m_call
The system call context this item part of.
bool isZero() const
Returns whether the parameter is set to 0 / NULL.
OTHER valueAs() const
Helper to cast the strongly typed Word m_val to other strong enum types.
const SystemCallInfo * currentInfo() const
Access the current SystemCallInfo if currently processing syscall entry/exit.
SystemCallNr callNr() const
Returns the system call table number for this system call.
Base class for traced processes.
Definition Tracee.hxx:39
bool readStruct(const ForeignPtr addr, T &out) const
Reads a system call struct from the tracee's address space into out.
Definition Tracee.hxx:221
void readBlob(const ForeignPtr addr, char *buffer, const size_t bytes) const
Reads an arbitrary binary blob of fixed length from the tracee.
Definition Tracee.cxx:981
std::string str() const override
Returns a human readable string representation of the item.
Definition clone.cxx:157
void updateData(const Tracee &proc) override
Called upon exit of the system call to update possible out parameters.
Definition clone.cxx:120
void processValue(const Tracee &) override
Processes the value stored in m_val acc. to the actual item type.
Definition clone.cxx:82
cosmos::ThreadID tid() const
Return the new child's ThreadID stored in parent's memory.
Definition clone.hxx:89
const std::optional< cosmos::CloneArgs > & args() const
Returns an optional containing the cosmos::CloneArgs structure, if available.
Definition clone.hxx:60
std::optional< cosmos::SignalNr > exit_signal
For clone() 1/2 this contains the child exit signal, which is additionally encoded in the flags.
Definition clone.hxx:40
void processValue(const Tracee &) override
Processes the value stored in m_val acc. to the actual item type.
Definition clone.cxx:59
std::string str() const override
Returns a human readable string representation of the item.
Definition clone.cxx:55
ForeignPtr
Strongly typed opaque pointer to tracee memory.
Definition types.hxx:140