Fix: lttng-ctl: assertion failure during unregistration of trigger
[lttng-tools.git] / src / bin / lttng / uprobe.c
1 /*
2 * Copyright (C) 2020 EfficiOS, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include "uprobe.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13
14 #include "common/compat/getenv.h"
15 #include "common/string-utils/string-utils.h"
16 #include "common/utils.h"
17 #include "lttng/constant.h"
18
19 #include "command.h"
20
21 /*
22 * Walk the directories in the PATH environment variable to find the target
23 * binary passed as parameter.
24 *
25 * On success, the full path of the binary is copied in binary_full_path out
26 * parameter. This buffer is allocated by the caller and must be at least
27 * LTTNG_PATH_MAX bytes long.
28 * On failure, returns -1;
29 */
30 static
31 int walk_command_search_path(const char *binary, char *binary_full_path)
32 {
33 char *tentative_binary_path = NULL;
34 char *command_search_path = NULL;
35 char *curr_search_dir_end = NULL;
36 char *curr_search_dir = NULL;
37 struct stat stat_output;
38 int ret = 0;
39
40 command_search_path = lttng_secure_getenv("PATH");
41 if (!command_search_path) {
42 ret = -1;
43 goto end;
44 }
45
46 /*
47 * Duplicate the $PATH string as the char pointer returned by getenv() should
48 * not be modified.
49 */
50 command_search_path = strdup(command_search_path);
51 if (!command_search_path) {
52 ret = -1;
53 goto end;
54 }
55
56 /*
57 * This char array is used to concatenate path to binary to look for
58 * the binary.
59 */
60 tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
61 if (!tentative_binary_path) {
62 ret = -1;
63 goto alloc_error;
64 }
65
66 curr_search_dir = command_search_path;
67 do {
68 /*
69 * Split on ':'. The return value of this call points to the
70 * matching character.
71 */
72 curr_search_dir_end = strchr(curr_search_dir, ':');
73 if (curr_search_dir_end != NULL) {
74 /*
75 * Add a NULL byte to the end of the first token so it
76 * can be used as a string.
77 */
78 curr_search_dir_end[0] = '\0';
79 }
80
81 /* Empty the tentative path */
82 memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
83
84 /*
85 * Build the tentative path to the binary using the current
86 * search directory and the name of the binary.
87 */
88 ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
89 curr_search_dir, binary);
90 if (ret < 0) {
91 goto free_binary_path;
92 }
93 if (ret < LTTNG_PATH_MAX) {
94 /*
95 * Use STAT(2) to see if the file exists.
96 */
97 ret = stat(tentative_binary_path, &stat_output);
98 if (ret == 0) {
99 /*
100 * Verify that it is a regular file or a
101 * symlink and not a special file (e.g.
102 * device).
103 */
104 if (S_ISREG(stat_output.st_mode)
105 || S_ISLNK(stat_output.st_mode)) {
106 /*
107 * Found a match, set the out parameter
108 * and return success.
109 */
110 ret = lttng_strncpy(binary_full_path,
111 tentative_binary_path,
112 LTTNG_PATH_MAX);
113 if (ret == -1) {
114 ERR("Source path does not fit "
115 "in destination buffer.");
116 }
117 goto free_binary_path;
118 }
119 }
120 }
121 /* Go to the next entry in the $PATH variable. */
122 curr_search_dir = curr_search_dir_end + 1;
123 } while (curr_search_dir_end != NULL);
124
125 free_binary_path:
126 free(tentative_binary_path);
127 alloc_error:
128 free(command_search_path);
129 end:
130 return ret;
131 }
132
133 /*
134 * Check if the symbol field passed by the user is in fact an address or an
135 * offset from a symbol. Those two instrumentation types are not supported yet.
136 * It's expected to be a common mistake because of the existing --probe option
137 * that does support these formats.
138 *
139 * Here are examples of these unsupported formats for the --userspace-probe
140 * option:
141 * elf:/path/to/binary:0x400430
142 * elf:/path/to/binary:4194364
143 * elf:/path/to/binary:my_symbol+0x323
144 * elf:/path/to/binary:my_symbol+43
145 */
146 static
147 int warn_userspace_probe_syntax(const char *symbol)
148 {
149 int ret;
150
151 /* Check if the symbol field is an hex address. */
152 ret = sscanf(symbol, "0x%*x");
153 if (ret > 0) {
154 /* If there is a match, print a warning and return an error. */
155 ERR("Userspace probe on address not supported yet.");
156 ret = CMD_UNSUPPORTED;
157 goto error;
158 }
159
160 /* Check if the symbol field is an decimal address. */
161 ret = sscanf(symbol, "%*u");
162 if (ret > 0) {
163 /* If there is a match, print a warning and return an error. */
164 ERR("Userspace probe on address not supported yet.");
165 ret = CMD_UNSUPPORTED;
166 goto error;
167 }
168
169 /* Check if the symbol field is symbol+hex_offset. */
170 ret = sscanf(symbol, "%*[^+]+0x%*x");
171 if (ret > 0) {
172 /* If there is a match, print a warning and return an error. */
173 ERR("Userspace probe on symbol+offset not supported yet.");
174 ret = CMD_UNSUPPORTED;
175 goto error;
176 }
177
178 /* Check if the symbol field is symbol+decimal_offset. */
179 ret = sscanf(symbol, "%*[^+]+%*u");
180 if (ret > 0) {
181 /* If there is a match, print a warning and return an error. */
182 ERR("Userspace probe on symbol+offset not supported yet.");
183 ret = CMD_UNSUPPORTED;
184 goto error;
185 }
186
187 ret = 0;
188
189 error:
190 return ret;
191 }
192
193 /*
194 * Parse userspace probe options
195 * Set the userspace probe fields in the lttng_event struct and set the
196 * target_path to the path to the binary.
197 */
198 LTTNG_HIDDEN
199 int parse_userspace_probe_opts(const char *opt,
200 struct lttng_userspace_probe_location **probe_location)
201 {
202 int ret = CMD_SUCCESS;
203 int num_token;
204 char **tokens = NULL;
205 char *target_path = NULL;
206 char *unescaped_target_path = NULL;
207 char *real_target_path = NULL;
208 char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
209 struct lttng_userspace_probe_location *probe_location_local = NULL;
210 struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
211
212 assert(opt);
213
214 /*
215 * userspace probe fields are separated by ':'.
216 */
217 tokens = strutils_split(opt, ':', 1);
218 num_token = strutils_array_of_strings_len(tokens);
219
220 /*
221 * Early sanity check that the number of parameter is between 2 and 4
222 * inclusively.
223 * elf:PATH:SYMBOL
224 * std:PATH:PROVIDER_NAME:PROBE_NAME
225 * PATH:SYMBOL (same behavior as ELF)
226 */
227 if (num_token < 2 || num_token > 4) {
228 ret = CMD_ERROR;
229 goto end;
230 }
231
232 /*
233 * Looking up the first parameter will tell the technique to use to
234 * interpret the userspace probe/function description.
235 */
236 switch (num_token) {
237 case 2:
238 /* When the probe type is omitted we assume ELF for now. */
239 case 3:
240 if (num_token == 3 && strcmp(tokens[0], "elf") == 0) {
241 target_path = tokens[1];
242 symbol_name = tokens[2];
243 } else if (num_token == 2) {
244 target_path = tokens[0];
245 symbol_name = tokens[1];
246 } else {
247 ret = CMD_ERROR;
248 goto end;
249 }
250 lookup_method =
251 lttng_userspace_probe_location_lookup_method_function_elf_create();
252 if (!lookup_method) {
253 WARN("Failed to create ELF lookup method");
254 ret = CMD_ERROR;
255 goto end;
256 }
257 break;
258 case 4:
259 if (strcmp(tokens[0], "sdt") == 0) {
260 target_path = tokens[1];
261 provider_name = tokens[2];
262 probe_name = tokens[3];
263 } else {
264 ret = CMD_ERROR;
265 goto end;
266 }
267 lookup_method =
268 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
269 if (!lookup_method) {
270 WARN("Failed to create SDT lookup method");
271 ret = CMD_ERROR;
272 goto end;
273 }
274 break;
275 default:
276 ret = CMD_ERROR;
277 goto end;
278 }
279
280 /* strutils_unescape_string allocates a new char *. */
281 unescaped_target_path = strutils_unescape_string(target_path, 0);
282 if (!unescaped_target_path) {
283 ret = CMD_ERROR;
284 goto end;
285 }
286
287 /*
288 * If there is not forward slash in the path. Walk the $PATH else
289 * expand.
290 */
291 if (strchr(unescaped_target_path, '/') == NULL) {
292 /* Walk the $PATH variable to find the targeted binary. */
293 real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
294 if (!real_target_path) {
295 PERROR("Error allocating path buffer");
296 ret = CMD_ERROR;
297 goto end;
298 }
299 ret = walk_command_search_path(unescaped_target_path, real_target_path);
300 if (ret) {
301 ERR("Binary not found.");
302 ret = CMD_ERROR;
303 goto end;
304 }
305 } else {
306 /*
307 * Expand references to `/./` and `/../`. This function does not check
308 * if the file exists. This call returns an allocated buffer on
309 * success.
310 */
311 real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
312 if (!real_target_path) {
313 ERR("Error expanding the path to binary.");
314 ret = CMD_ERROR;
315 goto end;
316 }
317
318 /*
319 * Check if the file exists using access(2), If it does not,
320 * return an error.
321 */
322 ret = access(real_target_path, F_OK);
323 if (ret) {
324 ERR("Cannot find binary at path: %s.", real_target_path);
325 ret = CMD_ERROR;
326 goto end;
327 }
328 }
329
330 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
331 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
332 /*
333 * Check for common mistakes in userspace probe description syntax.
334 */
335 ret = warn_userspace_probe_syntax(symbol_name);
336 if (ret) {
337 goto end;
338 }
339
340 probe_location_local = lttng_userspace_probe_location_function_create(
341 real_target_path, symbol_name, lookup_method);
342 if (!probe_location_local) {
343 WARN("Failed to create function probe location");
344 ret = CMD_ERROR;
345 goto end;
346 }
347
348 /* Ownership transferred to probe_location. */
349 lookup_method = NULL;
350 break;
351 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
352 probe_location_local = lttng_userspace_probe_location_tracepoint_create(
353 real_target_path, provider_name, probe_name, lookup_method);
354 if (!probe_location_local) {
355 WARN("Failed to create function probe location");
356 ret = CMD_ERROR;
357 goto end;
358 }
359
360 /* Ownership transferred to probe_location. */
361 lookup_method = NULL;
362 break;
363 default:
364 ret = CMD_ERROR;
365 goto end;
366 }
367
368 /*
369 * Everything went fine, transfer ownership of probe location to
370 * caller.
371 */
372 *probe_location = probe_location_local;
373 probe_location_local = NULL;
374
375 end:
376 lttng_userspace_probe_location_destroy(probe_location_local);
377 lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
378 strutils_free_null_terminated_array_of_strings(tokens);
379 /*
380 * Freeing both char * here makes the error handling simplier. free()
381 * performs not action if the pointer is NULL.
382 */
383 free(real_target_path);
384 free(unescaped_target_path);
385
386 return ret;
387 }
This page took 0.051454 seconds and 4 git commands to generate.