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 <linux/bpf.h>
7 : :
8 : : #include <elf.h>
9 : : #include <errno.h>
10 : :
11 : : #include <bpfilter/btf.h>
12 : : #include <bpfilter/elfstub.h>
13 : : #include <bpfilter/helper.h>
14 : : #include <bpfilter/logger.h>
15 : :
16 : : #include "cgen/rawstubs.h"
17 : :
18 : : static_assert_enum_mapping(_bf_rawstubs, _BF_ELFSTUB_MAX);
19 : :
20 : : #define _free_bf_printk_str_ __attribute__((cleanup(_bf_printk_str_free)))
21 : :
22 : : static int _bf_printk_str_new(struct bf_printk_str **pstr, size_t insn_idx,
23 : : const char *str)
24 : : {
25 : : assert(pstr);
26 : : assert(str);
27 : :
28 : 648 : *pstr = malloc(sizeof(struct bf_printk_str));
29 : 648 : if (!*pstr)
30 : : return -ENOMEM;
31 : :
32 : 648 : (*pstr)->insn_idx = insn_idx;
33 : 648 : (*pstr)->str = str;
34 : :
35 : : return 0;
36 : : }
37 : :
38 : 648 : static void _bf_printk_str_free(struct bf_printk_str **pstr)
39 : : {
40 [ - - + - ]: 648 : if (!*pstr)
41 : : return;
42 : :
43 : : freep((void *)pstr);
44 : : }
45 : :
46 : 1620 : static int _bf_elfstub_prepare(struct bf_elfstub *stub,
47 : : const struct bf_rawstub *raw)
48 : : {
49 : 1620 : const Elf64_Ehdr *ehdr = raw->elf;
50 : : Elf64_Shdr *shstrtab;
51 : : Elf64_Shdr *shdrs;
52 : : Elf64_Shdr *symtab = NULL;
53 : : Elf64_Shdr *symstrtab = NULL;
54 : : Elf64_Shdr *rodata_shdr = NULL;
55 : : Elf64_Sym *symbols;
56 : : char *sym_strtab;
57 : : size_t sym_count;
58 : : char *strtab;
59 : : int r;
60 : :
61 [ - + ]: 1620 : if (raw->len < sizeof(Elf64_Ehdr))
62 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid ELF header (wrong header size)");
63 : :
64 : : // Check ELF magic
65 [ - + ]: 1620 : if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
66 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid ELF header (wrong magic number)");
67 : :
68 : : // Ensure 64-bit ELF
69 [ - + ]: 1620 : if (ehdr->e_ident[EI_CLASS] != ELFCLASS64)
70 [ # # ]: 0 : return bf_err_r(-ENOTSUP, "only 64-bit ELF is supported");
71 : :
72 : : // Get section header table
73 [ + - - + ]: 1620 : if (ehdr->e_shoff >= raw->len || ehdr->e_shentsize != sizeof(Elf64_Shdr))
74 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid section header table");
75 : :
76 [ - + ]: 1620 : if (ehdr->e_shstrndx >= ehdr->e_shnum)
77 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid string table index");
78 : :
79 : 1620 : shdrs = (Elf64_Shdr *)(raw->elf + ehdr->e_shoff);
80 : 1620 : shstrtab = &shdrs[ehdr->e_shstrndx];
81 [ - + ]: 1620 : if (shstrtab->sh_offset >= raw->len)
82 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid string table offset");
83 : :
84 : 1620 : strtab = (char *)(raw->elf + shstrtab->sh_offset);
85 : :
86 : : // Find .text section
87 [ + - ]: 4860 : for (int i = 0; i < ehdr->e_shnum; i++) {
88 [ + + ]: 4860 : if (!bf_streq(&strtab[shdrs[i].sh_name], ".text"))
89 : : continue;
90 : :
91 [ - + ]: 1620 : if (shdrs[i].sh_offset + shdrs[i].sh_size > raw->len)
92 [ # # ]: 0 : return bf_err_r(-EINVAL, "invalid .text section");
93 : :
94 : 1620 : stub->insns = malloc(shdrs[i].sh_size);
95 [ + - ]: 1620 : if (!stub->insns)
96 : : return -ENOMEM;
97 : :
98 : 1620 : memcpy(stub->insns, raw->elf + shdrs[i].sh_offset, shdrs[i].sh_size);
99 : 1620 : stub->ninsns = shdrs[i].sh_size / sizeof(struct bpf_insn);
100 : 1620 : break;
101 : : }
102 : :
103 [ - + ]: 1620 : if (!stub->insns)
104 [ # # ]: 0 : return bf_err_r(-ENOENT, ".text section not found");
105 : :
106 : : // Find symbol table and its string table
107 [ + + ]: 40500 : for (int i = 0; i < ehdr->e_shnum; i++) {
108 [ + + ]: 38880 : if (shdrs[i].sh_type == SHT_SYMTAB) {
109 : : symtab = &shdrs[i];
110 [ + - ]: 1620 : if (shdrs[i].sh_link < ehdr->e_shnum)
111 : 1620 : symstrtab = &shdrs[shdrs[i].sh_link];
112 [ + + ]: 37260 : } else if (bf_streq(&strtab[shdrs[i].sh_name], ".rodata")) {
113 : : rodata_shdr = &shdrs[i];
114 : : }
115 : : }
116 : :
117 [ - + ]: 1620 : if (!symtab || !symstrtab)
118 [ # # ]: 0 : return bf_err_r(-ENOENT, "symbol table not found");
119 : :
120 : 1620 : symbols = (Elf64_Sym *)(raw->elf + symtab->sh_offset);
121 : 1620 : sym_strtab = (char *)(raw->elf + symstrtab->sh_offset);
122 : 1620 : sym_count = symtab->sh_size / sizeof(Elf64_Sym);
123 : :
124 : : // Process REL relocation sections
125 [ + + ]: 40500 : for (int i = 0; i < ehdr->e_shnum; i++) {
126 [ + + ]: 38880 : if (shdrs[i].sh_type != SHT_REL)
127 : 27216 : continue;
128 : :
129 : : // Check if this relocation section applies to .text
130 [ + - ]: 11664 : if (shdrs[i].sh_info != 0) {
131 : : // sh_info contains the section index this relocation applies to
132 [ + + ]: 11664 : if (!bf_streq(&strtab[shdrs[shdrs[i].sh_info].sh_name], ".text"))
133 : 10368 : continue;
134 : : }
135 : :
136 : : // REL relocations (no addend)
137 : 1296 : Elf64_Rel *rels = (Elf64_Rel *)(raw->elf + shdrs[i].sh_offset);
138 : 1296 : size_t rel_count = shdrs[i].sh_size / sizeof(Elf64_Rel);
139 : :
140 [ + + ]: 2592 : for (size_t j = 0; j < rel_count; j++) {
141 : 1296 : uint32_t type = ELF64_R_TYPE(rels[j].r_info);
142 : 1296 : uint32_t sym_idx = ELF64_R_SYM(rels[j].r_info);
143 : :
144 [ + + + - ]: 1296 : if (type == R_BPF_64_32 && sym_idx < sym_count) {
145 : 648 : uint32_t name_idx = symbols[sym_idx].st_name;
146 [ + - ]: 648 : if (name_idx < symstrtab->sh_size) {
147 : 648 : const char *name = &sym_strtab[name_idx];
148 : 648 : int id = bf_btf_get_id(name);
149 [ - + ]: 648 : if (id < 0)
150 [ # # ]: 0 : return bf_err_r(id, "function %s not found", name);
151 : :
152 : 648 : size_t idx = rels[j].r_offset / 8;
153 : 648 : stub->insns[idx] =
154 : : ((struct bpf_insn) {.code = BPF_JMP | BPF_CALL,
155 : : .dst_reg = 0,
156 : : .src_reg = BPF_PSEUDO_KFUNC_CALL,
157 : : .off = 0,
158 : : .imm = id});
159 : :
160 [ - + ]: 648 : bf_dbg("updated stub to call '%s' from instruction %lu",
161 : : name, idx);
162 : : }
163 [ + - ]: 648 : } else if (type == R_BPF_64_64 && rodata_shdr) {
164 : : _free_bf_printk_str_ struct bf_printk_str *pstr = NULL;
165 : 648 : size_t insn_idx = rels[j].r_offset / 8;
166 : 648 : size_t str_offset = stub->insns[insn_idx].imm;
167 : 648 : const char *str =
168 [ - + ]: 648 : raw->elf + rodata_shdr->sh_offset + str_offset;
169 : :
170 : : r = _bf_printk_str_new(&pstr, insn_idx, str);
171 : : if (r)
172 [ # # ]: 0 : return bf_err_r(r, "failed to create printk_str");
173 : :
174 : 648 : r = bf_list_add_tail(&stub->strs, pstr);
175 [ - + ]: 648 : if (r)
176 [ # # ]: 0 : return bf_err_r(r, "failed to add printk_str to elfstub");
177 : :
178 : : TAKE_PTR(pstr);
179 : : }
180 : : }
181 : : }
182 : :
183 : : return 0;
184 : : }
185 : :
186 : 1620 : int bf_elfstub_new(struct bf_elfstub **stub, enum bf_elfstub_id id)
187 : : {
188 : 1620 : _free_bf_elfstub_ struct bf_elfstub *_stub = NULL;
189 : : int r;
190 : :
191 : : assert(stub);
192 : :
193 : 1620 : _stub = calloc(1, sizeof(*_stub));
194 [ + - ]: 1620 : if (!_stub)
195 : : return -ENOMEM;
196 : :
197 : 1620 : _stub->strs = bf_list_default(_bf_printk_str_free, NULL);
198 : :
199 : 1620 : r = _bf_elfstub_prepare(_stub, &_bf_rawstubs[id]);
200 [ + - ]: 1620 : if (r)
201 : : return r;
202 : :
203 : 1620 : *stub = TAKE_PTR(_stub);
204 : :
205 : 1620 : return 0;
206 : : }
207 : :
208 : 3240 : void bf_elfstub_free(struct bf_elfstub **stub)
209 : : {
210 : : assert(stub);
211 : :
212 [ + + ]: 3240 : if (!*stub)
213 : : return;
214 : :
215 : 1620 : bf_list_clean(&(*stub)->strs);
216 : 1620 : freep((void *)&(*stub)->insns);
217 : : freep((void *)stub);
218 : : }
|