Skip to content

Commit 7bd509e

Browse files
borkmanndavem330
authored andcommitted
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: Daniel Borkmann <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8d829bd commit 7bd509e

File tree

9 files changed

+116
-2
lines changed

9 files changed

+116
-2
lines changed

include/linux/bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5);
216216
u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
217217

218218
bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp);
219+
void bpf_prog_calc_digest(struct bpf_prog *fp);
219220

220221
const struct bpf_func_proto *bpf_get_trace_printk_proto(void);
221222

include/linux/filter.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/workqueue.h>
1515
#include <linux/sched.h>
1616
#include <linux/capability.h>
17+
#include <linux/cryptohash.h>
1718

1819
#include <net/sch_generic.h>
1920

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

60+
/* Maximum BPF program size in bytes. */
61+
#define MAX_BPF_SIZE (BPF_MAXINSNS * sizeof(struct bpf_insn))
62+
5963
/* Helper macros for filter block array initializers. */
6064

6165
/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
@@ -404,8 +408,9 @@ struct bpf_prog {
404408
cb_access:1, /* Is control block accessed? */
405409
dst_needed:1; /* Do we need dst entry? */
406410
kmemcheck_bitfield_end(meta);
407-
u32 len; /* Number of filter blocks */
408411
enum bpf_prog_type type; /* Type of BPF program */
412+
u32 len; /* Number of filter blocks */
413+
u32 digest[SHA_DIGEST_WORDS]; /* Program digest */
409414
struct bpf_prog_aux *aux; /* Auxiliary fields */
410415
struct sock_fprog_kern *orig_prog; /* Original BPF program */
411416
unsigned int (*bpf_func)(const void *ctx,

include/uapi/linux/pkt_cls.h

+1
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ enum {
397397
TCA_BPF_NAME,
398398
TCA_BPF_FLAGS,
399399
TCA_BPF_FLAGS_GEN,
400+
TCA_BPF_DIGEST,
400401
__TCA_BPF_MAX,
401402
};
402403

include/uapi/linux/tc_act/tc_bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum {
2727
TCA_ACT_BPF_FD,
2828
TCA_ACT_BPF_NAME,
2929
TCA_ACT_BPF_PAD,
30+
TCA_ACT_BPF_DIGEST,
3031
__TCA_ACT_BPF_MAX,
3132
};
3233
#define TCA_ACT_BPF_MAX (__TCA_ACT_BPF_MAX - 1)

kernel/bpf/core.c

+65
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,71 @@ void __bpf_prog_free(struct bpf_prog *fp)
136136
vfree(fp);
137137
}
138138

139+
#define SHA_BPF_RAW_SIZE \
140+
round_up(MAX_BPF_SIZE + sizeof(__be64) + 1, SHA_MESSAGE_BYTES)
141+
142+
/* Called under verifier mutex. */
143+
void bpf_prog_calc_digest(struct bpf_prog *fp)
144+
{
145+
const u32 bits_offset = SHA_MESSAGE_BYTES - sizeof(__be64);
146+
static u32 ws[SHA_WORKSPACE_WORDS];
147+
static u8 raw[SHA_BPF_RAW_SIZE];
148+
struct bpf_insn *dst = (void *)raw;
149+
u32 i, bsize, psize, blocks;
150+
bool was_ld_map;
151+
u8 *todo = raw;
152+
__be32 *result;
153+
__be64 *bits;
154+
155+
sha_init(fp->digest);
156+
memset(ws, 0, sizeof(ws));
157+
158+
/* We need to take out the map fd for the digest calculation
159+
* since they are unstable from user space side.
160+
*/
161+
for (i = 0, was_ld_map = false; i < fp->len; i++) {
162+
dst[i] = fp->insnsi[i];
163+
if (!was_ld_map &&
164+
dst[i].code == (BPF_LD | BPF_IMM | BPF_DW) &&
165+
dst[i].src_reg == BPF_PSEUDO_MAP_FD) {
166+
was_ld_map = true;
167+
dst[i].imm = 0;
168+
} else if (was_ld_map &&
169+
dst[i].code == 0 &&
170+
dst[i].dst_reg == 0 &&
171+
dst[i].src_reg == 0 &&
172+
dst[i].off == 0) {
173+
was_ld_map = false;
174+
dst[i].imm = 0;
175+
} else {
176+
was_ld_map = false;
177+
}
178+
}
179+
180+
psize = fp->len * sizeof(struct bpf_insn);
181+
memset(&raw[psize], 0, sizeof(raw) - psize);
182+
raw[psize++] = 0x80;
183+
184+
bsize = round_up(psize, SHA_MESSAGE_BYTES);
185+
blocks = bsize / SHA_MESSAGE_BYTES;
186+
if (bsize - psize >= sizeof(__be64)) {
187+
bits = (__be64 *)(todo + bsize - sizeof(__be64));
188+
} else {
189+
bits = (__be64 *)(todo + bsize + bits_offset);
190+
blocks++;
191+
}
192+
*bits = cpu_to_be64((psize - 1) << 3);
193+
194+
while (blocks--) {
195+
sha_transform(fp->digest, todo, ws);
196+
todo += SHA_MESSAGE_BYTES;
197+
}
198+
199+
result = (__force __be32 *)fp->digest;
200+
for (i = 0; i < SHA_DIGEST_WORDS; i++)
201+
result[i] = cpu_to_be32(fp->digest[i]);
202+
}
203+
139204
static bool bpf_is_jmp_and_has_target(const struct bpf_insn *insn)
140205
{
141206
return BPF_CLASS(insn->code) == BPF_JMP &&

kernel/bpf/syscall.c

+23-1
Original file line numberDiff line numberDiff line change
@@ -662,8 +662,30 @@ static int bpf_prog_release(struct inode *inode, struct file *filp)
662662
return 0;
663663
}
664664

665+
#ifdef CONFIG_PROC_FS
666+
static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp)
667+
{
668+
const struct bpf_prog *prog = filp->private_data;
669+
char prog_digest[sizeof(prog->digest) * 2 + 1] = { };
670+
671+
bin2hex(prog_digest, prog->digest, sizeof(prog->digest));
672+
seq_printf(m,
673+
"prog_type:\t%u\n"
674+
"prog_jited:\t%u\n"
675+
"prog_digest:\t%s\n"
676+
"memlock:\t%llu\n",
677+
prog->type,
678+
prog->jited,
679+
prog_digest,
680+
prog->pages * 1ULL << PAGE_SHIFT);
681+
}
682+
#endif
683+
665684
static const struct file_operations bpf_prog_fops = {
666-
.release = bpf_prog_release,
685+
#ifdef CONFIG_PROC_FS
686+
.show_fdinfo = bpf_prog_show_fdinfo,
687+
#endif
688+
.release = bpf_prog_release,
667689
};
668690

669691
int bpf_prog_new_fd(struct bpf_prog *prog)

kernel/bpf/verifier.c

+2
Original file line numberDiff line numberDiff line change
@@ -3176,6 +3176,8 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
31763176
log_level = 0;
31773177
}
31783178

3179+
bpf_prog_calc_digest(env->prog);
3180+
31793181
ret = replace_map_fd_with_map_ptr(env);
31803182
if (ret < 0)
31813183
goto skip_full_check;

net/sched/act_bpf.c

+9
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,19 @@ static int tcf_bpf_dump_bpf_info(const struct tcf_bpf *prog,
117117
static int tcf_bpf_dump_ebpf_info(const struct tcf_bpf *prog,
118118
struct sk_buff *skb)
119119
{
120+
struct nlattr *nla;
121+
120122
if (prog->bpf_name &&
121123
nla_put_string(skb, TCA_ACT_BPF_NAME, prog->bpf_name))
122124
return -EMSGSIZE;
123125

126+
nla = nla_reserve(skb, TCA_ACT_BPF_DIGEST,
127+
sizeof(prog->filter->digest));
128+
if (nla == NULL)
129+
return -EMSGSIZE;
130+
131+
memcpy(nla_data(nla), prog->filter->digest, nla_len(nla));
132+
124133
return 0;
125134
}
126135

net/sched/cls_bpf.c

+8
Original file line numberDiff line numberDiff line change
@@ -549,10 +549,18 @@ static int cls_bpf_dump_bpf_info(const struct cls_bpf_prog *prog,
549549
static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog,
550550
struct sk_buff *skb)
551551
{
552+
struct nlattr *nla;
553+
552554
if (prog->bpf_name &&
553555
nla_put_string(skb, TCA_BPF_NAME, prog->bpf_name))
554556
return -EMSGSIZE;
555557

558+
nla = nla_reserve(skb, TCA_BPF_DIGEST, sizeof(prog->filter->digest));
559+
if (nla == NULL)
560+
return -EMSGSIZE;
561+
562+
memcpy(nla_data(nla), prog->filter->digest, nla_len(nla));
563+
556564
return 0;
557565
}
558566

0 commit comments

Comments
 (0)