Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7bd509e3 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller
Browse files

bpf: add prog_digest and expose it via fdinfo/netlink



When loading a BPF program via bpf(2), calculate the digest over
the program's instruction stream and store it in struct bpf_prog's
digest member. This is done at a point in time before any instructions
are rewritten by the verifier. Any unstable map file descriptor
number part of the imm field will be zeroed for the hash.

fdinfo example output for progs:

  # cat /proc/1590/fdinfo/5
  pos:          0
  flags:        02000002
  mnt_id:       11
  prog_type:    1
  prog_jited:   1
  prog_digest:  b27e8b06da22707513aa97363dfb11c7c3675d28
  memlock:      4096

When programs are pinned and retrieved by an ELF loader, the loader
can check the program's digest through fdinfo and compare it against
one that was generated over the ELF file's program section to see
if the program needs to be reloaded. Furthermore, this can also be
exposed through other means such as netlink in case of a tc cls/act
dump (or xdp in future), but also through tracepoints or other
facilities to identify the program. Other than that, the digest can
also serve as a base name for the work in progress kallsyms support
of programs. The digest doesn't depend/select the crypto layer, since
we need to keep dependencies to a minimum. iproute2 will get support
for this facility.

Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8d829bdb
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -216,6 +216,7 @@ u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5);
u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);

bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp);
void bpf_prog_calc_digest(struct bpf_prog *fp);

const struct bpf_func_proto *bpf_get_trace_printk_proto(void);

+6 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/capability.h>
#include <linux/cryptohash.h>

#include <net/sch_generic.h>

@@ -56,6 +57,9 @@ struct bpf_prog_aux;
/* BPF program can access up to 512 bytes of stack space. */
#define MAX_BPF_STACK	512

/* Maximum BPF program size in bytes. */
#define MAX_BPF_SIZE	(BPF_MAXINSNS * sizeof(struct bpf_insn))

/* Helper macros for filter block array initializers. */

/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
@@ -404,8 +408,9 @@ struct bpf_prog {
				cb_access:1,	/* Is control block accessed? */
				dst_needed:1;	/* Do we need dst entry? */
	kmemcheck_bitfield_end(meta);
	u32			len;		/* Number of filter blocks */
	enum bpf_prog_type	type;		/* Type of BPF program */
	u32			len;		/* Number of filter blocks */
	u32			digest[SHA_DIGEST_WORDS]; /* Program digest */
	struct bpf_prog_aux	*aux;		/* Auxiliary fields */
	struct sock_fprog_kern	*orig_prog;	/* Original BPF program */
	unsigned int		(*bpf_func)(const void *ctx,
+1 −0
Original line number Diff line number Diff line
@@ -397,6 +397,7 @@ enum {
	TCA_BPF_NAME,
	TCA_BPF_FLAGS,
	TCA_BPF_FLAGS_GEN,
	TCA_BPF_DIGEST,
	__TCA_BPF_MAX,
};

+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ enum {
	TCA_ACT_BPF_FD,
	TCA_ACT_BPF_NAME,
	TCA_ACT_BPF_PAD,
	TCA_ACT_BPF_DIGEST,
	__TCA_ACT_BPF_MAX,
};
#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)
+65 −0
Original line number Diff line number Diff line
@@ -136,6 +136,71 @@ void __bpf_prog_free(struct bpf_prog *fp)
	vfree(fp);
}

#define SHA_BPF_RAW_SIZE						\
	round_up(MAX_BPF_SIZE + sizeof(__be64) + 1, SHA_MESSAGE_BYTES)

/* Called under verifier mutex. */
void bpf_prog_calc_digest(struct bpf_prog *fp)
{
	const u32 bits_offset = SHA_MESSAGE_BYTES - sizeof(__be64);
	static u32 ws[SHA_WORKSPACE_WORDS];
	static u8 raw[SHA_BPF_RAW_SIZE];
	struct bpf_insn *dst = (void *)raw;
	u32 i, bsize, psize, blocks;
	bool was_ld_map;
	u8 *todo = raw;
	__be32 *result;
	__be64 *bits;

	sha_init(fp->digest);
	memset(ws, 0, sizeof(ws));

	/* We need to take out the map fd for the digest calculation
	 * since they are unstable from user space side.
	 */
	for (i = 0, was_ld_map = false; i < fp->len; i++) {
		dst[i] = fp->insnsi[i];
		if (!was_ld_map &&
		    dst[i].code == (BPF_LD | BPF_IMM | BPF_DW) &&
		    dst[i].src_reg == BPF_PSEUDO_MAP_FD) {
			was_ld_map = true;
			dst[i].imm = 0;
		} else if (was_ld_map &&
			   dst[i].code == 0 &&
			   dst[i].dst_reg == 0 &&
			   dst[i].src_reg == 0 &&
			   dst[i].off == 0) {
			was_ld_map = false;
			dst[i].imm = 0;
		} else {
			was_ld_map = false;
		}
	}

	psize = fp->len * sizeof(struct bpf_insn);
	memset(&raw[psize], 0, sizeof(raw) - psize);
	raw[psize++] = 0x80;

	bsize  = round_up(psize, SHA_MESSAGE_BYTES);
	blocks = bsize / SHA_MESSAGE_BYTES;
	if (bsize - psize >= sizeof(__be64)) {
		bits = (__be64 *)(todo + bsize - sizeof(__be64));
	} else {
		bits = (__be64 *)(todo + bsize + bits_offset);
		blocks++;
	}
	*bits = cpu_to_be64((psize - 1) << 3);

	while (blocks--) {
		sha_transform(fp->digest, todo, ws);
		todo += SHA_MESSAGE_BYTES;
	}

	result = (__force __be32 *)fp->digest;
	for (i = 0; i < SHA_DIGEST_WORDS; i++)
		result[i] = cpu_to_be32(fp->digest[i]);
}

static bool bpf_is_jmp_and_has_target(const struct bpf_insn *insn)
{
	return BPF_CLASS(insn->code) == BPF_JMP  &&
Loading