|
@@ -136,6 +136,77 @@ void __bpf_prog_free(struct bpf_prog *fp)
|
|
|
vfree(fp);
|
|
|
}
|
|
|
|
|
|
+static bool bpf_is_jmp_and_has_target(const struct bpf_insn *insn)
|
|
|
+{
|
|
|
+ return BPF_CLASS(insn->code) == BPF_JMP &&
|
|
|
+ /* Call and Exit are both special jumps with no
|
|
|
+ * target inside the BPF instruction image.
|
|
|
+ */
|
|
|
+ BPF_OP(insn->code) != BPF_CALL &&
|
|
|
+ BPF_OP(insn->code) != BPF_EXIT;
|
|
|
+}
|
|
|
+
|
|
|
+static void bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta)
|
|
|
+{
|
|
|
+ struct bpf_insn *insn = prog->insnsi;
|
|
|
+ u32 i, insn_cnt = prog->len;
|
|
|
+
|
|
|
+ for (i = 0; i < insn_cnt; i++, insn++) {
|
|
|
+ if (!bpf_is_jmp_and_has_target(insn))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Adjust offset of jmps if we cross boundaries. */
|
|
|
+ if (i < pos && i + insn->off + 1 > pos)
|
|
|
+ insn->off += delta;
|
|
|
+ else if (i > pos + delta && i + insn->off + 1 <= pos + delta)
|
|
|
+ insn->off -= delta;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
|
|
|
+ const struct bpf_insn *patch, u32 len)
|
|
|
+{
|
|
|
+ u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
|
|
|
+ struct bpf_prog *prog_adj;
|
|
|
+
|
|
|
+ /* Since our patchlet doesn't expand the image, we're done. */
|
|
|
+ if (insn_delta == 0) {
|
|
|
+ memcpy(prog->insnsi + off, patch, sizeof(*patch));
|
|
|
+ return prog;
|
|
|
+ }
|
|
|
+
|
|
|
+ insn_adj_cnt = prog->len + insn_delta;
|
|
|
+
|
|
|
+ /* Several new instructions need to be inserted. Make room
|
|
|
+ * for them. Likely, there's no need for a new allocation as
|
|
|
+ * last page could have large enough tailroom.
|
|
|
+ */
|
|
|
+ prog_adj = bpf_prog_realloc(prog, bpf_prog_size(insn_adj_cnt),
|
|
|
+ GFP_USER);
|
|
|
+ if (!prog_adj)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ prog_adj->len = insn_adj_cnt;
|
|
|
+
|
|
|
+ /* Patching happens in 3 steps:
|
|
|
+ *
|
|
|
+ * 1) Move over tail of insnsi from next instruction onwards,
|
|
|
+ * so we can patch the single target insn with one or more
|
|
|
+ * new ones (patching is always from 1 to n insns, n > 0).
|
|
|
+ * 2) Inject new instructions at the target location.
|
|
|
+ * 3) Adjust branch offsets if necessary.
|
|
|
+ */
|
|
|
+ insn_rest = insn_adj_cnt - off - len;
|
|
|
+
|
|
|
+ memmove(prog_adj->insnsi + off + len, prog_adj->insnsi + off + 1,
|
|
|
+ sizeof(*patch) * insn_rest);
|
|
|
+ memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
|
|
|
+
|
|
|
+ bpf_adj_branches(prog_adj, off, insn_delta);
|
|
|
+
|
|
|
+ return prog_adj;
|
|
|
+}
|
|
|
+
|
|
|
#ifdef CONFIG_BPF_JIT
|
|
|
struct bpf_binary_header *
|
|
|
bpf_jit_binary_alloc(unsigned int proglen, u8 **image_ptr,
|