1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2022-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
16#include "hidebug_base.h"
17
18#include <dlfcn.h>
19#include <errno.h>
20#include <inttypes.h>
21#include <limits.h>
22#include <signal.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "securec.h"
29
30#ifdef HIDEBUG_IN_INIT
31#include "init_module_engine.h"
32#include "init_log.h"
33#define HIDEBUG_LOGE(...) INIT_LOGE(__VA_ARGS__)
34#define HIDEBUG_LOGI(...) INIT_LOGI(__VA_ARGS__)
35#define LOG_PRIV_PUBLIC ""
36#else
37#include <parameter.h>
38#include <sysparam_errno.h>
39#include <hilog/log.h>
40#include "hichecker_wrapper.h"
41
42#undef LOG_DOMAIN
43#undef LOG_TAG
44#define LOG_DOMAIN 0xD002D0A
45#define LOG_TAG "HiDebug_Native"
46
47#define HIDEBUG_LOGE(...) HILOG_ERROR(LOG_CORE, __VA_ARGS__)
48#define HIDEBUG_LOGI(...) HILOG_INFO(LOG_CORE, __VA_ARGS__)
49#define LOG_PRIV_PUBLIC "{public}"
50#endif
51
52#define MAX_PARA_LEN 50
53#define MAX_PARA_CNT 20
54#define PARAM_BUF_LEN 128
55#define QUERYNAME_LEN 80
56#define COLON_CHR ':'
57#define SLASH_CHR '/'
58const char * const LIBC_HOOK_PARAM = "libc.hook_mode";
59
60struct Params {
61    char key[MAX_PARA_LEN];
62    char value[MAX_PARA_LEN];
63};
64
65static void ParseKeyValue(const char *input, uint32_t *paramCnt, struct Params *result, const uint32_t paramSize)
66{
67    if (*paramCnt >= paramSize) {
68        HIDEBUG_LOGE("Parameters is Full.");
69        return;
70    }
71    const char *colonPos = strchr(input, COLON_CHR);
72    if (colonPos == NULL) {
73        HIDEBUG_LOGE("params is illegal.");
74        return;
75    }
76    errno_t err = strncpy_s(result[*paramCnt].key, MAX_PARA_LEN, input, colonPos - input);
77    if (err != EOK) {
78        HIDEBUG_LOGE("strncpy_s copy key strings failed.");
79        return;
80    }
81    err = strncpy_s(result[*paramCnt].value, MAX_PARA_LEN, colonPos + 1, strlen(colonPos + 1));
82    if (err != EOK) {
83        HIDEBUG_LOGE("strncpy_s copy value strings failed.");
84        return;
85    }
86    (*paramCnt)++;
87}
88
89static uint32_t SplitParams(char *input, struct Params *result, const uint32_t paramSize)
90{
91    uint32_t paramCnt = 0;
92    const char space[] = " ";
93    char *param;
94    char *next = NULL;
95    param = strtok_s(input, space, &next);
96    while (param != NULL) {
97        ParseKeyValue(param, &paramCnt, result, paramSize);
98        param = strtok_s(NULL, space, &next);
99    }
100    return paramCnt;
101}
102
103static bool QueryParams(const char *queryName, char *result, const uint32_t size)
104{
105    if (result == NULL || size > PARAM_BUF_LEN) {
106        return false;
107    }
108#ifdef HIDEBUG_IN_INIT
109    uint32_t bufferSize = size;
110    int retLen = SystemReadParam(queryName, result, &bufferSize);
111    if (retLen != 0 || bufferSize == 0 || bufferSize > PARAM_BUF_LEN - 1) {
112        return false;
113    }
114    result[bufferSize] = '\0';
115#else
116    int retLen = GetParameter(queryName, NULL, result, size);
117    if (retLen <= 0 || retLen > PARAM_BUF_LEN - 1) {
118        return false;
119    }
120    result[retLen] = '\0';
121#endif
122    return true;
123}
124
125static bool ConcatenateParamName(char* queryName, const size_t size, const char* serviceName)
126{
127    if (strcat_s(queryName, size, serviceName) != EOK) {
128        HIDEBUG_LOGE("strcat_s query name failed.");
129        return false;
130    }
131    return true;
132}
133
134static bool InitHiDebugEnvParams(const char *queryName)
135{
136    char paramOutBuf[PARAM_BUF_LEN] = { 0 };
137    if (!QueryParams(queryName, paramOutBuf, PARAM_BUF_LEN)) {
138        return false;
139    }
140    struct Params params[MAX_PARA_CNT];
141    uint32_t paramsCnt = SplitParams(paramOutBuf, params, MAX_PARA_CNT);
142    for (uint32_t i = 0; i < paramsCnt; ++i) {
143        if (setenv(params[i].key, params[i].value, 1) != 0) { // 1 : overwrite
144            HIDEBUG_LOGE("setenv failed, err: %" LOG_PRIV_PUBLIC "s", strerror(errno));
145        }
146    }
147    return paramsCnt > 0;
148}
149
150static const char* FilterServiceName(const char *inputName)
151{
152    const char *ret = strrchr(inputName, SLASH_CHR);
153    if (ret == NULL) {
154        return inputName;
155    }
156    return ret + 1;
157}
158
159static int GetMallocHookStartupValue(const char *param, char *path, int size)
160{
161    if (path == NULL || size <= 0) {
162        return -1;
163    }
164
165    const char *ptr = param;
166
167    while (*ptr && *ptr != ':') {
168        ++ptr;
169    }
170    if (*ptr == ':') {
171        ++ptr;
172    }
173    const int paramLength = 7;
174    if (strncmp(param, "startup", paramLength) == 0) {
175        if (*ptr == '\"') {
176            ++ptr;
177            int idx = 0;
178            while (idx < size - 1 && *ptr && *ptr != '\"') {
179                path[idx++] = *ptr++;
180            }
181            path[idx] = '\0';
182        } else {
183            int idx = 0;
184            while (idx < size - 1 && *ptr) {
185                path[idx++] = *ptr++;
186            }
187            path[idx] = '\0';
188        }
189    }
190    return 0;
191}
192
193static bool MatchMallocHookStartupProp(const char *thisName)
194{
195    char paramOutBuf[PARAM_BUF_LEN] = { 0 };
196    char targetProcName[PARAM_BUF_LEN] = { 0 };
197
198#ifdef HIDEBUG_IN_INIT
199    uint32_t size = PARAM_BUF_LEN;
200    int retLen = SystemReadParam(LIBC_HOOK_PARAM, paramOutBuf, &size);
201    if (retLen != 0 || size <= 0) {
202        return 0;
203    }
204    retLen = (int)size;
205#else
206    char defStrValue[PARAM_BUF_LEN] = { 0 };
207    int retLen = GetParameter(LIBC_HOOK_PARAM, defStrValue, paramOutBuf, PARAM_BUF_LEN);
208    if (retLen == 0) {
209        return 0;
210    }
211#endif
212    const int paramLength = 8;
213    if (strncmp(paramOutBuf, "startup:", paramLength) != 0) {
214        return false;
215    }
216    retLen = GetMallocHookStartupValue(paramOutBuf, targetProcName, PARAM_BUF_LEN);
217    if (retLen == -1) {
218        HIDEBUG_LOGE("malloc hook parse startup value failed");
219        return false;
220    }
221
222    const int targetProcNameSize = strlen(targetProcName);
223    if (strncmp(targetProcName, "init", targetProcNameSize) == 0) {
224        HIDEBUG_LOGI("malloc hook: this target proc '%" LOG_PRIV_PUBLIC "s' no hook", targetProcName);
225        return false;
226    }
227
228    if (strcmp(thisName, targetProcName) == 0) {
229        return true;
230    }
231    return false;
232}
233
234static int SetupMallocHookAtStartup(const char *thisName)
235{
236    const int hookSignal = 36;
237    if (!MatchMallocHookStartupProp(thisName)) {
238        return 0;
239    }
240    HIDEBUG_LOGI("malloc send hook signal.");
241    return raise(hookSignal);
242}
243
244bool InitEnvironmentParam(const char *inputName)
245{
246    if (inputName == NULL) {
247        HIDEBUG_LOGE("input service name is null.");
248        return false;
249    }
250    const char *serviceName = FilterServiceName(inputName);
251    if (*serviceName == '\0') {
252        HIDEBUG_LOGE("input service name is illegal.");
253        return false;
254    }
255#ifndef HIDEBUG_IN_INIT
256    InitHicheckerParamWrapper(serviceName);
257#endif
258
259#ifdef HAS_MUSL_STARTUP_MALLOC_HOOK_INTF
260    setup_malloc_hook_mode();
261#else
262    SetupMallocHookAtStartup(serviceName);
263#endif
264
265    char paramOutBuf[PARAM_BUF_LEN] = { 0 };
266    if (!QueryParams("const.debuggable", paramOutBuf, PARAM_BUF_LEN) || strcmp(paramOutBuf, "1") != 0) {
267        return false;
268    }
269    char onceName[QUERYNAME_LEN] = "hiviewdfx.debugenv.";
270    if (!ConcatenateParamName(onceName, sizeof(onceName), serviceName)) {
271        return false;
272    }
273    if (InitHiDebugEnvParams(onceName)) {
274        return true;
275    }
276    char persistName[QUERYNAME_LEN] = "persist.hiviewdfx.debugenv.";
277    return ConcatenateParamName(persistName, sizeof(onceName), serviceName) && InitHiDebugEnvParams(persistName);
278}
279