Skip to content

Commit db34015

Browse files
committed
Merge tag 'fsnotify_for_v6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull fsnotify updates from Jan Kara: "Two fanotify cleanups and support for watching namespace-owned filesystems by namespace admins (most useful for being able to watch for new mounts / unmounts happening within a user namespace)" * tag 'fsnotify_for_v6.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: fanotify: support watching filesystems and mounts inside userns fanotify: remove redundant permission checks fanotify: Drop use of flex array in fanotify_fh
2 parents 1193e20 + 58f5fbe commit db34015

File tree

5 files changed

+38
-30
lines changed

5 files changed

+38
-30
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
415415
{
416416
int dwords, type = 0;
417417
char *ext_buf = NULL;
418-
void *buf = fh->buf;
418+
void *buf = fh + 1;
419419
int err;
420420

421421
fh->type = FILEID_ROOT;
@@ -1009,6 +1009,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
10091009

10101010
static void fanotify_free_group_priv(struct fsnotify_group *group)
10111011
{
1012+
put_user_ns(group->user_ns);
10121013
kfree(group->fanotify_data.merge_hash);
10131014
if (group->fanotify_data.ucounts)
10141015
dec_ucount(group->fanotify_data.ucounts,

fs/notify/fanotify/fanotify.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ enum {
2525
* stored in either the first or last 2 dwords.
2626
*/
2727
#define FANOTIFY_INLINE_FH_LEN (3 << 2)
28-
#define FANOTIFY_FH_HDR_LEN offsetof(struct fanotify_fh, buf)
28+
#define FANOTIFY_FH_HDR_LEN sizeof(struct fanotify_fh)
2929

3030
/* Fixed size struct for file handle */
3131
struct fanotify_fh {
@@ -34,7 +34,6 @@ struct fanotify_fh {
3434
#define FANOTIFY_FH_FLAG_EXT_BUF 1
3535
u8 flags;
3636
u8 pad;
37-
unsigned char buf[];
3837
} __aligned(4);
3938

4039
/* Variable size struct for dir file handle + child file handle + name */
@@ -92,7 +91,7 @@ static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh)
9291
BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN % 4);
9392
BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) >
9493
FANOTIFY_INLINE_FH_LEN);
95-
return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *));
94+
return (char **)ALIGN((unsigned long)(fh + 1), __alignof__(char *));
9695
}
9796

9897
static inline void *fanotify_fh_ext_buf(struct fanotify_fh *fh)
@@ -102,7 +101,7 @@ static inline void *fanotify_fh_ext_buf(struct fanotify_fh *fh)
102101

103102
static inline void *fanotify_fh_buf(struct fanotify_fh *fh)
104103
{
105-
return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf;
104+
return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh + 1;
106105
}
107106

108107
static inline int fanotify_info_dir_fh_len(struct fanotify_info *info)
@@ -278,7 +277,7 @@ static inline void fanotify_init_event(struct fanotify_event *event,
278277
#define FANOTIFY_INLINE_FH(name, size) \
279278
struct { \
280279
struct fanotify_fh name; \
281-
/* Space for object_fh.buf[] - access with fanotify_fh_buf() */ \
280+
/* Space for filehandle - access with fanotify_fh_buf() */ \
282281
unsigned char _inline_fh_buf[size]; \
283282
}
284283

fs/notify/fanotify/fanotify_user.c

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
13341334
* A group with FAN_UNLIMITED_MARKS does not contribute to mark count
13351335
* in the limited groups account.
13361336
*/
1337+
BUILD_BUG_ON(!(FANOTIFY_ADMIN_INIT_FLAGS & FAN_UNLIMITED_MARKS));
13371338
if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS) &&
13381339
!inc_ucount(ucounts->ns, ucounts->uid, UCOUNT_FANOTIFY_MARKS))
13391340
return ERR_PTR(-ENOSPC);
@@ -1498,6 +1499,7 @@ static struct hlist_head *fanotify_alloc_merge_hash(void)
14981499
/* fanotify syscalls */
14991500
SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15001501
{
1502+
struct user_namespace *user_ns = current_user_ns();
15011503
struct fsnotify_group *group;
15021504
int f_flags, fd;
15031505
unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
@@ -1512,10 +1514,11 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15121514
/*
15131515
* An unprivileged user can setup an fanotify group with
15141516
* limited functionality - an unprivileged group is limited to
1515-
* notification events with file handles and it cannot use
1516-
* unlimited queue/marks.
1517+
* notification events with file handles or mount ids and it
1518+
* cannot use unlimited queue/marks.
15171519
*/
1518-
if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) || !fid_mode)
1520+
if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) ||
1521+
!(flags & (FANOTIFY_FID_BITS | FAN_REPORT_MNT)))
15191522
return -EPERM;
15201523

15211524
/*
@@ -1594,8 +1597,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
15941597
}
15951598

15961599
/* Enforce groups limits per user in all containing user ns */
1597-
group->fanotify_data.ucounts = inc_ucount(current_user_ns(),
1598-
current_euid(),
1600+
group->fanotify_data.ucounts = inc_ucount(user_ns, current_euid(),
15991601
UCOUNT_FANOTIFY_GROUPS);
16001602
if (!group->fanotify_data.ucounts) {
16011603
fd = -EMFILE;
@@ -1604,6 +1606,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
16041606

16051607
group->fanotify_data.flags = flags | internal_flags;
16061608
group->memcg = get_mem_cgroup_from_mm(current->mm);
1609+
group->user_ns = get_user_ns(user_ns);
16071610

16081611
group->fanotify_data.merge_hash = fanotify_alloc_merge_hash();
16091612
if (!group->fanotify_data.merge_hash) {
@@ -1637,21 +1640,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
16371640
goto out_destroy_group;
16381641
}
16391642

1643+
BUILD_BUG_ON(!(FANOTIFY_ADMIN_INIT_FLAGS & FAN_UNLIMITED_QUEUE));
16401644
if (flags & FAN_UNLIMITED_QUEUE) {
1641-
fd = -EPERM;
1642-
if (!capable(CAP_SYS_ADMIN))
1643-
goto out_destroy_group;
16441645
group->max_events = UINT_MAX;
16451646
} else {
16461647
group->max_events = fanotify_max_queued_events;
16471648
}
16481649

1649-
if (flags & FAN_UNLIMITED_MARKS) {
1650-
fd = -EPERM;
1651-
if (!capable(CAP_SYS_ADMIN))
1652-
goto out_destroy_group;
1653-
}
1654-
16551650
if (flags & FAN_ENABLE_AUDIT) {
16561651
fd = -EPERM;
16571652
if (!capable(CAP_AUDIT_WRITE))
@@ -1811,6 +1806,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
18111806
struct fsnotify_group *group;
18121807
struct path path;
18131808
struct fan_fsid __fsid, *fsid = NULL;
1809+
struct user_namespace *user_ns = NULL;
1810+
struct mnt_namespace *mntns;
18141811
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
18151812
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
18161813
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
@@ -1904,12 +1901,10 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
19041901
}
19051902

19061903
/*
1907-
* An unprivileged user is not allowed to setup mount nor filesystem
1908-
* marks. This also includes setting up such marks by a group that
1909-
* was initialized by an unprivileged user.
1904+
* A user is allowed to setup sb/mount/mntns marks only if it is
1905+
* capable in the user ns where the group was created.
19101906
*/
1911-
if ((!capable(CAP_SYS_ADMIN) ||
1912-
FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV)) &&
1907+
if (!ns_capable(group->user_ns, CAP_SYS_ADMIN) &&
19131908
mark_type != FAN_MARK_INODE)
19141909
return -EPERM;
19151910

@@ -1988,18 +1983,31 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
19881983
fsid = &__fsid;
19891984
}
19901985

1991-
/* inode held in place by reference to path; group by fget on fd */
1986+
/*
1987+
* In addition to being capable in the user ns where group was created,
1988+
* the user also needs to be capable in the user ns associated with
1989+
* the filesystem or in the user ns associated with the mntns
1990+
* (when marking mntns).
1991+
*/
19921992
if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) {
19931993
inode = path.dentry->d_inode;
19941994
obj = inode;
19951995
} else if (obj_type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
1996+
user_ns = path.mnt->mnt_sb->s_user_ns;
19961997
obj = path.mnt;
19971998
} else if (obj_type == FSNOTIFY_OBJ_TYPE_SB) {
1999+
user_ns = path.mnt->mnt_sb->s_user_ns;
19982000
obj = path.mnt->mnt_sb;
19992001
} else if (obj_type == FSNOTIFY_OBJ_TYPE_MNTNS) {
2000-
obj = mnt_ns_from_dentry(path.dentry);
2002+
mntns = mnt_ns_from_dentry(path.dentry);
2003+
user_ns = mntns->user_ns;
2004+
obj = mntns;
20012005
}
20022006

2007+
ret = -EPERM;
2008+
if (user_ns && !ns_capable(user_ns, CAP_SYS_ADMIN))
2009+
goto path_put_and_out;
2010+
20032011
ret = -EINVAL;
20042012
if (!obj)
20052013
goto path_put_and_out;

include/linux/fanotify.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
FAN_REPORT_PIDFD | \
3939
FAN_REPORT_FD_ERROR | \
4040
FAN_UNLIMITED_QUEUE | \
41-
FAN_UNLIMITED_MARKS | \
42-
FAN_REPORT_MNT)
41+
FAN_UNLIMITED_MARKS)
4342

4443
/*
4544
* fanotify_init() flags that are allowed for user without CAP_SYS_ADMIN.
@@ -48,7 +47,7 @@
4847
* so one of the flags for reporting file handles is required.
4948
*/
5049
#define FANOTIFY_USER_INIT_FLAGS (FAN_CLASS_NOTIF | \
51-
FANOTIFY_FID_BITS | \
50+
FANOTIFY_FID_BITS | FAN_REPORT_MNT | \
5251
FAN_CLOEXEC | FAN_NONBLOCK)
5352

5453
#define FANOTIFY_INIT_FLAGS (FANOTIFY_ADMIN_INIT_FLAGS | \

include/linux/fsnotify_backend.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ struct fsnotify_group {
250250
* full */
251251

252252
struct mem_cgroup *memcg; /* memcg to charge allocations */
253+
struct user_namespace *user_ns; /* user ns where group was created */
253254

254255
/* groups can define private fields here or use the void *private */
255256
union {

0 commit comments

Comments
 (0)