diff options
author | Martin KaFai Lau <kafai@fb.com> | 2017-06-05 15:15:49 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-06-06 15:41:23 -0400 |
commit | b16d9aa4c2b90af8d2c3201e245150f8c430c3bc (patch) | |
tree | 2fc2f74214723830c29d5f7370870507a97b7387 /kernel/bpf/syscall.c | |
parent | 34ad5580f8f9c86cb273ebea25c149613cd1667e (diff) |
bpf: Add BPF_PROG_GET_FD_BY_ID
Add BPF_PROG_GET_FD_BY_ID command to allow user to get a fd
from a bpf_prog's ID.
bpf_prog_inc_not_zero() is added and is called with prog_idr_lock
held.
__bpf_prog_put() is also added which has the 'bool do_idr_lock'
param to decide if the prog_idr_lock should be acquired when
freeing the prog->id.
In the error path of bpf_prog_inc_not_zero(), it may have to
call __bpf_prog_put(map, false) which does not need
to take the prog_idr_lock when freeing the prog->id.
It is currently limited to CAP_SYS_ADMIN which we can
consider to lift it in followup patches.
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@fb.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/bpf/syscall.c')
-rw-r--r-- | kernel/bpf/syscall.c | 91 |
1 files changed, 81 insertions, 10 deletions
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 2405feedb8c1..dc6253bb8ebb 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
@@ -703,15 +703,23 @@ static int bpf_prog_alloc_id(struct bpf_prog *prog) | |||
703 | return id > 0 ? 0 : id; | 703 | return id > 0 ? 0 : id; |
704 | } | 704 | } |
705 | 705 | ||
706 | static void bpf_prog_free_id(struct bpf_prog *prog) | 706 | static void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock) |
707 | { | 707 | { |
708 | /* cBPF to eBPF migrations are currently not in the idr store. */ | 708 | /* cBPF to eBPF migrations are currently not in the idr store. */ |
709 | if (!prog->aux->id) | 709 | if (!prog->aux->id) |
710 | return; | 710 | return; |
711 | 711 | ||
712 | spin_lock_bh(&prog_idr_lock); | 712 | if (do_idr_lock) |
713 | spin_lock_bh(&prog_idr_lock); | ||
714 | else | ||
715 | __acquire(&prog_idr_lock); | ||
716 | |||
713 | idr_remove(&prog_idr, prog->aux->id); | 717 | idr_remove(&prog_idr, prog->aux->id); |
714 | spin_unlock_bh(&prog_idr_lock); | 718 | |
719 | if (do_idr_lock) | ||
720 | spin_unlock_bh(&prog_idr_lock); | ||
721 | else | ||
722 | __release(&prog_idr_lock); | ||
715 | } | 723 | } |
716 | 724 | ||
717 | static void __bpf_prog_put_rcu(struct rcu_head *rcu) | 725 | static void __bpf_prog_put_rcu(struct rcu_head *rcu) |
@@ -723,16 +731,21 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu) | |||
723 | bpf_prog_free(aux->prog); | 731 | bpf_prog_free(aux->prog); |
724 | } | 732 | } |
725 | 733 | ||
726 | void bpf_prog_put(struct bpf_prog *prog) | 734 | static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock) |
727 | { | 735 | { |
728 | if (atomic_dec_and_test(&prog->aux->refcnt)) { | 736 | if (atomic_dec_and_test(&prog->aux->refcnt)) { |
729 | trace_bpf_prog_put_rcu(prog); | 737 | trace_bpf_prog_put_rcu(prog); |
730 | /* bpf_prog_free_id() must be called first */ | 738 | /* bpf_prog_free_id() must be called first */ |
731 | bpf_prog_free_id(prog); | 739 | bpf_prog_free_id(prog, do_idr_lock); |
732 | bpf_prog_kallsyms_del(prog); | 740 | bpf_prog_kallsyms_del(prog); |
733 | call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); | 741 | call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu); |
734 | } | 742 | } |
735 | } | 743 | } |
744 | |||
745 | void bpf_prog_put(struct bpf_prog *prog) | ||
746 | { | ||
747 | __bpf_prog_put(prog, true); | ||
748 | } | ||
736 | EXPORT_SYMBOL_GPL(bpf_prog_put); | 749 | EXPORT_SYMBOL_GPL(bpf_prog_put); |
737 | 750 | ||
738 | static int bpf_prog_release(struct inode *inode, struct file *filp) | 751 | static int bpf_prog_release(struct inode *inode, struct file *filp) |
@@ -814,6 +827,24 @@ struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog) | |||
814 | } | 827 | } |
815 | EXPORT_SYMBOL_GPL(bpf_prog_inc); | 828 | EXPORT_SYMBOL_GPL(bpf_prog_inc); |
816 | 829 | ||
830 | /* prog_idr_lock should have been held */ | ||
831 | static struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog) | ||
832 | { | ||
833 | int refold; | ||
834 | |||
835 | refold = __atomic_add_unless(&prog->aux->refcnt, 1, 0); | ||
836 | |||
837 | if (refold >= BPF_MAX_REFCNT) { | ||
838 | __bpf_prog_put(prog, false); | ||
839 | return ERR_PTR(-EBUSY); | ||
840 | } | ||
841 | |||
842 | if (!refold) | ||
843 | return ERR_PTR(-ENOENT); | ||
844 | |||
845 | return prog; | ||
846 | } | ||
847 | |||
817 | static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) | 848 | static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) |
818 | { | 849 | { |
819 | struct fd f = fdget(ufd); | 850 | struct fd f = fdget(ufd); |
@@ -928,16 +959,21 @@ static int bpf_prog_load(union bpf_attr *attr) | |||
928 | goto free_used_maps; | 959 | goto free_used_maps; |
929 | 960 | ||
930 | err = bpf_prog_new_fd(prog); | 961 | err = bpf_prog_new_fd(prog); |
931 | if (err < 0) | 962 | if (err < 0) { |
932 | /* failed to allocate fd */ | 963 | /* failed to allocate fd. |
933 | goto free_id; | 964 | * bpf_prog_put() is needed because the above |
965 | * bpf_prog_alloc_id() has published the prog | ||
966 | * to the userspace and the userspace may | ||
967 | * have refcnt-ed it through BPF_PROG_GET_FD_BY_ID. | ||
968 | */ | ||
969 | bpf_prog_put(prog); | ||
970 | return err; | ||
971 | } | ||
934 | 972 | ||
935 | bpf_prog_kallsyms_add(prog); | 973 | bpf_prog_kallsyms_add(prog); |
936 | trace_bpf_prog_load(prog, err); | 974 | trace_bpf_prog_load(prog, err); |
937 | return err; | 975 | return err; |
938 | 976 | ||
939 | free_id: | ||
940 | bpf_prog_free_id(prog); | ||
941 | free_used_maps: | 977 | free_used_maps: |
942 | free_used_maps(prog->aux); | 978 | free_used_maps(prog->aux); |
943 | free_prog: | 979 | free_prog: |
@@ -1099,6 +1135,38 @@ static int bpf_obj_get_next_id(const union bpf_attr *attr, | |||
1099 | return err; | 1135 | return err; |
1100 | } | 1136 | } |
1101 | 1137 | ||
1138 | #define BPF_PROG_GET_FD_BY_ID_LAST_FIELD prog_id | ||
1139 | |||
1140 | static int bpf_prog_get_fd_by_id(const union bpf_attr *attr) | ||
1141 | { | ||
1142 | struct bpf_prog *prog; | ||
1143 | u32 id = attr->prog_id; | ||
1144 | int fd; | ||
1145 | |||
1146 | if (CHECK_ATTR(BPF_PROG_GET_FD_BY_ID)) | ||
1147 | return -EINVAL; | ||
1148 | |||
1149 | if (!capable(CAP_SYS_ADMIN)) | ||
1150 | return -EPERM; | ||
1151 | |||
1152 | spin_lock_bh(&prog_idr_lock); | ||
1153 | prog = idr_find(&prog_idr, id); | ||
1154 | if (prog) | ||
1155 | prog = bpf_prog_inc_not_zero(prog); | ||
1156 | else | ||
1157 | prog = ERR_PTR(-ENOENT); | ||
1158 | spin_unlock_bh(&prog_idr_lock); | ||
1159 | |||
1160 | if (IS_ERR(prog)) | ||
1161 | return PTR_ERR(prog); | ||
1162 | |||
1163 | fd = bpf_prog_new_fd(prog); | ||
1164 | if (fd < 0) | ||
1165 | bpf_prog_put(prog); | ||
1166 | |||
1167 | return fd; | ||
1168 | } | ||
1169 | |||
1102 | SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) | 1170 | SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size) |
1103 | { | 1171 | { |
1104 | union bpf_attr attr = {}; | 1172 | union bpf_attr attr = {}; |
@@ -1184,6 +1252,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz | |||
1184 | err = bpf_obj_get_next_id(&attr, uattr, | 1252 | err = bpf_obj_get_next_id(&attr, uattr, |
1185 | &map_idr, &map_idr_lock); | 1253 | &map_idr, &map_idr_lock); |
1186 | break; | 1254 | break; |
1255 | case BPF_PROG_GET_FD_BY_ID: | ||
1256 | err = bpf_prog_get_fd_by_id(&attr); | ||
1257 | break; | ||
1187 | default: | 1258 | default: |
1188 | err = -EINVAL; | 1259 | err = -EINVAL; |
1189 | break; | 1260 | break; |