bpf: Add BPF_OBJ_GET_INFO_BY_FD
authorMartin KaFai Lau <kafai@fb.com>
Mon, 5 Jun 2017 19:15:52 +0000 (12:15 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 6 Jun 2017 19:41:24 +0000 (15:41 -0400)
A single BPF_OBJ_GET_INFO_BY_FD cmd is used to obtain the info
for both bpf_prog and bpf_map.  The kernel can figure out the
fd is associated with a bpf_prog or bpf_map.

The suggested struct bpf_prog_info and struct bpf_map_info are
not meant to be a complete list and it is not the goal of this patch.
New fields can be added in the future patch.

The focus of this patch is to create the interface,
BPF_OBJ_GET_INFO_BY_FD cmd for exposing the bpf_prog's and
bpf_map's info.

The obj's info, which will be extended (and get bigger) over time, is
separated from the bpf_attr to avoid bloating the bpf_attr.

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>
include/linux/filter.h
include/uapi/linux/bpf.h
kernel/bpf/syscall.c

index 1e2dddf21f3b0de0e930ed95d4cca560ab7c44e2..1fa26dc562cef1345430cb3f0a6d71a10092e1a7 100644 (file)
@@ -69,8 +69,6 @@ struct bpf_prog_aux;
 /* BPF program can access up to 512 bytes of stack space. */
 #define MAX_BPF_STACK  512
 
-#define BPF_TAG_SIZE   8
-
 /* Helper macros for filter block array initializers. */
 
 /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
index dd23f47ff00c3bfc30249b8405d982809f8f6fb2..9b2c10b45733e4dc66d601ac2ea52fed65f3752d 100644 (file)
@@ -86,6 +86,7 @@ enum bpf_cmd {
        BPF_MAP_GET_NEXT_ID,
        BPF_PROG_GET_FD_BY_ID,
        BPF_MAP_GET_FD_BY_ID,
+       BPF_OBJ_GET_INFO_BY_FD,
 };
 
 enum bpf_map_type {
@@ -222,6 +223,12 @@ union bpf_attr {
                };
                __u32           next_id;
        };
+
+       struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
+               __u32           bpf_fd;
+               __u32           info_len;
+               __aligned_u64   info;
+       } info;
 } __attribute__((aligned(8)));
 
 /* BPF helper function descriptions:
@@ -686,4 +693,25 @@ struct xdp_md {
        __u32 data_end;
 };
 
+#define BPF_TAG_SIZE   8
+
+struct bpf_prog_info {
+       __u32 type;
+       __u32 id;
+       __u8  tag[BPF_TAG_SIZE];
+       __u32 jited_prog_len;
+       __u32 xlated_prog_len;
+       __aligned_u64 jited_prog_insns;
+       __aligned_u64 xlated_prog_insns;
+} __attribute__((aligned(8)));
+
+struct bpf_map_info {
+       __u32 type;
+       __u32 id;
+       __u32 key_size;
+       __u32 value_size;
+       __u32 max_entries;
+       __u32 map_flags;
+} __attribute__((aligned(8)));
+
 #endif /* _UAPI__LINUX_BPF_H__ */
index 1802bb9c47d943f65a3e10d6b406872650dbe76b..8942c820d620a15ef38ff6904c0a2f647cdab7be 100644 (file)
@@ -1239,6 +1239,145 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
        return fd;
 }
 
+static int check_uarg_tail_zero(void __user *uaddr,
+                               size_t expected_size,
+                               size_t actual_size)
+{
+       unsigned char __user *addr;
+       unsigned char __user *end;
+       unsigned char val;
+       int err;
+
+       if (actual_size <= expected_size)
+               return 0;
+
+       addr = uaddr + expected_size;
+       end  = uaddr + actual_size;
+
+       for (; addr < end; addr++) {
+               err = get_user(val, addr);
+               if (err)
+                       return err;
+               if (val)
+                       return -E2BIG;
+       }
+
+       return 0;
+}
+
+static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
+                                  const union bpf_attr *attr,
+                                  union bpf_attr __user *uattr)
+{
+       struct bpf_prog_info __user *uinfo = u64_to_user_ptr(attr->info.info);
+       struct bpf_prog_info info = {};
+       u32 info_len = attr->info.info_len;
+       char __user *uinsns;
+       u32 ulen;
+       int err;
+
+       err = check_uarg_tail_zero(uinfo, sizeof(info), info_len);
+       if (err)
+               return err;
+       info_len = min_t(u32, sizeof(info), info_len);
+
+       if (copy_from_user(&info, uinfo, info_len))
+               return err;
+
+       info.type = prog->type;
+       info.id = prog->aux->id;
+
+       memcpy(info.tag, prog->tag, sizeof(prog->tag));
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               info.jited_prog_len = 0;
+               info.xlated_prog_len = 0;
+               goto done;
+       }
+
+       ulen = info.jited_prog_len;
+       info.jited_prog_len = prog->jited_len;
+       if (info.jited_prog_len && ulen) {
+               uinsns = u64_to_user_ptr(info.jited_prog_insns);
+               ulen = min_t(u32, info.jited_prog_len, ulen);
+               if (copy_to_user(uinsns, prog->bpf_func, ulen))
+                       return -EFAULT;
+       }
+
+       ulen = info.xlated_prog_len;
+       info.xlated_prog_len = bpf_prog_size(prog->len);
+       if (info.xlated_prog_len && ulen) {
+               uinsns = u64_to_user_ptr(info.xlated_prog_insns);
+               ulen = min_t(u32, info.xlated_prog_len, ulen);
+               if (copy_to_user(uinsns, prog->insnsi, ulen))
+                       return -EFAULT;
+       }
+
+done:
+       if (copy_to_user(uinfo, &info, info_len) ||
+           put_user(info_len, &uattr->info.info_len))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int bpf_map_get_info_by_fd(struct bpf_map *map,
+                                 const union bpf_attr *attr,
+                                 union bpf_attr __user *uattr)
+{
+       struct bpf_map_info __user *uinfo = u64_to_user_ptr(attr->info.info);
+       struct bpf_map_info info = {};
+       u32 info_len = attr->info.info_len;
+       int err;
+
+       err = check_uarg_tail_zero(uinfo, sizeof(info), info_len);
+       if (err)
+               return err;
+       info_len = min_t(u32, sizeof(info), info_len);
+
+       info.type = map->map_type;
+       info.id = map->id;
+       info.key_size = map->key_size;
+       info.value_size = map->value_size;
+       info.max_entries = map->max_entries;
+       info.map_flags = map->map_flags;
+
+       if (copy_to_user(uinfo, &info, info_len) ||
+           put_user(info_len, &uattr->info.info_len))
+               return -EFAULT;
+
+       return 0;
+}
+
+#define BPF_OBJ_GET_INFO_BY_FD_LAST_FIELD info.info
+
+static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
+                                 union bpf_attr __user *uattr)
+{
+       int ufd = attr->info.bpf_fd;
+       struct fd f;
+       int err;
+
+       if (CHECK_ATTR(BPF_OBJ_GET_INFO_BY_FD))
+               return -EINVAL;
+
+       f = fdget(ufd);
+       if (!f.file)
+               return -EBADFD;
+
+       if (f.file->f_op == &bpf_prog_fops)
+               err = bpf_prog_get_info_by_fd(f.file->private_data, attr,
+                                             uattr);
+       else if (f.file->f_op == &bpf_map_fops)
+               err = bpf_map_get_info_by_fd(f.file->private_data, attr,
+                                            uattr);
+       else
+               err = -EINVAL;
+
+       fdput(f);
+       return err;
+}
+
 SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
 {
        union bpf_attr attr = {};
@@ -1258,23 +1397,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
         * user-space does not rely on any kernel feature
         * extensions we dont know about yet.
         */
-       if (size > sizeof(attr)) {
-               unsigned char __user *addr;
-               unsigned char __user *end;
-               unsigned char val;
-
-               addr = (void __user *)uattr + sizeof(attr);
-               end  = (void __user *)uattr + size;
-
-               for (; addr < end; addr++) {
-                       err = get_user(val, addr);
-                       if (err)
-                               return err;
-                       if (val)
-                               return -E2BIG;
-               }
-               size = sizeof(attr);
-       }
+       err = check_uarg_tail_zero(uattr, sizeof(attr), size);
+       if (err)
+               return err;
+       size = min_t(u32, size, sizeof(attr));
 
        /* copy attributes from user space, may be less than sizeof(bpf_attr) */
        if (copy_from_user(&attr, uattr, size) != 0)
@@ -1330,6 +1456,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
        case BPF_MAP_GET_FD_BY_ID:
                err = bpf_map_get_fd_by_id(&attr);
                break;
+       case BPF_OBJ_GET_INFO_BY_FD:
+               err = bpf_obj_get_info_by_fd(&attr, uattr);
+               break;
        default:
                err = -EINVAL;
                break;