On 15/01/22, Paul Moore wrote: > In order to ensure that filenames are not released before the audit > subsystem is done with the strings there are a number of hacks built > into the fs and audit subsystems around getname() and putname(). To > say these hacks are "ugly" would be kind. > > This patch removes the filename hackery in favor of a more > conventional reference count based approach. The diffstat below tells > most of the story; lots of audit/fs specific code is replaced with a > traditional reference count based approach that is easily understood, > even by those not familiar with the audit and/or fs subsystems. > > CC: viro@xxxxxxxxxxxxxxxxxx > CC: linux-fsdevel@xxxxxxxxxxxxxxx > Signed-off-by: Paul Moore <pmoore@xxxxxxxxxx> Noted change of bumping refcnt before passing back pointer to struct filename. Reviewed-by: Richard Guy Briggs <rgb@xxxxxxxxxx> > --- > fs/namei.c | 29 +++++++------- > include/linux/audit.h | 3 - > include/linux/fs.h | 9 +--- > kernel/audit.h | 17 +------- > kernel/auditsc.c | 105 +++++-------------------------------------------- > 5 files changed, 29 insertions(+), 134 deletions(-) > > diff --git a/fs/namei.c b/fs/namei.c > index e18a2b5..f73e757 100644 > --- a/fs/namei.c > +++ b/fs/namei.c > @@ -117,15 +117,6 @@ > * POSIX.1 2.4: an empty pathname is invalid (ENOENT). > * PATH_MAX includes the nul terminator --RR. > */ > -void final_putname(struct filename *name) > -{ > - if (name->separate) { > - __putname(name->name); > - kfree(name); > - } else { > - __putname(name); > - } > -} > > #define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) > > @@ -144,6 +135,7 @@ getname_flags(const char __user *filename, int flags, int *empty) > result = __getname(); > if (unlikely(!result)) > return ERR_PTR(-ENOMEM); > + result->refcnt = 1; > > /* > * First, try to embed the struct filename inside the names_cache > @@ -178,6 +170,7 @@ recopy: > } > result->name = kname; > result->separate = true; > + result->refcnt = 1; > max = PATH_MAX; > goto recopy; > } > @@ -201,7 +194,7 @@ recopy: > return result; > > error: > - final_putname(result); > + putname(result); > return err; > } > > @@ -242,19 +235,25 @@ getname_kernel(const char * filename) > memcpy((char *)result->name, filename, len); > result->uptr = NULL; > result->aname = NULL; > + result->refcnt = 1; > audit_getname(result); > > return result; > } > > -#ifdef CONFIG_AUDITSYSCALL > void putname(struct filename *name) > { > - if (unlikely(!audit_dummy_context())) > - return audit_putname(name); > - final_putname(name); > + BUG_ON(name->refcnt <= 0); > + > + if (--name->refcnt > 0) > + return; > + > + if (name->separate) { > + __putname(name->name); > + kfree(name); > + } else > + __putname(name); > } > -#endif > > static int check_acl(struct inode *inode, int mask) > { > diff --git a/include/linux/audit.h b/include/linux/audit.h > index b481779..5f2ad5f 100644 > --- a/include/linux/audit.h > +++ b/include/linux/audit.h > @@ -127,7 +127,6 @@ extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1, > extern void __audit_syscall_exit(int ret_success, long ret_value); > extern struct filename *__audit_reusename(const __user char *uptr); > extern void __audit_getname(struct filename *name); > -extern void audit_putname(struct filename *name); > > #define AUDIT_INODE_PARENT 1 /* dentry represents the parent */ > #define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */ > @@ -346,8 +345,6 @@ static inline struct filename *audit_reusename(const __user char *name) > } > static inline void audit_getname(struct filename *name) > { } > -static inline void audit_putname(struct filename *name) > -{ } > static inline void __audit_inode(struct filename *name, > const struct dentry *dentry, > unsigned int flags) > diff --git a/include/linux/fs.h b/include/linux/fs.h > index e11d60c..df7a047 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -2017,6 +2017,7 @@ struct filename { > const char *name; /* pointer to actual string */ > const __user char *uptr; /* original userland pointer */ > struct audit_names *aname; > + int refcnt; > bool separate; /* should "name" be freed? */ > }; > > @@ -2036,6 +2037,7 @@ extern int filp_close(struct file *, fl_owner_t id); > > extern struct filename *getname(const char __user *); > extern struct filename *getname_kernel(const char *); > +extern void putname(struct filename *name); > > enum { > FILE_CREATED = 1, > @@ -2056,15 +2058,8 @@ extern void __init vfs_caches_init(unsigned long); > > extern struct kmem_cache *names_cachep; > > -extern void final_putname(struct filename *name); > - > #define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL) > #define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) > -#ifndef CONFIG_AUDITSYSCALL > -#define putname(name) final_putname(name) > -#else > -extern void putname(struct filename *name); > -#endif > > #ifdef CONFIG_BLOCK > extern int register_blkdev(unsigned int, const char *); > diff --git a/kernel/audit.h b/kernel/audit.h > index 3cdffad..1caa0d3 100644 > --- a/kernel/audit.h > +++ b/kernel/audit.h > @@ -24,12 +24,6 @@ > #include <linux/skbuff.h> > #include <uapi/linux/mqueue.h> > > -/* 0 = no checking > - 1 = put_count checking > - 2 = verbose put_count checking > -*/ > -#define AUDIT_DEBUG 0 > - > /* AUDIT_NAMES is the number of slots we reserve in the audit_context > * for saving names from getname(). If we get more names we will allocate > * a name dynamically and also add those to the list anchored by names_list. */ > @@ -74,9 +68,8 @@ struct audit_cap_data { > }; > }; > > -/* When fs/namei.c:getname() is called, we store the pointer in name and > - * we don't let putname() free it (instead we free all of the saved > - * pointers at syscall exit time). > +/* When fs/namei.c:getname() is called, we store the pointer in name and bump > + * the refcnt in the associated filename struct. > * > * Further, in fs/namei.c:path_lookup() we store the inode and device. > */ > @@ -86,7 +79,6 @@ struct audit_names { > struct filename *name; > int name_len; /* number of chars to log */ > bool hidden; /* don't log this record */ > - bool name_put; /* call __putname()? */ > > unsigned long ino; > dev_t dev; > @@ -208,11 +200,6 @@ struct audit_context { > }; > int fds[2]; > struct audit_proctitle proctitle; > - > -#if AUDIT_DEBUG > - int put_count; > - int ino_count; > -#endif > }; > > extern u32 audit_ever_enabled; > diff --git a/kernel/auditsc.c b/kernel/auditsc.c > index c54b5f0..0a93b71 100644 > --- a/kernel/auditsc.c > +++ b/kernel/auditsc.c > @@ -866,33 +866,10 @@ static inline void audit_free_names(struct audit_context *context) > { > struct audit_names *n, *next; > > -#if AUDIT_DEBUG == 2 > - if (context->put_count + context->ino_count != context->name_count) { > - int i = 0; > - > - pr_err("%s:%d(:%d): major=%d in_syscall=%d" > - " name_count=%d put_count=%d ino_count=%d" > - " [NOT freeing]\n", __FILE__, __LINE__, > - context->serial, context->major, context->in_syscall, > - context->name_count, context->put_count, > - context->ino_count); > - list_for_each_entry(n, &context->names_list, list) { > - pr_err("names[%d] = %p = %s\n", i++, n->name, > - n->name->name ?: "(null)"); > - } > - dump_stack(); > - return; > - } > -#endif > -#if AUDIT_DEBUG > - context->put_count = 0; > - context->ino_count = 0; > -#endif > - > list_for_each_entry_safe(n, next, &context->names_list, list) { > list_del(&n->list); > - if (n->name && n->name_put) > - final_putname(n->name); > + if (n->name) > + putname(n->name); > if (n->should_free) > kfree(n); > } > @@ -1711,9 +1688,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context, > list_add_tail(&aname->list, &context->names_list); > > context->name_count++; > -#if AUDIT_DEBUG > - context->ino_count++; > -#endif > return aname; > } > > @@ -1734,8 +1708,10 @@ __audit_reusename(const __user char *uptr) > list_for_each_entry(n, &context->names_list, list) { > if (!n->name) > continue; > - if (n->name->uptr == uptr) > + if (n->name->uptr == uptr) { > + n->name->refcnt++; > return n->name; > + } > } > return NULL; > } > @@ -1752,19 +1728,8 @@ void __audit_getname(struct filename *name) > struct audit_context *context = current->audit_context; > struct audit_names *n; > > - if (!context->in_syscall) { > -#if AUDIT_DEBUG == 2 > - pr_err("%s:%d(:%d): ignoring getname(%p)\n", > - __FILE__, __LINE__, context->serial, name); > - dump_stack(); > -#endif > + if (!context->in_syscall) > return; > - } > - > -#if AUDIT_DEBUG > - /* The filename _must_ have a populated ->name */ > - BUG_ON(!name->name); > -#endif > > n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); > if (!n) > @@ -1772,56 +1737,13 @@ void __audit_getname(struct filename *name) > > n->name = name; > n->name_len = AUDIT_NAME_FULL; > - n->name_put = true; > name->aname = n; > + name->refcnt++; > > if (!context->pwd.dentry) > get_fs_pwd(current->fs, &context->pwd); > } > > -/* audit_putname - intercept a putname request > - * @name: name to intercept and delay for putname > - * > - * If we have stored the name from getname in the audit context, > - * then we delay the putname until syscall exit. > - * Called from include/linux/fs.h:putname(). > - */ > -void audit_putname(struct filename *name) > -{ > - struct audit_context *context = current->audit_context; > - > - BUG_ON(!context); > - if (!name->aname || !context->in_syscall) { > -#if AUDIT_DEBUG == 2 > - pr_err("%s:%d(:%d): final_putname(%p)\n", > - __FILE__, __LINE__, context->serial, name); > - if (context->name_count) { > - struct audit_names *n; > - int i = 0; > - > - list_for_each_entry(n, &context->names_list, list) > - pr_err("name[%d] = %p = %s\n", i++, n->name, > - n->name->name ?: "(null)"); > - } > -#endif > - final_putname(name); > - } > -#if AUDIT_DEBUG > - else { > - ++context->put_count; > - if (context->put_count > context->name_count) { > - pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)" > - " name_count=%d put_count=%d\n", > - __FILE__, __LINE__, > - context->serial, context->major, > - context->in_syscall, name->name, > - context->name_count, context->put_count); > - dump_stack(); > - } > - } > -#endif > -} > - > /** > * __audit_inode - store the inode and device from a lookup > * @name: name being audited > @@ -1842,11 +1764,6 @@ void __audit_inode(struct filename *name, const struct dentry *dentry, > if (!name) > goto out_alloc; > > -#if AUDIT_DEBUG > - /* The struct filename _must_ have a populated ->name */ > - BUG_ON(!name->name); > -#endif > - > /* > * If we have a pointer to an audit_names entry already, then we can > * just use it directly if the type is correct. > @@ -1893,9 +1810,10 @@ out_alloc: > n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); > if (!n) > return; > - if (name) > - /* no need to set ->name_put as the original will cleanup */ > + if (name) { > n->name = name; > + name->refcnt++; > + } > > out: > if (parent) { > @@ -1995,8 +1913,7 @@ void __audit_inode_child(const struct inode *parent, > if (found_parent) { > found_child->name = found_parent->name; > found_child->name_len = AUDIT_NAME_FULL; > - /* don't call __putname() */ > - found_child->name_put = false; > + found_child->name->refcnt++; > } > } > > - RGB -- Richard Guy Briggs <rbriggs@xxxxxxxxxx> Senior Software Engineer, Kernel Security, AMER ENG Base Operating Systems, Red Hat Remote, Ottawa, Canada Voice: +1.647.777.2635, Internal: (81) 32635, Alt: +1.613.693.0684x3545 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html