audit: replace getname()/putname() hacks with reference counters
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@zeniv.linux.org.uk CC: linux-fsdevel@vger.kernel.org Signed-off-by: Paul Moore <pmoore@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
105
kernel/auditsc.c
105
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) {
|
||||
@@ -2000,8 +1918,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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user