struct filename: separate public and private parts ALL pointers to struct filename should be either * ERR_PTR(...) * NULL * or address of ->pubic of some struct __filename instance. All private members moved out of struct filename to containing struct __filename (yes, including refcount). Only two files have any business accessing those - fs/filename.c itself and kernel/auditsc.c; the main reason for this series is to untangle the incestous relationship between those. Let's start with making sure nobody else plays with that stuff; this commit is just a brute-force way to do that. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
diff --git a/fs/filename.c b/fs/filename.c index 0c6609801..294bc89 100644 --- a/fs/filename.c +++ b/fs/filename.c
@@ -14,11 +14,12 @@ * PATH_MAX includes the nul terminator --RR. */ -#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) +#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct __filename, iname)) struct filename * getname_flags(const char __user *filename, int flags) { + struct __filename *full; struct filename *result; char *kname; int len; @@ -27,15 +28,16 @@ getname_flags(const char __user *filename, int flags) if (result) return result; - result = __getname(); - if (unlikely(!result)) + full = __getname(); + if (unlikely(!full)) return ERR_PTR(-ENOMEM); + result = &full->public; /* * First, try to embed the struct filename inside the names_cache * allocation */ - kname = (char *)result->iname; + kname = (char *)full->iname; result->name = kname; len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX); @@ -44,13 +46,13 @@ getname_flags(const char __user *filename, int flags) */ if (unlikely(len <= 0)) { if (unlikely(len < 0)) { - __putname(result); + __putname(full); return ERR_PTR(len); } /* The empty path is special. */ if (!(flags & LOOKUP_EMPTY)) { - __putname(result); + __putname(full); return ERR_PTR(-ENOENT); } } @@ -62,42 +64,43 @@ getname_flags(const char __user *filename, int flags) * userland. */ if (unlikely(len == EMBEDDED_NAME_MAX)) { - const size_t size = offsetof(struct filename, iname[1]); - kname = (char *)result; + const size_t size = offsetof(struct __filename, iname[1]); + kname = (char *)full; /* * size is chosen that way we to guarantee that * result->iname[0] is within the same object and that * kname can't be equal to result->iname, no matter what. */ - result = kzalloc(size, GFP_KERNEL); - if (unlikely(!result)) { + full = kzalloc(size, GFP_KERNEL); + if (unlikely(!full)) { __putname(kname); return ERR_PTR(-ENOMEM); } + result = &full->public; result->name = kname; len = strncpy_from_user(kname, filename, PATH_MAX); if (unlikely(len < 0)) { __putname(kname); - kfree(result); + kfree(full); return ERR_PTR(len); } /* The empty path is special. */ if (unlikely(!len) && !(flags & LOOKUP_EMPTY)) { __putname(kname); - kfree(result); + kfree(full); return ERR_PTR(-ENOENT); } if (unlikely(len == PATH_MAX)) { __putname(kname); - kfree(result); + kfree(full); return ERR_PTR(-ENAMETOOLONG); } } - atomic_set(&result->refcnt, 1); - result->uptr = filename; - result->aname = NULL; + atomic_set(&full->refcnt, 1); + full->uptr = filename; + full->aname = NULL; audit_getname(result); return result; } @@ -135,34 +138,37 @@ struct filename *__getname_maybe_null(const char __user *pathname) struct filename *getname_kernel(const char * filename) { + struct __filename *full; struct filename *result; int len = strlen(filename) + 1; - result = __getname(); - if (unlikely(!result)) + full = __getname(); + if (unlikely(!full)) return ERR_PTR(-ENOMEM); + result = &full->public; if (len <= EMBEDDED_NAME_MAX) { - result->name = (char *)result->iname; + result->name = (char *)full->iname; } else if (len <= PATH_MAX) { - const size_t size = offsetof(struct filename, iname[1]); - struct filename *tmp; + const size_t size = offsetof(struct __filename, iname[1]); + struct __filename *tmp; tmp = kmalloc(size, GFP_KERNEL); if (unlikely(!tmp)) { - __putname(result); + __putname(full); return ERR_PTR(-ENOMEM); } - tmp->name = (char *)result; - result = tmp; + tmp->public.name = (char *)full; + full = tmp; + result = &full->public; } else { - __putname(result); + __putname(full); return ERR_PTR(-ENAMETOOLONG); } memcpy((char *)result->name, filename, len); - result->uptr = NULL; - result->aname = NULL; - atomic_set(&result->refcnt, 1); + full->uptr = NULL; + full->aname = NULL; + atomic_set(&full->refcnt, 1); audit_getname(result); return result; @@ -171,19 +177,22 @@ EXPORT_SYMBOL(getname_kernel); void putname(struct filename *name) { + struct __filename *full; + if (IS_ERR_OR_NULL(name)) return; - if (WARN_ON_ONCE(!atomic_read(&name->refcnt))) + full = __filename_full(name); + if (WARN_ON_ONCE(!atomic_read(&full->refcnt))) return; - if (!atomic_dec_and_test(&name->refcnt)) + if (!atomic_dec_and_test(&full->refcnt)) return; - if (name->name != name->iname) { + if (name->name != full->iname) { __putname(name->name); - kfree(name); + kfree(full); } else - __putname(name); + __putname(full); } EXPORT_SYMBOL(putname);
diff --git a/include/linux/filename.h b/include/linux/filename.h index 3bfef5c..5c3bf63 100644 --- a/include/linux/filename.h +++ b/include/linux/filename.h
@@ -8,11 +8,20 @@ struct audit_names; struct filename { const char *name; /* pointer to actual string */ +}; + +struct __filename { + struct filename public; const __user char *uptr; /* original userland pointer */ atomic_t refcnt; struct audit_names *aname; const char iname[]; }; -static_assert(offsetof(struct filename, iname) % sizeof(long) == 0); +static_assert(offsetof(struct __filename, iname) % sizeof(long) == 0); + +static inline struct __filename *__filename_full(struct filename *name) +{ + return container_of(name, struct __filename, public); +} #endif /* _LINUX_FILENAME_H */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 073ea29..69a0193 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c
@@ -2210,10 +2210,12 @@ __audit_reusename(const __user char *uptr) struct audit_names *n; list_for_each_entry(n, &context->names_list, list) { + struct __filename *name; if (!n->name) continue; - if (n->name->uptr == uptr) { - atomic_inc(&n->name->refcnt); + name = __filename_full(n->name); + if (name->uptr == uptr) { + atomic_inc(&name->refcnt); return n->name; } } @@ -2229,6 +2231,7 @@ __audit_reusename(const __user char *uptr) */ void __audit_getname(struct filename *name) { + struct __filename *full = __filename_full(name); struct audit_context *context = audit_context(); struct audit_names *n; @@ -2241,8 +2244,8 @@ void __audit_getname(struct filename *name) n->name = name; n->name_len = AUDIT_NAME_FULL; - name->aname = n; - atomic_inc(&name->refcnt); + full->aname = n; + atomic_inc(&full->refcnt); } static inline int audit_copy_fcaps(struct audit_names *name, @@ -2330,7 +2333,7 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, * If we have a pointer to an audit_names entry already, then we can * just use it directly if the type is correct. */ - n = name->aname; + n = __filename_full(name)->aname; if (n) { if (parent) { if (n->type == AUDIT_TYPE_PARENT || @@ -2373,8 +2376,9 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, if (!n) return; if (name) { + struct __filename *full = __filename_full(name); n->name = name; - atomic_inc(&name->refcnt); + atomic_inc(&full->refcnt); } out: @@ -2501,7 +2505,7 @@ void __audit_inode_child(struct inode *parent, if (found_parent) { found_child->name = found_parent->name; found_child->name_len = AUDIT_NAME_FULL; - atomic_inc(&found_child->name->refcnt); + atomic_inc(&__filename_full(found_child->name)->refcnt); } }