Skip to content

Commit 2798b80

Browse files
committed
Merge branch 'eBPF-based-device-cgroup-controller'
Roman Gushchin says: ==================== eBPF-based device cgroup controller This patchset introduces an eBPF-based device controller for cgroup v2. Patches (1) and (2) are a preparational work required to share some code with the existing device controller implementation. Patch (3) is the main patch, which introduces a new bpf prog type and all necessary infrastructure. Patch (4) moves cgroup_helpers.c/h to use them by patch (4). Patch (5) implements an example of eBPF program which controls access to device files and corresponding userspace test. v3: Renamed constants introduced by patch (3) to BPF_DEVCG_* v2: Added patch (1). v1: https://lkml.org/lkml/2017/11/1/363 ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 488e5b3 + 37f1ba0 commit 2798b80

File tree

15 files changed

+369
-76
lines changed

15 files changed

+369
-76
lines changed

include/linux/bpf-cgroup.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
6767
struct bpf_sock_ops_kern *sock_ops,
6868
enum bpf_attach_type type);
6969

70+
int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
71+
short access, enum bpf_attach_type type);
72+
7073
/* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */
7174
#define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb) \
7275
({ \
@@ -112,6 +115,17 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
112115
} \
113116
__ret; \
114117
})
118+
119+
#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access) \
120+
({ \
121+
int __ret = 0; \
122+
if (cgroup_bpf_enabled) \
123+
__ret = __cgroup_bpf_check_dev_permission(type, major, minor, \
124+
access, \
125+
BPF_CGROUP_DEVICE); \
126+
\
127+
__ret; \
128+
})
115129
#else
116130

117131
struct cgroup_bpf {};
@@ -122,6 +136,7 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; }
122136
#define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; })
123137
#define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; })
124138
#define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
139+
#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; })
125140

126141
#endif /* CONFIG_CGROUP_BPF */
127142

include/linux/bpf_types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe)
1919
BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint)
2020
BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event)
2121
#endif
22+
#ifdef CONFIG_CGROUP_BPF
23+
BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
24+
#endif
2225

2326
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
2427
BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)

include/linux/device_cgroup.h

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,76 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22
#include <linux/fs.h>
3+
#include <linux/bpf-cgroup.h>
4+
5+
#define DEVCG_ACC_MKNOD 1
6+
#define DEVCG_ACC_READ 2
7+
#define DEVCG_ACC_WRITE 4
8+
#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE)
9+
10+
#define DEVCG_DEV_BLOCK 1
11+
#define DEVCG_DEV_CHAR 2
12+
#define DEVCG_DEV_ALL 4 /* this represents all devices */
313

414
#ifdef CONFIG_CGROUP_DEVICE
5-
extern int __devcgroup_inode_permission(struct inode *inode, int mask);
6-
extern int devcgroup_inode_mknod(int mode, dev_t dev);
15+
extern int __devcgroup_check_permission(short type, u32 major, u32 minor,
16+
short access);
17+
#else
18+
static inline int __devcgroup_check_permission(short type, u32 major, u32 minor,
19+
short access)
20+
{ return 0; }
21+
#endif
22+
23+
#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
24+
static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
25+
short access)
26+
{
27+
int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
28+
29+
if (rc)
30+
return -EPERM;
31+
32+
return __devcgroup_check_permission(type, major, minor, access);
33+
}
34+
735
static inline int devcgroup_inode_permission(struct inode *inode, int mask)
836
{
37+
short type, access = 0;
38+
939
if (likely(!inode->i_rdev))
1040
return 0;
11-
if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
41+
42+
if (S_ISBLK(inode->i_mode))
43+
type = DEVCG_DEV_BLOCK;
44+
else if (S_ISCHR(inode->i_mode))
45+
type = DEVCG_DEV_CHAR;
46+
else
47+
return 0;
48+
49+
if (mask & MAY_WRITE)
50+
access |= DEVCG_ACC_WRITE;
51+
if (mask & MAY_READ)
52+
access |= DEVCG_ACC_READ;
53+
54+
return devcgroup_check_permission(type, imajor(inode), iminor(inode),
55+
access);
56+
}
57+
58+
static inline int devcgroup_inode_mknod(int mode, dev_t dev)
59+
{
60+
short type;
61+
62+
if (!S_ISBLK(mode) && !S_ISCHR(mode))
1263
return 0;
13-
return __devcgroup_inode_permission(inode, mask);
64+
65+
if (S_ISBLK(mode))
66+
type = DEVCG_DEV_BLOCK;
67+
else
68+
type = DEVCG_DEV_CHAR;
69+
70+
return devcgroup_check_permission(type, MAJOR(dev), MINOR(dev),
71+
DEVCG_ACC_MKNOD);
1472
}
73+
1574
#else
1675
static inline int devcgroup_inode_permission(struct inode *inode, int mask)
1776
{ return 0; }

include/uapi/linux/bpf.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ enum bpf_prog_type {
132132
BPF_PROG_TYPE_LWT_XMIT,
133133
BPF_PROG_TYPE_SOCK_OPS,
134134
BPF_PROG_TYPE_SK_SKB,
135+
BPF_PROG_TYPE_CGROUP_DEVICE,
135136
};
136137

137138
enum bpf_attach_type {
@@ -141,6 +142,7 @@ enum bpf_attach_type {
141142
BPF_CGROUP_SOCK_OPS,
142143
BPF_SK_SKB_STREAM_PARSER,
143144
BPF_SK_SKB_STREAM_VERDICT,
145+
BPF_CGROUP_DEVICE,
144146
__MAX_BPF_ATTACH_TYPE
145147
};
146148

@@ -991,4 +993,17 @@ struct bpf_perf_event_value {
991993
__u64 running;
992994
};
993995

996+
#define BPF_DEVCG_ACC_MKNOD (1ULL << 0)
997+
#define BPF_DEVCG_ACC_READ (1ULL << 1)
998+
#define BPF_DEVCG_ACC_WRITE (1ULL << 2)
999+
1000+
#define BPF_DEVCG_DEV_BLOCK (1ULL << 0)
1001+
#define BPF_DEVCG_DEV_CHAR (1ULL << 1)
1002+
1003+
struct bpf_cgroup_dev_ctx {
1004+
__u32 access_type; /* (access << 16) | type */
1005+
__u32 major;
1006+
__u32 minor;
1007+
};
1008+
9941009
#endif /* _UAPI__LINUX_BPF_H__ */

kernel/bpf/cgroup.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,3 +522,70 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk,
522522
return ret == 1 ? 0 : -EPERM;
523523
}
524524
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops);
525+
526+
int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
527+
short access, enum bpf_attach_type type)
528+
{
529+
struct cgroup *cgrp;
530+
struct bpf_cgroup_dev_ctx ctx = {
531+
.access_type = (access << 16) | dev_type,
532+
.major = major,
533+
.minor = minor,
534+
};
535+
int allow = 1;
536+
537+
rcu_read_lock();
538+
cgrp = task_dfl_cgroup(current);
539+
allow = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx,
540+
BPF_PROG_RUN);
541+
rcu_read_unlock();
542+
543+
return !allow;
544+
}
545+
EXPORT_SYMBOL(__cgroup_bpf_check_dev_permission);
546+
547+
static const struct bpf_func_proto *
548+
cgroup_dev_func_proto(enum bpf_func_id func_id)
549+
{
550+
switch (func_id) {
551+
case BPF_FUNC_map_lookup_elem:
552+
return &bpf_map_lookup_elem_proto;
553+
case BPF_FUNC_map_update_elem:
554+
return &bpf_map_update_elem_proto;
555+
case BPF_FUNC_map_delete_elem:
556+
return &bpf_map_delete_elem_proto;
557+
case BPF_FUNC_get_current_uid_gid:
558+
return &bpf_get_current_uid_gid_proto;
559+
case BPF_FUNC_trace_printk:
560+
if (capable(CAP_SYS_ADMIN))
561+
return bpf_get_trace_printk_proto();
562+
default:
563+
return NULL;
564+
}
565+
}
566+
567+
static bool cgroup_dev_is_valid_access(int off, int size,
568+
enum bpf_access_type type,
569+
struct bpf_insn_access_aux *info)
570+
{
571+
if (type == BPF_WRITE)
572+
return false;
573+
574+
if (off < 0 || off + size > sizeof(struct bpf_cgroup_dev_ctx))
575+
return false;
576+
/* The verifier guarantees that size > 0. */
577+
if (off % size != 0)
578+
return false;
579+
if (size != sizeof(__u32))
580+
return false;
581+
582+
return true;
583+
}
584+
585+
const struct bpf_prog_ops cg_dev_prog_ops = {
586+
};
587+
588+
const struct bpf_verifier_ops cg_dev_verifier_ops = {
589+
.get_func_proto = cgroup_dev_func_proto,
590+
.is_valid_access = cgroup_dev_is_valid_access,
591+
};

kernel/bpf/syscall.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)
13261326
case BPF_CGROUP_SOCK_OPS:
13271327
ptype = BPF_PROG_TYPE_SOCK_OPS;
13281328
break;
1329+
case BPF_CGROUP_DEVICE:
1330+
ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
1331+
break;
13291332
case BPF_SK_SKB_STREAM_PARSER:
13301333
case BPF_SK_SKB_STREAM_VERDICT:
13311334
return sockmap_get_from_fd(attr, true);
@@ -1378,6 +1381,9 @@ static int bpf_prog_detach(const union bpf_attr *attr)
13781381
case BPF_CGROUP_SOCK_OPS:
13791382
ptype = BPF_PROG_TYPE_SOCK_OPS;
13801383
break;
1384+
case BPF_CGROUP_DEVICE:
1385+
ptype = BPF_PROG_TYPE_CGROUP_DEVICE;
1386+
break;
13811387
case BPF_SK_SKB_STREAM_PARSER:
13821388
case BPF_SK_SKB_STREAM_VERDICT:
13831389
return sockmap_get_from_fd(attr, false);
@@ -1420,6 +1426,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
14201426
case BPF_CGROUP_INET_EGRESS:
14211427
case BPF_CGROUP_INET_SOCK_CREATE:
14221428
case BPF_CGROUP_SOCK_OPS:
1429+
case BPF_CGROUP_DEVICE:
14231430
break;
14241431
default:
14251432
return -EINVAL;

kernel/bpf/verifier.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,6 +3124,7 @@ static int check_return_code(struct bpf_verifier_env *env)
31243124
case BPF_PROG_TYPE_CGROUP_SKB:
31253125
case BPF_PROG_TYPE_CGROUP_SOCK:
31263126
case BPF_PROG_TYPE_SOCK_OPS:
3127+
case BPF_PROG_TYPE_CGROUP_DEVICE:
31273128
break;
31283129
default:
31293130
return 0;

samples/bpf/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ hostprogs-y += syscall_tp
4646

4747
# Libbpf dependencies
4848
LIBBPF := ../../tools/lib/bpf/bpf.o
49+
CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o
4950

5051
test_lru_dist-objs := test_lru_dist.o $(LIBBPF)
5152
sock_example-objs := sock_example.o $(LIBBPF)
@@ -69,13 +70,13 @@ map_perf_test-objs := bpf_load.o $(LIBBPF) map_perf_test_user.o
6970
test_overhead-objs := bpf_load.o $(LIBBPF) test_overhead_user.o
7071
test_cgrp2_array_pin-objs := $(LIBBPF) test_cgrp2_array_pin.o
7172
test_cgrp2_attach-objs := $(LIBBPF) test_cgrp2_attach.o
72-
test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o cgroup_helpers.o
73+
test_cgrp2_attach2-objs := $(LIBBPF) test_cgrp2_attach2.o $(CGROUP_HELPERS)
7374
test_cgrp2_sock-objs := $(LIBBPF) test_cgrp2_sock.o
7475
test_cgrp2_sock2-objs := bpf_load.o $(LIBBPF) test_cgrp2_sock2.o
7576
xdp1-objs := bpf_load.o $(LIBBPF) xdp1_user.o
7677
# reuse xdp1 source intentionally
7778
xdp2-objs := bpf_load.o $(LIBBPF) xdp1_user.o
78-
test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) cgroup_helpers.o \
79+
test_current_task_under_cgroup-objs := bpf_load.o $(LIBBPF) $(CGROUP_HELPERS) \
7980
test_current_task_under_cgroup_user.o
8081
trace_event-objs := bpf_load.o $(LIBBPF) trace_event_user.o
8182
sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o

0 commit comments

Comments
 (0)