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 = {};
         * 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)
        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;