Branch data Line data Source code
1 : : /* SPDX-License-Identifier: GPL-2.0-only */
2 : : /*
3 : : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "cgen/swich.h"
7 : :
8 : : #include <linux/bpf.h>
9 : : #include <linux/bpf_common.h>
10 : :
11 : : #include <errno.h>
12 : : #include <stddef.h>
13 : : #include <stdint.h>
14 : : #include <stdlib.h>
15 : : #include <string.h>
16 : :
17 : : #include <bpfilter/helper.h>
18 : : #include <bpfilter/list.h>
19 : : #include <bpfilter/logger.h>
20 : :
21 : : #include "cgen/jmp.h"
22 : : #include "cgen/program.h"
23 : : #include "filter.h"
24 : :
25 : : /// Cleanup attribute for a @ref bf_swich_option variable.
26 : : #define _free_bf_swich_option_ __attribute__((cleanup(_bf_swich_option_free)))
27 : :
28 : : /**
29 : : * @struct bf_swich_option
30 : : *
31 : : * Represent a @c case of the switch: contains the instructions to execute if
32 : : * the switch's register is equal to a specific value.
33 : : */
34 : : struct bf_swich_option
35 : : {
36 : : /// Immediate value to match against the switch's register.
37 : : uint32_t imm;
38 : : /// Jump context used to jump from the comparison to the bytecode to
39 : : /// execute, then to the end of the switch.
40 : : struct bf_jmpctx jmp;
41 : : /// Number of instructions for this option.
42 : : size_t insns_len;
43 : : /// Instructions to execute, as a flexible array member.
44 : : struct bpf_insn insns[];
45 : : };
46 : :
47 : : static void _bf_swich_option_free(struct bf_swich_option **option);
48 : :
49 : : /**
50 : : * Allocate and initialize a new switch option (case).
51 : : *
52 : : * @param option Switch option to allocate and initialize. Can't be NULL.
53 : : * @param imm Immediate value to match against the switch's register.
54 : : * @param insns Instructions to execute if the option matches.
55 : : * @param insns_len Number of instructions in @p insns .
56 : : * @return 0 on success, or negative errno value on failure.
57 : : */
58 : 1095 : static int _bf_swich_option_new(struct bf_swich_option **option, uint32_t imm,
59 : : const struct bpf_insn *insns, size_t insns_len)
60 : : {
61 : : _free_bf_swich_option_ struct bf_swich_option *_option = NULL;
62 : :
63 : : bf_assert(option);
64 : : bf_assert(insns);
65 : :
66 : 1095 : _option = malloc(sizeof(*_option) + (sizeof(struct bpf_insn) * insns_len));
67 [ + - ]: 1095 : if (!_option)
68 : : return -ENOMEM;
69 : :
70 : 1095 : _option->imm = imm;
71 : 1095 : _option->insns_len = insns_len;
72 : 1095 : memcpy(_option->insns, insns, sizeof(struct bpf_insn) * insns_len);
73 : :
74 : 1095 : *option = TAKE_PTR(_option);
75 : :
76 : : return 0;
77 : : }
78 : :
79 : : /**
80 : : * Free a switch option.
81 : : *
82 : : * Free @p option is it points to valid memory. If @p option points to a NULL
83 : : * pointer, nothing is done.
84 : : *
85 : : * @param option Option to free. Can't be NULL.
86 : : */
87 : 770 : static void _bf_swich_option_free(struct bf_swich_option **option)
88 : : {
89 : : bf_assert(option);
90 : :
91 [ - - - + : 2190 : if (!*option)
- + + - +
- ]
92 : : return;
93 : :
94 : 0 : free(*option);
95 : 1095 : *option = NULL;
96 : : }
97 : :
98 : 325 : int bf_swich_init(struct bf_swich *swich, struct bf_program *program, int reg)
99 : : {
100 : : bf_assert(swich);
101 : : bf_assert(program);
102 : :
103 : 325 : swich->program = program;
104 : 325 : swich->reg = reg;
105 : :
106 : 325 : bf_list_init(
107 : : &swich->options,
108 : 325 : (bf_list_ops[]) {{.free = (bf_list_ops_free)_bf_swich_option_free}});
109 : :
110 : 325 : swich->default_opt = NULL;
111 : :
112 : 325 : return 0;
113 : : }
114 : :
115 : 325 : void bf_swich_cleanup(struct bf_swich *swich)
116 : : {
117 : : bf_assert(swich);
118 : :
119 : 325 : bf_list_clean(&swich->options);
120 : : _bf_swich_option_free(&swich->default_opt);
121 : 325 : free(swich->default_opt);
122 : 325 : }
123 : :
124 : 770 : int bf_swich_add_option(struct bf_swich *swich, uint32_t imm,
125 : : const struct bpf_insn *insns, size_t insns_len)
126 : : {
127 : 770 : _free_bf_swich_option_ struct bf_swich_option *option = NULL;
128 : : int r;
129 : :
130 : : bf_assert(swich);
131 : : bf_assert(insns);
132 : :
133 : 770 : r = _bf_swich_option_new(&option, imm, insns, insns_len);
134 [ + - ]: 770 : if (r)
135 : : return r;
136 : :
137 : 770 : r = bf_list_add_tail(&swich->options, option);
138 [ + - ]: 770 : if (r)
139 : : return r;
140 : :
141 : 770 : TAKE_PTR(option);
142 : :
143 : 770 : return 0;
144 : : }
145 : :
146 : 325 : int bf_swich_set_default(struct bf_swich *swich, const struct bpf_insn *insns,
147 : : size_t insns_len)
148 : : {
149 : 325 : _free_bf_swich_option_ struct bf_swich_option *option = NULL;
150 : : int r;
151 : :
152 : : bf_assert(swich);
153 : : bf_assert(insns);
154 : :
155 [ - + ]: 325 : if (swich->default_opt) {
156 [ # # ]: 0 : bf_warn("default bf_swich option already exists, replacing it");
157 : : _bf_swich_option_free(&swich->default_opt);
158 : : }
159 : :
160 : 325 : r = _bf_swich_option_new(&option, 0, insns, insns_len);
161 [ + - ]: 325 : if (r)
162 : : return r;
163 : :
164 : 325 : swich->default_opt = TAKE_PTR(option);
165 : :
166 : 325 : return 0;
167 : : }
168 : :
169 : 325 : int bf_swich_generate(struct bf_swich *swich)
170 : : {
171 : 325 : struct bf_program *program = swich->program;
172 : :
173 : : // Match an option against the value and jump to the related bytecode
174 [ + - + + ]: 1865 : bf_list_foreach (&swich->options, option_node) {
175 : : struct bf_swich_option *option = bf_list_node_get_data(option_node);
176 : :
177 [ - + + + ]: 770 : option->jmp = bf_jmpctx_get(
178 : : program, BPF_JMP_IMM(BPF_JEQ, swich->reg, option->imm, 0));
179 : : }
180 : :
181 : : // Insert the default option if any
182 [ + - ]: 325 : if (swich->default_opt)
183 [ - + ]: 325 : swich->default_opt->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
184 : :
185 : : // Insert each option's bytecode
186 [ + - + + ]: 1865 : bf_list_foreach (&swich->options, option_node) {
187 : : struct bf_swich_option *option = bf_list_node_get_data(option_node);
188 : :
189 : 770 : bf_jmpctx_cleanup(&option->jmp);
190 [ + + ]: 1540 : for (size_t i = 0; i < option->insns_len; ++i)
191 [ + - ]: 770 : EMIT(program, option->insns[i]);
192 [ - + + + ]: 770 : option->jmp = bf_jmpctx_get(program, BPF_JMP_A(0));
193 : : }
194 : :
195 : : // Insert the default instructions
196 [ + - ]: 325 : if (swich->default_opt) {
197 : 325 : bf_jmpctx_cleanup(&swich->default_opt->jmp);
198 [ + + ]: 650 : for (size_t i = 0; i < swich->default_opt->insns_len; ++i)
199 [ + - ]: 325 : EMIT(program, swich->default_opt->insns[i]);
200 : : }
201 : :
202 [ + - + + : 2190 : bf_list_foreach (&swich->options, option_node) {
+ + ]
203 : : struct bf_swich_option *option = bf_list_node_get_data(option_node);
204 : :
205 : 770 : bf_jmpctx_cleanup(&option->jmp);
206 : : }
207 : :
208 : : return 0;
209 : : }
|