aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Moore <pmoore@redhat.com>2015-01-22 00:00:23 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2015-01-23 00:23:58 -0500
commit55422d0bd292f5ad143cc32cb8bb8505257274c4 (patch)
treefd1d6fae56c5e01d9d8fd6fb2fd913f5e24c7b56
parent57c59f5837bdfd0b4fee3b02a44857e263a09bfa (diff)
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>
-rw-r--r--fs/namei.c29
-rw-r--r--include/linux/audit.h3
-rw-r--r--include/linux/fs.h9
-rw-r--r--kernel/audit.h17
-rw-r--r--kernel/auditsc.c105
5 files changed, 29 insertions, 134 deletions
diff --git a/fs/namei.c b/fs/namei.c
index a3fde77d4abf..96ca11dea4a2 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -118,15 +118,6 @@
118 * POSIX.1 2.4: an empty pathname is invalid (ENOENT). 118 * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
119 * PATH_MAX includes the nul terminator --RR. 119 * PATH_MAX includes the nul terminator --RR.
120 */ 120 */
121void final_putname(struct filename *name)
122{
123 if (name->separate) {
124 __putname(name->name);
125 kfree(name);
126 } else {
127 __putname(name);
128 }
129}
130 121
131#define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) 122#define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename))
132 123
@@ -145,6 +136,7 @@ getname_flags(const char __user *filename, int flags, int *empty)
145 result = __getname(); 136 result = __getname();
146 if (unlikely(!result)) 137 if (unlikely(!result))
147 return ERR_PTR(-ENOMEM); 138 return ERR_PTR(-ENOMEM);
139 result->refcnt = 1;
148 140
149 /* 141 /*
150 * First, try to embed the struct filename inside the names_cache 142 * First, try to embed the struct filename inside the names_cache
@@ -179,6 +171,7 @@ recopy:
179 } 171 }
180 result->name = kname; 172 result->name = kname;
181 result->separate = true; 173 result->separate = true;
174 result->refcnt = 1;
182 max = PATH_MAX; 175 max = PATH_MAX;
183 goto recopy; 176 goto recopy;
184 } 177 }
@@ -202,7 +195,7 @@ recopy:
202 return result; 195 return result;
203 196
204error: 197error:
205 final_putname(result); 198 putname(result);
206 return err; 199 return err;
207} 200}
208 201
@@ -243,19 +236,25 @@ getname_kernel(const char * filename)
243 memcpy((char *)result->name, filename, len); 236 memcpy((char *)result->name, filename, len);
244 result->uptr = NULL; 237 result->uptr = NULL;
245 result->aname = NULL; 238 result->aname = NULL;
239 result->refcnt = 1;
246 audit_getname(result); 240 audit_getname(result);
247 241
248 return result; 242 return result;
249} 243}
250 244
251#ifdef CONFIG_AUDITSYSCALL
252void putname(struct filename *name) 245void putname(struct filename *name)
253{ 246{
254 if (unlikely(!audit_dummy_context())) 247 BUG_ON(name->refcnt <= 0);
255 return audit_putname(name); 248
256 final_putname(name); 249 if (--name->refcnt > 0)
250 return;
251
252 if (name->separate) {
253 __putname(name->name);
254 kfree(name);
255 } else
256 __putname(name);
257} 257}
258#endif
259 258
260static int check_acl(struct inode *inode, int mask) 259static int check_acl(struct inode *inode, int mask)
261{ 260{
diff --git a/include/linux/audit.h b/include/linux/audit.h
index af84234e1f6e..87c2d347d255 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -128,7 +128,6 @@ extern void __audit_syscall_entry(int major, unsigned long a0, unsigned long a1,
128extern void __audit_syscall_exit(int ret_success, long ret_value); 128extern void __audit_syscall_exit(int ret_success, long ret_value);
129extern struct filename *__audit_reusename(const __user char *uptr); 129extern struct filename *__audit_reusename(const __user char *uptr);
130extern void __audit_getname(struct filename *name); 130extern void __audit_getname(struct filename *name);
131extern void audit_putname(struct filename *name);
132 131
133#define AUDIT_INODE_PARENT 1 /* dentry represents the parent */ 132#define AUDIT_INODE_PARENT 1 /* dentry represents the parent */
134#define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */ 133#define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */
@@ -353,8 +352,6 @@ static inline struct filename *audit_reusename(const __user char *name)
353} 352}
354static inline void audit_getname(struct filename *name) 353static inline void audit_getname(struct filename *name)
355{ } 354{ }
356static inline void audit_putname(struct filename *name)
357{ }
358static inline void __audit_inode(struct filename *name, 355static inline void __audit_inode(struct filename *name,
359 const struct dentry *dentry, 356 const struct dentry *dentry,
360 unsigned int flags) 357 unsigned int flags)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f90c0282c114..b49842fe203f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2080,6 +2080,7 @@ struct filename {
2080 const char *name; /* pointer to actual string */ 2080 const char *name; /* pointer to actual string */
2081 const __user char *uptr; /* original userland pointer */ 2081 const __user char *uptr; /* original userland pointer */
2082 struct audit_names *aname; 2082 struct audit_names *aname;
2083 int refcnt;
2083 bool separate; /* should "name" be freed? */ 2084 bool separate; /* should "name" be freed? */
2084}; 2085};
2085 2086
@@ -2101,6 +2102,7 @@ extern int filp_close(struct file *, fl_owner_t id);
2101extern struct filename *getname_flags(const char __user *, int, int *); 2102extern struct filename *getname_flags(const char __user *, int, int *);
2102extern struct filename *getname(const char __user *); 2103extern struct filename *getname(const char __user *);
2103extern struct filename *getname_kernel(const char *); 2104extern struct filename *getname_kernel(const char *);
2105extern void putname(struct filename *name);
2104 2106
2105enum { 2107enum {
2106 FILE_CREATED = 1, 2108 FILE_CREATED = 1,
@@ -2121,15 +2123,8 @@ extern void __init vfs_caches_init(unsigned long);
2121 2123
2122extern struct kmem_cache *names_cachep; 2124extern struct kmem_cache *names_cachep;
2123 2125
2124extern void final_putname(struct filename *name);
2125
2126#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL) 2126#define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL)
2127#define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) 2127#define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
2128#ifndef CONFIG_AUDITSYSCALL
2129#define putname(name) final_putname(name)
2130#else
2131extern void putname(struct filename *name);
2132#endif
2133 2128
2134#ifdef CONFIG_BLOCK 2129#ifdef CONFIG_BLOCK
2135extern int register_blkdev(unsigned int, const char *); 2130extern int register_blkdev(unsigned int, const char *);
diff --git a/kernel/audit.h b/kernel/audit.h
index 3cdffad5a1d9..1caa0d345d90 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -24,12 +24,6 @@
24#include <linux/skbuff.h> 24#include <linux/skbuff.h>
25#include <uapi/linux/mqueue.h> 25#include <uapi/linux/mqueue.h>
26 26
27/* 0 = no checking
28 1 = put_count checking
29 2 = verbose put_count checking
30*/
31#define AUDIT_DEBUG 0
32
33/* AUDIT_NAMES is the number of slots we reserve in the audit_context 27/* AUDIT_NAMES is the number of slots we reserve in the audit_context
34 * for saving names from getname(). If we get more names we will allocate 28 * for saving names from getname(). If we get more names we will allocate
35 * a name dynamically and also add those to the list anchored by names_list. */ 29 * a name dynamically and also add those to the list anchored by names_list. */
@@ -74,9 +68,8 @@ struct audit_cap_data {
74 }; 68 };
75}; 69};
76 70
77/* When fs/namei.c:getname() is called, we store the pointer in name and 71/* When fs/namei.c:getname() is called, we store the pointer in name and bump
78 * we don't let putname() free it (instead we free all of the saved 72 * the refcnt in the associated filename struct.
79 * pointers at syscall exit time).
80 * 73 *
81 * Further, in fs/namei.c:path_lookup() we store the inode and device. 74 * Further, in fs/namei.c:path_lookup() we store the inode and device.
82 */ 75 */
@@ -86,7 +79,6 @@ struct audit_names {
86 struct filename *name; 79 struct filename *name;
87 int name_len; /* number of chars to log */ 80 int name_len; /* number of chars to log */
88 bool hidden; /* don't log this record */ 81 bool hidden; /* don't log this record */
89 bool name_put; /* call __putname()? */
90 82
91 unsigned long ino; 83 unsigned long ino;
92 dev_t dev; 84 dev_t dev;
@@ -208,11 +200,6 @@ struct audit_context {
208 }; 200 };
209 int fds[2]; 201 int fds[2];
210 struct audit_proctitle proctitle; 202 struct audit_proctitle proctitle;
211
212#if AUDIT_DEBUG
213 int put_count;
214 int ino_count;
215#endif
216}; 203};
217 204
218extern u32 audit_ever_enabled; 205extern u32 audit_ever_enabled;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 4f521964ccaa..0c38604a630c 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -866,33 +866,10 @@ static inline void audit_free_names(struct audit_context *context)
866{ 866{
867 struct audit_names *n, *next; 867 struct audit_names *n, *next;
868 868
869#if AUDIT_DEBUG == 2
870 if (context->put_count + context->ino_count != context->name_count) {
871 int i = 0;
872
873 pr_err("%s:%d(:%d): major=%d in_syscall=%d"
874 " name_count=%d put_count=%d ino_count=%d"
875 " [NOT freeing]\n", __FILE__, __LINE__,
876 context->serial, context->major, context->in_syscall,
877 context->name_count, context->put_count,
878 context->ino_count);
879 list_for_each_entry(n, &context->names_list, list) {
880 pr_err("names[%d] = %p = %s\n", i++, n->name,
881 n->name->name ?: "(null)");
882 }
883 dump_stack();
884 return;
885 }
886#endif
887#if AUDIT_DEBUG
888 context->put_count = 0;
889 context->ino_count = 0;
890#endif
891
892 list_for_each_entry_safe(n, next, &context->names_list, list) { 869 list_for_each_entry_safe(n, next, &context->names_list, list) {
893 list_del(&n->list); 870 list_del(&n->list);
894 if (n->name && n->name_put) 871 if (n->name)
895 final_putname(n->name); 872 putname(n->name);
896 if (n->should_free) 873 if (n->should_free)
897 kfree(n); 874 kfree(n);
898 } 875 }
@@ -1711,9 +1688,6 @@ static struct audit_names *audit_alloc_name(struct audit_context *context,
1711 list_add_tail(&aname->list, &context->names_list); 1688 list_add_tail(&aname->list, &context->names_list);
1712 1689
1713 context->name_count++; 1690 context->name_count++;
1714#if AUDIT_DEBUG
1715 context->ino_count++;
1716#endif
1717 return aname; 1691 return aname;
1718} 1692}
1719 1693
@@ -1734,8 +1708,10 @@ __audit_reusename(const __user char *uptr)
1734 list_for_each_entry(n, &context->names_list, list) { 1708 list_for_each_entry(n, &context->names_list, list) {
1735 if (!n->name) 1709 if (!n->name)
1736 continue; 1710 continue;
1737 if (n->name->uptr == uptr) 1711 if (n->name->uptr == uptr) {
1712 n->name->refcnt++;
1738 return n->name; 1713 return n->name;
1714 }
1739 } 1715 }
1740 return NULL; 1716 return NULL;
1741} 1717}
@@ -1752,19 +1728,8 @@ void __audit_getname(struct filename *name)
1752 struct audit_context *context = current->audit_context; 1728 struct audit_context *context = current->audit_context;
1753 struct audit_names *n; 1729 struct audit_names *n;
1754 1730
1755 if (!context->in_syscall) { 1731 if (!context->in_syscall)
1756#if AUDIT_DEBUG == 2
1757 pr_err("%s:%d(:%d): ignoring getname(%p)\n",
1758 __FILE__, __LINE__, context->serial, name);
1759 dump_stack();
1760#endif
1761 return; 1732 return;
1762 }
1763
1764#if AUDIT_DEBUG
1765 /* The filename _must_ have a populated ->name */
1766 BUG_ON(!name->name);
1767#endif
1768 1733
1769 n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); 1734 n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
1770 if (!n) 1735 if (!n)
@@ -1772,56 +1737,13 @@ void __audit_getname(struct filename *name)
1772 1737
1773 n->name = name; 1738 n->name = name;
1774 n->name_len = AUDIT_NAME_FULL; 1739 n->name_len = AUDIT_NAME_FULL;
1775 n->name_put = true;
1776 name->aname = n; 1740 name->aname = n;
1741 name->refcnt++;
1777 1742
1778 if (!context->pwd.dentry) 1743 if (!context->pwd.dentry)
1779 get_fs_pwd(current->fs, &context->pwd); 1744 get_fs_pwd(current->fs, &context->pwd);
1780} 1745}
1781 1746
1782/* audit_putname - intercept a putname request
1783 * @name: name to intercept and delay for putname
1784 *
1785 * If we have stored the name from getname in the audit context,
1786 * then we delay the putname until syscall exit.
1787 * Called from include/linux/fs.h:putname().
1788 */
1789void audit_putname(struct filename *name)
1790{
1791 struct audit_context *context = current->audit_context;
1792
1793 BUG_ON(!context);
1794 if (!name->aname || !context->in_syscall) {
1795#if AUDIT_DEBUG == 2
1796 pr_err("%s:%d(:%d): final_putname(%p)\n",
1797 __FILE__, __LINE__, context->serial, name);
1798 if (context->name_count) {
1799 struct audit_names *n;
1800 int i = 0;
1801
1802 list_for_each_entry(n, &context->names_list, list)
1803 pr_err("name[%d] = %p = %s\n", i++, n->name,
1804 n->name->name ?: "(null)");
1805 }
1806#endif
1807 final_putname(name);
1808 }
1809#if AUDIT_DEBUG
1810 else {
1811 ++context->put_count;
1812 if (context->put_count > context->name_count) {
1813 pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)"
1814 " name_count=%d put_count=%d\n",
1815 __FILE__, __LINE__,
1816 context->serial, context->major,
1817 context->in_syscall, name->name,
1818 context->name_count, context->put_count);
1819 dump_stack();
1820 }
1821 }
1822#endif
1823}
1824
1825/** 1747/**
1826 * __audit_inode - store the inode and device from a lookup 1748 * __audit_inode - store the inode and device from a lookup
1827 * @name: name being audited 1749 * @name: name being audited
@@ -1842,11 +1764,6 @@ void __audit_inode(struct filename *name, const struct dentry *dentry,
1842 if (!name) 1764 if (!name)
1843 goto out_alloc; 1765 goto out_alloc;
1844 1766
1845#if AUDIT_DEBUG
1846 /* The struct filename _must_ have a populated ->name */
1847 BUG_ON(!name->name);
1848#endif
1849
1850 /* 1767 /*
1851 * If we have a pointer to an audit_names entry already, then we can 1768 * If we have a pointer to an audit_names entry already, then we can
1852 * just use it directly if the type is correct. 1769 * just use it directly if the type is correct.
@@ -1893,9 +1810,10 @@ out_alloc:
1893 n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); 1810 n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN);
1894 if (!n) 1811 if (!n)
1895 return; 1812 return;
1896 if (name) 1813 if (name) {
1897 /* no need to set ->name_put as the original will cleanup */
1898 n->name = name; 1814 n->name = name;
1815 name->refcnt++;
1816 }
1899 1817
1900out: 1818out:
1901 if (parent) { 1819 if (parent) {
@@ -2000,8 +1918,7 @@ void __audit_inode_child(const struct inode *parent,
2000 if (found_parent) { 1918 if (found_parent) {
2001 found_child->name = found_parent->name; 1919 found_child->name = found_parent->name;
2002 found_child->name_len = AUDIT_NAME_FULL; 1920 found_child->name_len = AUDIT_NAME_FULL;
2003 /* don't call __putname() */ 1921 found_child->name->refcnt++;
2004 found_child->name_put = false;
2005 } 1922 }
2006 } 1923 }
2007 1924