Skip to content

Commit ebc614f

Browse files
rgushchindavem330
authored andcommitted
bpf, cgroup: implement eBPF-based device controller for cgroup v2
Cgroup v2 lacks the device controller, provided by cgroup v1. This patch adds a new eBPF program type, which in combination of previously added ability to attach multiple eBPF programs to a cgroup, will provide a similar functionality, but with some additional flexibility. This patch introduces a BPF_PROG_TYPE_CGROUP_DEVICE program type. A program takes major and minor device numbers, device type (block/character) and access type (mknod/read/write) as parameters and returns an integer which defines if the operation should be allowed or terminated with -EPERM. Signed-off-by: Roman Gushchin <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Acked-by: Tejun Heo <[email protected]> Cc: Daniel Borkmann <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ecf8fec commit ebc614f

File tree

8 files changed

+130
-1
lines changed

8 files changed

+130
-1
lines changed

include/linux/bpf-cgroup.h

+15
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

+3
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

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22
#include <linux/fs.h>
3+
#include <linux/bpf-cgroup.h>
34

45
#define DEVCG_ACC_MKNOD 1
56
#define DEVCG_ACC_READ 2
@@ -19,10 +20,15 @@ static inline int __devcgroup_check_permission(short type, u32 major, u32 minor,
1920
{ return 0; }
2021
#endif
2122

22-
#ifdef CONFIG_CGROUP_DEVICE
23+
#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF)
2324
static inline int devcgroup_check_permission(short type, u32 major, u32 minor,
2425
short access)
2526
{
27+
int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access);
28+
29+
if (rc)
30+
return -EPERM;
31+
2632
return __devcgroup_check_permission(type, major, minor, access);
2733
}
2834

include/uapi/linux/bpf.h

+15
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

+67
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

+7
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

+1
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;

tools/include/uapi/linux/bpf.h

+15
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ enum bpf_prog_type {
131131
BPF_PROG_TYPE_LWT_XMIT,
132132
BPF_PROG_TYPE_SOCK_OPS,
133133
BPF_PROG_TYPE_SK_SKB,
134+
BPF_PROG_TYPE_CGROUP_DEVICE,
134135
};
135136

136137
enum bpf_attach_type {
@@ -140,6 +141,7 @@ enum bpf_attach_type {
140141
BPF_CGROUP_SOCK_OPS,
141142
BPF_SK_SKB_STREAM_PARSER,
142143
BPF_SK_SKB_STREAM_VERDICT,
144+
BPF_CGROUP_DEVICE,
143145
__MAX_BPF_ATTACH_TYPE
144146
};
145147

@@ -990,4 +992,17 @@ struct bpf_perf_event_value {
990992
__u64 running;
991993
};
992994

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

0 commit comments

Comments
 (0)