diff options
| -rw-r--r-- | include/linux/bpf.h | 1 | ||||
| -rw-r--r-- | include/linux/filter.h | 7 | ||||
| -rw-r--r-- | include/uapi/linux/pkt_cls.h | 1 | ||||
| -rw-r--r-- | include/uapi/linux/tc_act/tc_bpf.h | 1 | ||||
| -rw-r--r-- | kernel/bpf/core.c | 65 | ||||
| -rw-r--r-- | kernel/bpf/syscall.c | 24 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 2 | ||||
| -rw-r--r-- | net/sched/act_bpf.c | 9 | ||||
| -rw-r--r-- | net/sched/cls_bpf.c | 8 | 
9 files changed, 116 insertions, 2 deletions
| diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 69d0a7f12a3b..8796ff03f472 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -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); diff --git a/include/linux/filter.h b/include/linux/filter.h index 97338134398f..f078d2b1cff6 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -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, diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 86786d45ee66..1adc0b654996 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -397,6 +397,7 @@ enum {  	TCA_BPF_NAME,  	TCA_BPF_FLAGS,  	TCA_BPF_FLAGS_GEN, +	TCA_BPF_DIGEST,  	__TCA_BPF_MAX,  }; diff --git a/include/uapi/linux/tc_act/tc_bpf.h b/include/uapi/linux/tc_act/tc_bpf.h index 063d9d465119..a6b88a6f7f71 100644 --- a/include/uapi/linux/tc_act/tc_bpf.h +++ b/include/uapi/linux/tc_act/tc_bpf.h @@ -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) diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 82a04143368e..bdcc9f4ba767 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -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  && diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 85af86c496cd..c0d2b423ce93 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -662,8 +662,30 @@ static int bpf_prog_release(struct inode *inode, struct file *filp)  	return 0;  } +#ifdef CONFIG_PROC_FS +static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp) +{ +	const struct bpf_prog *prog = filp->private_data; +	char prog_digest[sizeof(prog->digest) * 2 + 1] = { }; + +	bin2hex(prog_digest, prog->digest, sizeof(prog->digest)); +	seq_printf(m, +		   "prog_type:\t%u\n" +		   "prog_jited:\t%u\n" +		   "prog_digest:\t%s\n" +		   "memlock:\t%llu\n", +		   prog->type, +		   prog->jited, +		   prog_digest, +		   prog->pages * 1ULL << PAGE_SHIFT); +} +#endif +  static const struct file_operations bpf_prog_fops = { -        .release = bpf_prog_release, +#ifdef CONFIG_PROC_FS +	.show_fdinfo	= bpf_prog_show_fdinfo, +#endif +	.release	= bpf_prog_release,  };  int bpf_prog_new_fd(struct bpf_prog *prog) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 38d05da84a49..cb37339ca0da 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3176,6 +3176,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)  		log_level = 0;  	} +	bpf_prog_calc_digest(env->prog); +  	ret = replace_map_fd_with_map_ptr(env);  	if (ret < 0)  		goto skip_full_check; diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 84c1d2da4f8b..1c60317f0121 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -117,10 +117,19 @@ static int tcf_bpf_dump_bpf_info(const struct tcf_bpf *prog,  static int tcf_bpf_dump_ebpf_info(const struct tcf_bpf *prog,  				  struct sk_buff *skb)  { +	struct nlattr *nla; +  	if (prog->bpf_name &&  	    nla_put_string(skb, TCA_ACT_BPF_NAME, prog->bpf_name))  		return -EMSGSIZE; +	nla = nla_reserve(skb, TCA_ACT_BPF_DIGEST, +			  sizeof(prog->filter->digest)); +	if (nla == NULL) +		return -EMSGSIZE; + +	memcpy(nla_data(nla), prog->filter->digest, nla_len(nla)); +  	return 0;  } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index f70e03d2d2c8..adc776048d1a 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -549,10 +549,18 @@ static int cls_bpf_dump_bpf_info(const struct cls_bpf_prog *prog,  static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog,  				  struct sk_buff *skb)  { +	struct nlattr *nla; +  	if (prog->bpf_name &&  	    nla_put_string(skb, TCA_BPF_NAME, prog->bpf_name))  		return -EMSGSIZE; +	nla = nla_reserve(skb, TCA_BPF_DIGEST, sizeof(prog->filter->digest)); +	if (nla == NULL) +		return -EMSGSIZE; + +	memcpy(nla_data(nla), prog->filter->digest, nla_len(nla)); +  	return 0;  } | 
