Skip to content

Commit cfd86ef

Browse files
committed
anon_inode: use a proper mode internally
This allows the VFS to not trip over anonymous inodes and we can add asserts based on the mode into the vfs. When we report it to userspace we can simply hide the mode to avoid regressions. I've audited all direct callers of alloc_anon_inode() and only secretmen overrides i_mode and i_op inode operations but it already uses a regular file. Link: https://lore.kernel.org/[email protected] Fixes: af153bb ("vfs: catch invalid modes in may_open()") Reviewed-by: Jeff Layton <[email protected]> Cc: [email protected] # all LTS kernels Reported-by: [email protected] Closes: https://lore.kernel.org/all/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 418556f commit cfd86ef

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

fs/anon_inodes.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,43 @@
2424

2525
#include <linux/uaccess.h>
2626

27+
#include "internal.h"
28+
2729
static struct vfsmount *anon_inode_mnt __ro_after_init;
2830
static struct inode *anon_inode_inode __ro_after_init;
2931

32+
/*
33+
* User space expects anonymous inodes to have no file type in st_mode.
34+
*
35+
* In particular, 'lsof' has this legacy logic:
36+
*
37+
* type = s->st_mode & S_IFMT;
38+
* switch (type) {
39+
* ...
40+
* case 0:
41+
* if (!strcmp(p, "anon_inode"))
42+
* Lf->ntype = Ntype = N_ANON_INODE;
43+
*
44+
* to detect our old anon_inode logic.
45+
*
46+
* Rather than mess with our internal sane inode data, just fix it
47+
* up here in getattr() by masking off the format bits.
48+
*/
49+
int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
50+
struct kstat *stat, u32 request_mask,
51+
unsigned int query_flags)
52+
{
53+
struct inode *inode = d_inode(path->dentry);
54+
55+
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
56+
stat->mode &= ~S_IFMT;
57+
return 0;
58+
}
59+
60+
static const struct inode_operations anon_inode_operations = {
61+
.getattr = anon_inode_getattr,
62+
};
63+
3064
/*
3165
* anon_inodefs_dname() is called from d_path().
3266
*/
@@ -66,6 +100,7 @@ static struct inode *anon_inode_make_secure_inode(
66100
if (IS_ERR(inode))
67101
return inode;
68102
inode->i_flags &= ~S_PRIVATE;
103+
inode->i_op = &anon_inode_operations;
69104
error = security_inode_init_security_anon(inode, &QSTR(name),
70105
context_inode);
71106
if (error) {
@@ -313,6 +348,7 @@ static int __init anon_inode_init(void)
313348
anon_inode_inode = alloc_anon_inode(anon_inode_mnt->mnt_sb);
314349
if (IS_ERR(anon_inode_inode))
315350
panic("anon_inode_init() inode allocation failed (%ld)\n", PTR_ERR(anon_inode_inode));
351+
anon_inode_inode->i_op = &anon_inode_operations;
316352

317353
return 0;
318354
}

fs/internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,6 @@ static inline bool path_mounted(const struct path *path)
343343
void file_f_owner_release(struct file *file);
344344
bool file_seek_cur_needs_f_lock(struct file *file);
345345
int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map);
346+
int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
347+
struct kstat *stat, u32 request_mask,
348+
unsigned int query_flags);

fs/libfs.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1647,7 +1647,13 @@ struct inode *alloc_anon_inode(struct super_block *s)
16471647
* that it already _is_ on the dirty list.
16481648
*/
16491649
inode->i_state = I_DIRTY;
1650-
inode->i_mode = S_IRUSR | S_IWUSR;
1650+
/*
1651+
* Historically anonymous inodes didn't have a type at all and
1652+
* userspace has come to rely on this. Internally they're just
1653+
* regular files but S_IFREG is masked off when reporting
1654+
* information to userspace.
1655+
*/
1656+
inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
16511657
inode->i_uid = current_fsuid();
16521658
inode->i_gid = current_fsgid();
16531659
inode->i_flags |= S_PRIVATE;

0 commit comments

Comments
 (0)