Skip to content

Commit 92117d8

Browse files
4astdavem330
authored andcommitted
bpf: fix refcnt overflow
On a system with >32Gbyte of phyiscal memory and infinite RLIMIT_MEMLOCK, the malicious application may overflow 32-bit bpf program refcnt. It's also possible to overflow map refcnt on 1Tb system. Impose 32k hard limit which means that the same bpf program or map cannot be shared by more than 32k processes. Fixes: 1be7f75 ("bpf: enable non-root eBPF programs") Reported-by: Jann Horn <jannh@google.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent bd34cf6 commit 92117d8

File tree

4 files changed

+33
-12
lines changed

4 files changed

+33
-12
lines changed

Diff for: include/linux/bpf.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,13 @@ void bpf_register_prog_type(struct bpf_prog_type_list *tl);
171171
void bpf_register_map_type(struct bpf_map_type_list *tl);
172172

173173
struct bpf_prog *bpf_prog_get(u32 ufd);
174+
struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog);
174175
void bpf_prog_put(struct bpf_prog *prog);
175176
void bpf_prog_put_rcu(struct bpf_prog *prog);
176177

177178
struct bpf_map *bpf_map_get_with_uref(u32 ufd);
178179
struct bpf_map *__bpf_map_get(struct fd f);
179-
void bpf_map_inc(struct bpf_map *map, bool uref);
180+
struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref);
180181
void bpf_map_put_with_uref(struct bpf_map *map);
181182
void bpf_map_put(struct bpf_map *map);
182183
int bpf_map_precharge_memlock(u32 pages);

Diff for: kernel/bpf/inode.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ static void *bpf_any_get(void *raw, enum bpf_type type)
3131
{
3232
switch (type) {
3333
case BPF_TYPE_PROG:
34-
atomic_inc(&((struct bpf_prog *)raw)->aux->refcnt);
34+
raw = bpf_prog_inc(raw);
3535
break;
3636
case BPF_TYPE_MAP:
37-
bpf_map_inc(raw, true);
37+
raw = bpf_map_inc(raw, true);
3838
break;
3939
default:
4040
WARN_ON_ONCE(1);
@@ -297,7 +297,8 @@ static void *bpf_obj_do_get(const struct filename *pathname,
297297
goto out;
298298

299299
raw = bpf_any_get(inode->i_private, *type);
300-
touch_atime(&path);
300+
if (!IS_ERR(raw))
301+
touch_atime(&path);
301302

302303
path_put(&path);
303304
return raw;

Diff for: kernel/bpf/syscall.c

+20-4
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,18 @@ struct bpf_map *__bpf_map_get(struct fd f)
218218
return f.file->private_data;
219219
}
220220

221-
void bpf_map_inc(struct bpf_map *map, bool uref)
221+
/* prog's and map's refcnt limit */
222+
#define BPF_MAX_REFCNT 32768
223+
224+
struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref)
222225
{
223-
atomic_inc(&map->refcnt);
226+
if (atomic_inc_return(&map->refcnt) > BPF_MAX_REFCNT) {
227+
atomic_dec(&map->refcnt);
228+
return ERR_PTR(-EBUSY);
229+
}
224230
if (uref)
225231
atomic_inc(&map->usercnt);
232+
return map;
226233
}
227234

228235
struct bpf_map *bpf_map_get_with_uref(u32 ufd)
@@ -234,7 +241,7 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd)
234241
if (IS_ERR(map))
235242
return map;
236243

237-
bpf_map_inc(map, true);
244+
map = bpf_map_inc(map, true);
238245
fdput(f);
239246

240247
return map;
@@ -658,6 +665,15 @@ static struct bpf_prog *__bpf_prog_get(struct fd f)
658665
return f.file->private_data;
659666
}
660667

668+
struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog)
669+
{
670+
if (atomic_inc_return(&prog->aux->refcnt) > BPF_MAX_REFCNT) {
671+
atomic_dec(&prog->aux->refcnt);
672+
return ERR_PTR(-EBUSY);
673+
}
674+
return prog;
675+
}
676+
661677
/* called by sockets/tracing/seccomp before attaching program to an event
662678
* pairs with bpf_prog_put()
663679
*/
@@ -670,7 +686,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd)
670686
if (IS_ERR(prog))
671687
return prog;
672688

673-
atomic_inc(&prog->aux->refcnt);
689+
prog = bpf_prog_inc(prog);
674690
fdput(f);
675691

676692
return prog;

Diff for: kernel/bpf/verifier.c

+7-4
Original file line numberDiff line numberDiff line change
@@ -2049,15 +2049,18 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env)
20492049
return -E2BIG;
20502050
}
20512051

2052-
/* remember this map */
2053-
env->used_maps[env->used_map_cnt++] = map;
2054-
20552052
/* hold the map. If the program is rejected by verifier,
20562053
* the map will be released by release_maps() or it
20572054
* will be used by the valid program until it's unloaded
20582055
* and all maps are released in free_bpf_prog_info()
20592056
*/
2060-
bpf_map_inc(map, false);
2057+
map = bpf_map_inc(map, false);
2058+
if (IS_ERR(map)) {
2059+
fdput(f);
2060+
return PTR_ERR(map);
2061+
}
2062+
env->used_maps[env->used_map_cnt++] = map;
2063+
20612064
fdput(f);
20622065
next_insn:
20632066
insn++;

0 commit comments

Comments
 (0)