bpf: Support O_PATH FDs in BPF_OBJ_PIN and BPF_OBJ_GET commands
Current UAPI of BPF_OBJ_PIN and BPF_OBJ_GET commands of bpf() syscall forces users to specify pinning location as a string-based absolute or relative (to current working directory) path. This has various implications related to security (e.g., symlink-based attacks), forces BPF FS to be exposed in the file system, which can cause races with other applications. One of the feedbacks we got from folks working with containers heavily was that inability to use purely FD-based location specification was an unfortunate limitation and hindrance for BPF_OBJ_PIN and BPF_OBJ_GET commands. This patch closes this oversight, adding path_fd field to BPF_OBJ_PIN and BPF_OBJ_GET UAPI, following conventions established by *at() syscalls for dirfd + pathname combinations. This now allows interesting possibilities like working with detached BPF FS mount (e.g., to perform multiple pinnings without running a risk of someone interfering with them), and generally making pinning/getting more secure and not prone to any races and/or security attacks. This is demonstrated by a selftest added in subsequent patch that takes advantage of new mount APIs (fsopen, fsconfig, fsmount) to demonstrate creating detached BPF FS mount, pinning, and then getting BPF map out of it, all while never exposing this private instance of BPF FS to outside worlds. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Reviewed-by: Christian Brauner <brauner@kernel.org> Link: https://lore.kernel.org/bpf/20230523170013.728457-4-andrii@kernel.org
This commit is contained in:
parent
2b001b9407
commit
cb8edce280
@ -2077,8 +2077,8 @@ struct file *bpf_link_new_file(struct bpf_link *link, int *reserved_fd);
|
|||||||
struct bpf_link *bpf_link_get_from_fd(u32 ufd);
|
struct bpf_link *bpf_link_get_from_fd(u32 ufd);
|
||||||
struct bpf_link *bpf_link_get_curr_or_next(u32 *id);
|
struct bpf_link *bpf_link_get_curr_or_next(u32 *id);
|
||||||
|
|
||||||
int bpf_obj_pin_user(u32 ufd, const char __user *pathname);
|
int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname);
|
||||||
int bpf_obj_get_user(const char __user *pathname, int flags);
|
int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags);
|
||||||
|
|
||||||
#define BPF_ITER_FUNC_PREFIX "bpf_iter_"
|
#define BPF_ITER_FUNC_PREFIX "bpf_iter_"
|
||||||
#define DEFINE_BPF_ITER_FUNC(target, args...) \
|
#define DEFINE_BPF_ITER_FUNC(target, args...) \
|
||||||
|
@ -1272,6 +1272,9 @@ enum {
|
|||||||
|
|
||||||
/* Create a map that will be registered/unregesitered by the backed bpf_link */
|
/* Create a map that will be registered/unregesitered by the backed bpf_link */
|
||||||
BPF_F_LINK = (1U << 13),
|
BPF_F_LINK = (1U << 13),
|
||||||
|
|
||||||
|
/* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */
|
||||||
|
BPF_F_PATH_FD = (1U << 14),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Flags for BPF_PROG_QUERY. */
|
/* Flags for BPF_PROG_QUERY. */
|
||||||
@ -1420,6 +1423,13 @@ union bpf_attr {
|
|||||||
__aligned_u64 pathname;
|
__aligned_u64 pathname;
|
||||||
__u32 bpf_fd;
|
__u32 bpf_fd;
|
||||||
__u32 file_flags;
|
__u32 file_flags;
|
||||||
|
/* Same as dirfd in openat() syscall; see openat(2)
|
||||||
|
* manpage for details of path FD and pathname semantics;
|
||||||
|
* path_fd should accompanied by BPF_F_PATH_FD flag set in
|
||||||
|
* file_flags field, otherwise it should be set to zero;
|
||||||
|
* if BPF_F_PATH_FD flag is not set, AT_FDCWD is assumed.
|
||||||
|
*/
|
||||||
|
__s32 path_fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
||||||
|
@ -435,7 +435,7 @@ static int bpf_iter_link_pin_kernel(struct dentry *parent,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_obj_do_pin(const char __user *pathname, void *raw,
|
static int bpf_obj_do_pin(int path_fd, const char __user *pathname, void *raw,
|
||||||
enum bpf_type type)
|
enum bpf_type type)
|
||||||
{
|
{
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
@ -444,7 +444,7 @@ static int bpf_obj_do_pin(const char __user *pathname, void *raw,
|
|||||||
umode_t mode;
|
umode_t mode;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dentry = user_path_create(AT_FDCWD, pathname, &path, 0);
|
dentry = user_path_create(path_fd, pathname, &path, 0);
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return PTR_ERR(dentry);
|
return PTR_ERR(dentry);
|
||||||
|
|
||||||
@ -477,7 +477,7 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bpf_obj_pin_user(u32 ufd, const char __user *pathname)
|
int bpf_obj_pin_user(u32 ufd, int path_fd, const char __user *pathname)
|
||||||
{
|
{
|
||||||
enum bpf_type type;
|
enum bpf_type type;
|
||||||
void *raw;
|
void *raw;
|
||||||
@ -487,14 +487,14 @@ int bpf_obj_pin_user(u32 ufd, const char __user *pathname)
|
|||||||
if (IS_ERR(raw))
|
if (IS_ERR(raw))
|
||||||
return PTR_ERR(raw);
|
return PTR_ERR(raw);
|
||||||
|
|
||||||
ret = bpf_obj_do_pin(pathname, raw, type);
|
ret = bpf_obj_do_pin(path_fd, pathname, raw, type);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
bpf_any_put(raw, type);
|
bpf_any_put(raw, type);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *bpf_obj_do_get(const char __user *pathname,
|
static void *bpf_obj_do_get(int path_fd, const char __user *pathname,
|
||||||
enum bpf_type *type, int flags)
|
enum bpf_type *type, int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
@ -502,7 +502,7 @@ static void *bpf_obj_do_get(const char __user *pathname,
|
|||||||
void *raw;
|
void *raw;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = user_path_at(AT_FDCWD, pathname, LOOKUP_FOLLOW, &path);
|
ret = user_path_at(path_fd, pathname, LOOKUP_FOLLOW, &path);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
|
|
||||||
@ -526,7 +526,7 @@ out:
|
|||||||
return ERR_PTR(ret);
|
return ERR_PTR(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bpf_obj_get_user(const char __user *pathname, int flags)
|
int bpf_obj_get_user(int path_fd, const char __user *pathname, int flags)
|
||||||
{
|
{
|
||||||
enum bpf_type type = BPF_TYPE_UNSPEC;
|
enum bpf_type type = BPF_TYPE_UNSPEC;
|
||||||
int f_flags;
|
int f_flags;
|
||||||
@ -537,7 +537,7 @@ int bpf_obj_get_user(const char __user *pathname, int flags)
|
|||||||
if (f_flags < 0)
|
if (f_flags < 0)
|
||||||
return f_flags;
|
return f_flags;
|
||||||
|
|
||||||
raw = bpf_obj_do_get(pathname, &type, f_flags);
|
raw = bpf_obj_do_get(path_fd, pathname, &type, f_flags);
|
||||||
if (IS_ERR(raw))
|
if (IS_ERR(raw))
|
||||||
return PTR_ERR(raw);
|
return PTR_ERR(raw);
|
||||||
|
|
||||||
|
@ -2697,23 +2697,38 @@ free_prog:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BPF_OBJ_LAST_FIELD file_flags
|
#define BPF_OBJ_LAST_FIELD path_fd
|
||||||
|
|
||||||
static int bpf_obj_pin(const union bpf_attr *attr)
|
static int bpf_obj_pin(const union bpf_attr *attr)
|
||||||
{
|
{
|
||||||
if (CHECK_ATTR(BPF_OBJ) || attr->file_flags != 0)
|
int path_fd;
|
||||||
|
|
||||||
|
if (CHECK_ATTR(BPF_OBJ) || attr->file_flags & ~BPF_F_PATH_FD)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname));
|
/* path_fd has to be accompanied by BPF_F_PATH_FD flag */
|
||||||
|
if (!(attr->file_flags & BPF_F_PATH_FD) && attr->path_fd)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
path_fd = attr->file_flags & BPF_F_PATH_FD ? attr->path_fd : AT_FDCWD;
|
||||||
|
return bpf_obj_pin_user(attr->bpf_fd, path_fd,
|
||||||
|
u64_to_user_ptr(attr->pathname));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bpf_obj_get(const union bpf_attr *attr)
|
static int bpf_obj_get(const union bpf_attr *attr)
|
||||||
{
|
{
|
||||||
|
int path_fd;
|
||||||
|
|
||||||
if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0 ||
|
if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0 ||
|
||||||
attr->file_flags & ~BPF_OBJ_FLAG_MASK)
|
attr->file_flags & ~(BPF_OBJ_FLAG_MASK | BPF_F_PATH_FD))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return bpf_obj_get_user(u64_to_user_ptr(attr->pathname),
|
/* path_fd has to be accompanied by BPF_F_PATH_FD flag */
|
||||||
|
if (!(attr->file_flags & BPF_F_PATH_FD) && attr->path_fd)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
path_fd = attr->file_flags & BPF_F_PATH_FD ? attr->path_fd : AT_FDCWD;
|
||||||
|
return bpf_obj_get_user(path_fd, u64_to_user_ptr(attr->pathname),
|
||||||
attr->file_flags);
|
attr->file_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1272,6 +1272,9 @@ enum {
|
|||||||
|
|
||||||
/* Create a map that will be registered/unregesitered by the backed bpf_link */
|
/* Create a map that will be registered/unregesitered by the backed bpf_link */
|
||||||
BPF_F_LINK = (1U << 13),
|
BPF_F_LINK = (1U << 13),
|
||||||
|
|
||||||
|
/* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */
|
||||||
|
BPF_F_PATH_FD = (1U << 14),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Flags for BPF_PROG_QUERY. */
|
/* Flags for BPF_PROG_QUERY. */
|
||||||
@ -1420,6 +1423,13 @@ union bpf_attr {
|
|||||||
__aligned_u64 pathname;
|
__aligned_u64 pathname;
|
||||||
__u32 bpf_fd;
|
__u32 bpf_fd;
|
||||||
__u32 file_flags;
|
__u32 file_flags;
|
||||||
|
/* Same as dirfd in openat() syscall; see openat(2)
|
||||||
|
* manpage for details of path FD and pathname semantics;
|
||||||
|
* path_fd should accompanied by BPF_F_PATH_FD flag set in
|
||||||
|
* file_flags field, otherwise it should be set to zero;
|
||||||
|
* if BPF_F_PATH_FD flag is not set, AT_FDCWD is assumed.
|
||||||
|
*/
|
||||||
|
__s32 path_fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user