• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#   Copyright (c) 2022 Huawei Device Co., Ltd.
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
16import sys
17
18MIN_DECL_LIST_LEN = 2
19
20
21def handle_macro(decl_str):
22    '''
23    # Function:
24    #   The declaration string may contain macro,
25    #   This function expands it. it's defined as follow:
26    #       #ifdef CONFIG_CPU_BIG_ENDIAN
27    #       #define arg_u32p(name)	u32, name##_hi, u32, name##_lo
28    #       #else
29    #       #define arg_u32p(name)	u32, name##_lo, u32, name##_hi
30    #       #endif
31    # find the macro
32    '''
33    start = decl_str.find('arg_u32p')
34    if start == -1:
35        return decl_str
36    macro_str = decl_str[start:]
37    end = macro_str.find(')')
38    end += 1
39    macro_str = macro_str[:end]
40    macro_str_len = len(macro_str)
41    macro_list = macro_str.split('(')
42    var_name = macro_list[1]
43    var_name = var_name[:-1]
44    var_name = var_name.strip()
45    expansion = "u64, {}".format(var_name)
46    left = decl_str[:start]
47    right = decl_str[start + macro_str_len:]
48    decl_str = "{}{}{}".format(left, expansion, right)
49    return decl_str
50
51
52def get_decl_str(inf):
53    '''
54    # Function:
55    #   Get the declaration string of system call
56    # Arguments:
57    #   @inf: input file
58    # Return:
59    #   Declaration string of system call, e.g.
60    #   do_sys_openat2(open, int, dfd, const char __user*, filename, struct open_how *, how)
61    '''
62    decl_str = inf.readline()
63    if decl_str == "":
64        print("empty line read from input file")
65        return ""
66    decl_str = decl_str.strip()
67    if decl_str[0] == '"':
68        while True:
69            line = inf.readline()
70            line = line.strip()
71            decl_str = "{}{}".format(decl_str, line)
72            if (decl_str[-1] == '"'):
73                break
74        decl_str = decl_str[1:-1]
75    return handle_macro(decl_str)
76
77
78def get_decl_list(decl_str):
79    '''
80    # Function:
81    #   Split declaration string into list
82    # Arguments:
83    #   @decl_str: system call declaration string, e.g.
84    #       do_sys_openat2(open, int, dfd, const char __user*, filename, struct open_how *, how)
85    # Return:
86    #   Declaration list, e.g.
87    #   ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
88    '''
89    decl_list = decl_str.split('(')
90    if len(decl_list) < 2:
91        print("failed to split %s with '('" % decl_str)
92        return []
93    probe_entry = decl_list[0]
94    probe_entry = probe_entry.strip()
95    args_str = decl_list[1]
96    args_str = args_str.strip()
97    # drop the tailing ')'
98    args_str = args_str[:-1]
99    args_list = args_str.split(',')
100    args_list = [args_item.strip() for args_item in args_list]
101    decl_list = []
102    decl_list.append(probe_entry)
103    decl_list = decl_list + args_list
104    return decl_list
105
106
107def get_args_list(decl_list):
108    '''
109    # Function:
110    #   Truncate the prefix and system call entry name from the declaration list
111    # Arguments:
112    #   @decl_list: Declaration list, e.g.
113    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
114    # Return:
115    #   Arguments list of system call, e.g.
116    #       ['open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
117    '''
118    if len(decl_list) < MIN_DECL_LIST_LEN:
119        print("failed to get arguments list from %s" % decl_list)
120        return []
121    return decl_list[MIN_DECL_LIST_LEN - 1:]
122
123
124def gen_kprobe_sec(decl_list):
125    '''
126    # Function:
127    #   Generate the kprobe prog type declaration, e.g. SEC("kprobe/__arm64_sys_open")
128    # Arguments:
129    #   @decl_list: Declaration list, e.g.
130    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
131    # Return:
132    #   kprobe prog type declaration, e.g. SEC("kprobe/do_sys_openat2")
133    '''
134    if len(decl_list) < MIN_DECL_LIST_LEN:
135        print("failed to generate kprobe section from %s" % decl_list)
136        return ""
137    probe_str = '\nSEC("kprobe/'
138    syscall_fn_index = 0
139    syscall = decl_list[syscall_fn_index]
140    probe_str = '{}{}")\n'.format(probe_str, syscall)
141    return probe_str
142
143
144def gen_kretprobe_sec(decl_list):
145    '''
146    # Function:
147    #   Generate the kretprobe prog type declaration, e.g. SEC("kretprobe/__arm64_sys_open")
148    # Arguments:
149    #   @decl_list: Declaration list, e.g.
150    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
151    # Return:
152    #   kretprobe prog type declaration, e.g. SEC("kretprobe/do_sys_openat2")
153    '''
154    if len(decl_list) < MIN_DECL_LIST_LEN:
155        print("failed to generate kretprobe section from %s" % decl_list)
156        return ""
157    probe_str = '\nSEC("kretprobe/'
158    syscall_fn_index = 0
159    syscall = decl_list[syscall_fn_index]
160    probe_str = '{}{}")\n'.format(probe_str, syscall)
161    return probe_str
162
163
164def gen_kprobe_decl(decl_list):
165    '''
166    # Function:
167    #   Generate the kprobe prog declaration, e.g.
168    #   int BPF_KPROBE(do_sys_openat2_entry, int dfd, const char* filename, struct open_how* how)
169    # Arguments:
170    #   @decl_list: Declaration list, e.g.
171    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
172    # Return:
173    #   kprobe prog declaration, e.g.
174    #   int BPF_KPROBE(do_sys_openat2_entry, int dfd, const char __user* filename, struct open_how *how)
175    '''
176    if len(decl_list) < MIN_DECL_LIST_LEN:
177        print("failed to generate kprobe prog declaration from %s" % decl_list)
178        return ""
179    prog_decl = "int BPF_KPROBE("
180    syscall_fn_index = 0
181    prog_name = "{}_entry".format(decl_list[syscall_fn_index])
182    prog_decl = "{}{}".format(prog_decl, prog_name)
183    index = MIN_DECL_LIST_LEN + 1
184    count = 0
185    max_nr_args = 5
186    while index < len(decl_list):
187        if count >= max_nr_args:
188            break
189        prog_decl = '%s, %s %s' % (prog_decl, decl_list[index - 1], decl_list[index])
190        index += 2
191        count += 1
192    prog_decl = "{})".format(prog_decl)
193    return prog_decl
194
195
196def gen_kretprobe_decl(decl_list):
197    '''
198    # Function:
199    #   Generate the kretprobe prog declaration, e.g.
200    #   int BPF_KRETPROBE(__arm64_sys_open_exit, long long ret)
201    # Arguments:
202    #   @decl_list: Declaration list, e.g.
203    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
204    # Return:
205    #   kretprobe prog declaration, e.g.
206    #   int BPF_KRETPROBE(do_sys_openat2_exit, long long ret)
207    '''
208    if len(decl_list) < MIN_DECL_LIST_LEN:
209        print("failed to generate kretprobe prog declaration from %s" % decl_list)
210        return ""
211    prog_decl = "int BPF_KRETPROBE("
212    syscall_fn_index = 0
213    prog_name = "{}_exit".format(decl_list[syscall_fn_index])
214    prog_decl = "{}{}, int64_t retval)".format(prog_decl, prog_name)
215    return prog_decl
216
217HEAD_BPF_PROG_CODE = (
218'/************************** fstrace BPF progs BEGIN *****************************/')
219
220TAIL_BPF_PROG_CODE = (
221'/*************************** fstrace BPF progs END ******************************/\n')
222
223HEAD_COMMON_KPROBE_CODE = R'''
224{
225    if (check_current_pid(-1, -1) != 0) {
226        // not any one of target processes, skip it
227        return 0;
228    }
229    struct start_event_t start_event = {};
230    __builtin_memset(&start_event, 0, sizeof(start_event));
231    struct fstrace_start_event_t *fs_se = &start_event.fs_se;
232    u64 pid_tgid = bpf_get_current_pid_tgid();
233    // get timestamp of the start of system call
234    fs_se->stime = bpf_ktime_get_ns();
235    // get argument of the system call
236'''
237
238TAIL_COMMON_KPROBE_CODE = R'''
239    // store the start event with pid as key
240    int err = (int) bpf_map_update_elem(&start_event_map, &pid_tgid, &start_event, BPF_ANY);
241    if (err != 0) {
242        BPFLOGE(BPF_TRUE, "failed to store fstrace start event");
243        return -1;
244    }
245    return 0;
246}
247'''
248
249HEAD_COMMON_KRETPROBE_CODE = R'''
250{
251    if (check_current_pid(-1, -1) != 0) {
252        // not any one of target processes, skip it
253        return 0;
254    }
255'''
256
257TAIL_COMMON_KRETPROBE_CODE = '''
258}
259'''
260
261HIEBPF_TYPES_HEAD = R'''
262#ifndef FSTRACE_TYPES_H
263#define FSTRACE_TYPES_H
264
265#include "hiebpf_macros.h"
266
267'''
268
269HIEBPF_TYPES_TAIL = '#endif'
270
271
272def get_args_type(decl_list):
273    '''
274    # Function:
275    #   Get the arguments struct type, e.g. struct sys_pidfd_open_args_t
276    # Arguments:
277    #   @decl_list: Declaration list, e.g.
278    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
279    # Return:
280    #   arguments struct type, e.g. struct sys_open_args_t
281    '''
282    if len(decl_list) < MIN_DECL_LIST_LEN:
283        print("failed to get arguments type from %s" % decl_list)
284        return ""
285    args_type_index = 1
286    fn_name = decl_list[args_type_index]
287    args_type_str = "struct sys_{}_args_t".format(fn_name)
288    return args_type_str
289
290
291def get_arg_variable_name(decl_list):
292    '''
293    # Function
294    #   Get the arguments struct type instance name, e.g. pidfd_open_args
295    # Arguments:
296    #   @decl_list: Declaration list, e.g.
297    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
298    # Return:
299    #   the arguments struct type instance name, e.g. open_args
300    '''
301    if len(decl_list) < MIN_DECL_LIST_LEN:
302        print("failed to get arguments variable name from %s" % decl_list)
303        return ""
304    args_type_index = 1
305    return "{}_args".format(decl_list[args_type_index])
306
307
308def get_arg_member_index(decl_list):
309    '''
310    # Function:
311    #   Get the member name list of arguments type
312    # Arguments:
313    #    @decl_list: Declaration list, e.g.
314    #       ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
315    # Return:
316    #   member name list of arguments type, e.g. ['pid', 'flags']
317    '''
318    member_index_list = []
319    if len(decl_list) < MIN_DECL_LIST_LEN:
320        print("failed to get arguments member index list from %s" % decl_list)
321        return member_index_list
322    index = MIN_DECL_LIST_LEN + 1
323    count = 0
324    max_nr_args = 5
325    while index < len(decl_list):
326        if count >= max_nr_args:
327            break
328        member_index_list.append(index)
329        index += 2
330        count += 1
331    return member_index_list
332
333
334def gen_kprobe_code(decl_list):
335    '''
336    # Function:
337    #   Generate system call specific code of BPF kprobe prog
338    # Arguments:
339    #   @decl_list: Declaration list, e.g.
340    #      ['do_sys_openat2', 'open', 'int', 'dfd', 'const char __user*', 'filename', 'struct open_how *', 'how']
341    # Return:
342    # system call specific code string of BPF kprobe prog
343    '''
344    indent = "    "
345    args_type = get_args_type(decl_list)
346    if args_type == "":
347        return ""
348    args_var_name = get_arg_variable_name(decl_list)
349    if args_var_name == "":
350        return ""
351    args_type_index = 1
352    event_type = "SYS_{}".format(decl_list[args_type_index].upper())
353    code_str = "    fs_se->type = {};\n".format(event_type)
354    var_decl_str = "{}{}* args = &fs_se->{};\n".format(indent, args_type, args_var_name)
355    member_index_list = get_arg_member_index(decl_list)
356    assignment_code_str = ""
357    count = 0
358    max_nr_args = 5
359    for index in member_index_list:
360        if count >= max_nr_args:
361            break
362        member_name = decl_list[index]
363        assignment_str = "{}args->{} = {};\n".format(indent, member_name, member_name)
364        if member_name == "filename":
365            assignment_str = "{}{}emit_strtrace_event(fs_se->stime, fs_se->type, filename, FSTRACE);\n".format(
366                assignment_str, indent)
367        assignment_code_str = "{}{}".format(assignment_code_str, assignment_str)
368        count += 1
369    code_str = "{}{}{}".format(code_str, var_decl_str, assignment_code_str)
370    return code_str
371
372
373def gen_kretprobe_code(decl_list):
374    '''
375    # Function:
376    #   Generate system call specific code of BPF kretprobe prog
377    # Arguments:
378    #   @decl_list: Declaration list, e.g.
379    #      ['yes', '__arm64_sys_pidfd_open', 'pidfd_open', 'pid_t', 'pid', 'unsigned int', 'flags']
380    # Return:
381    # system call specific code string of BPF kretprobe prog
382    '''
383    if len(decl_list) < MIN_DECL_LIST_LEN:
384        print("failed to get system call type from %s" % decl_list)
385        return ""
386    fn_name = decl_list[2]
387    tracer = "FSTRACE"
388    return "    return emit_event(ctx, retval, {});".format(tracer)
389
390
391def underscore_int_types(arg_type):
392    # underscore u8
393    arg_type = arg_type.strip()
394    arg_type_list = arg_type.split(' ')
395    for index , arg_item in enumerate(arg_type_list):
396        if arg_item == "u8":
397            arg_type_list[index] = "__u8"
398            break
399        if arg_item == "u16":
400            arg_type_list[index] = "__u16"
401            break
402        if arg_item == "u32":
403            arg_type_list[index] = "__u32"
404            break
405        if arg_item == "u64":
406            arg_type_list[index] = "__u64"
407            break
408        if arg_item == "s8":
409            arg_type_list[index] = "__s8"
410            break
411        if arg_item == "s16":
412            arg_type_list[index] = "__s16"
413            break
414        if arg_item == "s32":
415            arg_type_list[index] = "__s32"
416            break
417        if arg_item == "s64":
418            arg_type_list[index] = "__s64"
419            break
420    arg_type = arg_type_list[0]
421    for index, arg_item in enumerate(arg_type_list):
422        if index == 0:
423            continue
424        arg_type = "%s %s" % (arg_type, arg_type_list[index])
425    return arg_type
426
427
428def gen_struct_str(args_list):
429    '''
430    # Function:
431    #   Generate arguments type struct, e.g.
432    #   struct sys_open_args_t {
433    #       const char __user* filename;
434    #       int flags;
435    #       umode_t mode;
436    #   }
437    # Arguments:
438    #   @args_list: arguments list of system call, e.g.
439    #       ['do_sys_openat2', 'int', 'dfd', 'unsigned int', 'flags']
440    # Return:
441    #   arguments type struct, e.g.
442    #   struct sys_open_args_t {
443    #       const char __user* filename;
444    #       int flags;
445    #       umode_t mode;
446    #   }
447    '''
448    length = len(args_list)
449    if length < 1:
450        return ""
451    result = "struct "
452    index = 0
453    while index < length:
454        if index == 0:
455            fn_name = args_list[index]
456            fn_name = "sys_%s_args_t {\n" % (fn_name)
457            result = "{}{}".format(result, fn_name)
458            index = index + 1
459        else:
460            arg_type = underscore_int_types(args_list[index])
461            index = index + 1
462            if index >= length:
463                return ""
464            arg_name = args_list[index]
465            index = index + 1
466            result = "{}    {} {};\n".format(result, arg_type, arg_name)
467    result = "%s};\n" % (result)
468    return result
469
470
471def output_fstrace_code(fstrace_progs_file, fstrace_types_file, fstrace_targets_file):
472    with open(fstrace_progs_file, 'a') as progs_output_file:
473        progs_output_file.write(HEAD_BPF_PROG_CODE)
474        with open(fstrace_types_file, 'a') as args_output_file:
475            args_output_file.write(HIEBPF_TYPES_HEAD)
476            with open(fstrace_targets_file, 'r') as inf:
477                type_defs_set = set()
478                fn_names_set = set()
479                start_event_def = (
480                    "struct fstrace_start_event_t {\n"
481                    "    __u32 type;\n"
482                    "    __u64 stime;\n"
483                    "    union {\n")
484                start_event_def_end = "    };\n};\n\n"
485                arg_types_enum_def = "enum FSTraceEventType:__u32 {\n"
486                arg_types_enum_def_end = "};\n"
487                count = 1
488                while True:
489                    # get declaration list, arguments list and arguments definition string
490                    decl_str = get_decl_str(inf)
491                    if decl_str == "":
492                        break
493                    decl_list = get_decl_list(decl_str)
494                    args_list = get_args_list(decl_list)
495                    type_def_str = gen_struct_str(args_list)
496                    fn_name = args_list[0]
497                    # save arguments type definitions
498                    if type_def_str not in type_defs_set:
499                        type_defs_set.add(type_def_str)
500                        type_def_lines = type_def_str.split('\n')
501                        max_type_def_lines = 7
502                        type_def_lines_reduced = type_def_lines
503                        if len(type_def_lines) > max_type_def_lines:
504                            type_def_lines_reduced = type_def_lines[:(max_type_def_lines - 1)]
505                            type_def_lines_reduced.append("};\n")
506                        type_def_str_reduced = ""
507                        for type_def_line in type_def_lines_reduced:
508                            type_def_str_reduced = "{}{}\n".format(type_def_str_reduced, type_def_line)
509                        args_output_file.write(type_def_str_reduced)
510                        start_event_def  = "{}        struct sys_{}_args_t {}_args;\n".format(start_event_def,
511                                                                                              fn_name, fn_name)
512                        if count == 1:
513                            arg_type = "SYS_{} = 1".format(fn_name.upper())
514                            count = 0
515                        else:
516                            arg_type = "SYS_{}".format(fn_name.upper())
517                        arg_types_enum_def = "{}    {},\n".format(arg_types_enum_def, arg_type)
518
519                    # generate kprobe prog
520                    kprobe_sec = gen_kprobe_sec(decl_list)
521                    krpobe_decl = gen_kprobe_decl(decl_list)
522                    kprobe_code = gen_kprobe_code(decl_list)
523
524                    # save kprobe prog
525                    progs_output_file.write(kprobe_sec)
526                    progs_output_file.write(krpobe_decl)
527                    progs_output_file.write(HEAD_COMMON_KPROBE_CODE)
528                    progs_output_file.write(kprobe_code)
529                    progs_output_file.write(TAIL_COMMON_KPROBE_CODE)
530
531                    # generate kretprobe prog
532                    kretprobe_sec = gen_kretprobe_sec(decl_list)
533                    kretprobe_decl = gen_kretprobe_decl(decl_list)
534                    kretprobe_code = gen_kretprobe_code(decl_list)
535
536                    # save kretprobe prog
537                    progs_output_file.write(kretprobe_sec)
538                    progs_output_file.write(kretprobe_decl)
539                    progs_output_file.write(HEAD_COMMON_KRETPROBE_CODE)
540                    progs_output_file.write(kretprobe_code)
541                    progs_output_file.write(TAIL_COMMON_KRETPROBE_CODE)
542            start_event_def = "{}{}".format(start_event_def, start_event_def_end)
543            arg_types_enum_def = "{}{}".format(arg_types_enum_def, arg_types_enum_def_end)
544            args_output_file.write(start_event_def)
545            args_output_file.write(arg_types_enum_def)
546            args_output_file.write(HIEBPF_TYPES_TAIL)
547        progs_output_file.write(TAIL_BPF_PROG_CODE)
548        progs_output_file.write("\n\n")
549    return
550