1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
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 <malloc.h>
16 #include "hook_socket_client.h"
17
18 #include "common.h"
19 #include "hook_common.h"
20 #include "unix_socket_client.h"
21 #include "share_memory_allocator.h"
22 #include "logging.h"
23 #include "sampling.h"
24 #include <unistd.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <cstdio>
29 #include <cstring>
30 #include <iostream>
31
32 namespace {
33 constexpr int FLUSH_FLAG = 20;
34 std::atomic<uint64_t> g_flushCount = 0;
35 std::atomic<bool> g_disableHook = false;
36 constexpr uint32_t MEMCHECK_DETAILINFO_MAXSIZE = 102400;
37
38 struct OptArg {
39 size_t pos;
40 char *buf;
41 };
42
43 } // namespace
44
45
GetRealTime()46 static std::string GetRealTime()
47 {
48 time_t now = time(nullptr);
49 tm tm;
50 const int timeLength = 64;
51 char stampStr[timeLength] = {0};
52
53 if (localtime_r(&now, &tm) == nullptr || strftime(stampStr, timeLength, "%Y/%m/%d %H:%M:%S", &tm) == 0) {
54 return "error time format!";
55 }
56 return std::string(stampStr);
57 }
58
NmdWriteStat(void * arg,const char * buf)59 static void NmdWriteStat(void *arg, const char *buf)
60 {
61 struct OptArg *opt = static_cast<struct OptArg*>(arg);
62 std::string getNmdTime = std::to_string(getpid()) + " " + GetRealTime() + "\n";
63 size_t nmdTimeLen = getNmdTime.size();
64 if (strncpy_s(opt->buf + opt->pos, MEMCHECK_DETAILINFO_MAXSIZE - opt->pos,
65 getNmdTime.c_str(), nmdTimeLen) != EOK) {
66 return;
67 }
68 opt->pos += nmdTimeLen;
69
70 size_t len = strlen(buf);
71 if (len + opt->pos + 1 > MEMCHECK_DETAILINFO_MAXSIZE) {
72 return;
73 }
74 if (strncpy_s(opt->buf + opt->pos, MEMCHECK_DETAILINFO_MAXSIZE - opt->pos, buf, len) != EOK) {
75 return;
76 }
77 opt->pos += len;
78 }
79
HookSocketClient(int pid,ClientConfig * config,Sampling * sampler,void (* disableHookCallback)())80 HookSocketClient::HookSocketClient(int pid, ClientConfig *config, Sampling *sampler, void (*disableHookCallback)())
81 : pid_(pid), config_(config), sampler_(sampler), disableHookCallback_(disableHookCallback)
82 {
83 smbFd_ = 0;
84 eventFd_ = 0;
85 unixSocketClient_ = nullptr;
86 serviceName_ = "HookService";
87 Connect(DEFAULT_UNIX_SOCKET_HOOK_FULL_PATH);
88 }
89
~HookSocketClient()90 HookSocketClient::~HookSocketClient()
91 {
92 if (stackWriter_) {
93 stackWriter_->Flush();
94 }
95 unixSocketClient_ = nullptr;
96 stackWriter_ = nullptr;
97 }
98
Connect(const std::string addrname)99 bool HookSocketClient::Connect(const std::string addrname)
100 {
101 if (unixSocketClient_ != nullptr) {
102 return false;
103 }
104 unixSocketClient_ = std::make_shared<UnixSocketClient>();
105 if (!unixSocketClient_->Connect(addrname, *this)) {
106 unixSocketClient_ = nullptr;
107 return false;
108 }
109
110 unixSocketClient_->SendHookConfig(reinterpret_cast<uint8_t *>(&pid_), sizeof(pid_));
111 return true;
112 }
113
ProtocolProc(SocketContext & context,uint32_t pnum,const int8_t * buf,const uint32_t size)114 bool HookSocketClient::ProtocolProc(SocketContext &context, uint32_t pnum, const int8_t *buf, const uint32_t size)
115 {
116 if (size != sizeof(ClientConfig)) {
117 return true;
118 }
119 *config_ = *reinterpret_cast<ClientConfig *>(const_cast<int8_t*>(buf));
120 config_->maxStackDepth = config_->maxStackDepth > MAX_UNWIND_DEPTH ? MAX_UNWIND_DEPTH : config_->maxStackDepth;
121 std::string configStr = config_->ToString();
122 sampler_->InitSampling(config_->sampleInterval);
123 smbFd_ = context.ReceiveFileDiscriptor();
124 eventFd_ = context.ReceiveFileDiscriptor();
125 std::string smbName = "hooknativesmb_" + std::to_string(pid_);
126 stackWriter_ = std::make_shared<StackWriter>(smbName, config_->shareMemorySize,
127 smbFd_, eventFd_, config_->isBlocked);
128 nmdType_ = config_->nmdType;
129 if (nmdType_ == 0) {
130 SendNmdInfo();
131 }
132 return true;
133 }
134
SendStack(const void * data,size_t size)135 bool HookSocketClient::SendStack(const void* data, size_t size)
136 {
137 if (stackWriter_ == nullptr || unixSocketClient_ == nullptr) {
138 return false;
139 }
140
141 if (!unixSocketClient_->SendHeartBeat()) {
142 return false;
143 }
144
145 stackWriter_->WriteTimeout(data, size);
146 stackWriter_->Flush();
147
148 return true;
149 }
150
SendStackWithPayload(const void * data,size_t size,const void * payload,size_t payloadSize)151 bool HookSocketClient::SendStackWithPayload(const void* data, size_t size, const void* payload,
152 size_t payloadSize)
153 {
154 if (stackWriter_ == nullptr || unixSocketClient_ == nullptr || g_disableHook) {
155 return false;
156 } else if (unixSocketClient_->GetClientState() == CLIENT_STAT_THREAD_EXITED) {
157 DisableHook();
158 return false;
159 }
160
161 bool ret = stackWriter_->WriteWithPayloadTimeout(data, size, payload, payloadSize,
162 std::bind(&HookSocketClient::PeerIsConnected, this));
163 if (!ret && config_->isBlocked) {
164 DisableHook();
165 return false;
166 }
167 ++g_flushCount;
168 if (g_flushCount % FLUSH_FLAG == 0) {
169 stackWriter_->Flush();
170 }
171 return true;
172 }
173
Flush()174 void HookSocketClient::Flush()
175 {
176 if (stackWriter_ == nullptr || unixSocketClient_ == nullptr) {
177 return;
178 }
179 stackWriter_->Flush();
180 }
181
DisableHook()182 void HookSocketClient::DisableHook()
183 {
184 bool expected = false;
185 if (g_disableHook.compare_exchange_strong(expected, true, std::memory_order_release, std::memory_order_relaxed)) {
186 HILOG_INFO(LOG_CORE, "%s", __func__);
187 if (disableHookCallback_) {
188 disableHookCallback_();
189 }
190 }
191 }
192
PeerIsConnected()193 bool HookSocketClient::PeerIsConnected()
194 {
195 return !unixSocketClient_->IsConnected();
196 }
197
SendNmdInfo()198 bool HookSocketClient::SendNmdInfo()
199 {
200 if (!config_->printNmd) {
201 return false;
202 }
203 void* nmdBuf = malloc(MEMCHECK_DETAILINFO_MAXSIZE);
204 if (nmdBuf == nullptr) {
205 return false;
206 }
207 struct OptArg opt = {0, reinterpret_cast<char*>(nmdBuf) };
208 malloc_stats_print(NmdWriteStat, &opt, "a");
209 StackRawData rawdata = {{{{0}}}};
210 rawdata.type = NMD_MSG;
211 if (stackWriter_) {
212 stackWriter_->WriteWithPayloadTimeout(&rawdata, sizeof(BaseStackRawData),
213 reinterpret_cast<int8_t*>(opt.buf), strlen(opt.buf) + 1,
214 std::bind(&HookSocketClient::PeerIsConnected, this));
215 }
216 free(nmdBuf);
217 return true;
218 }
219
SendEndMsg()220 bool HookSocketClient::SendEndMsg()
221 {
222 StackRawData rawdata = {{{{0}}}};
223 rawdata.type = END_MSG;
224 if (stackWriter_) {
225 stackWriter_->WriteTimeout(&rawdata, sizeof(BaseStackRawData));
226 }
227 return true;
228 }