Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Paul Moore and committed by
Al Viro
55422d0b 57c59f58

+29 -134
+14 -15
fs/namei.c
··· 118 118 * POSIX.1 2.4: an empty pathname is invalid (ENOENT). 119 119 * PATH_MAX includes the nul terminator --RR. 120 120 */ 121 - void 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 122 #define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) 132 123 ··· 136 145 result = __getname(); 137 146 if (unlikely(!result)) 138 147 return ERR_PTR(-ENOMEM); 148 + result->refcnt = 1; 139 149 140 150 /* 141 151 * First, try to embed the struct filename inside the names_cache ··· 171 179 } 172 180 result->name = kname; 173 181 result->separate = true; 182 + result->refcnt = 1; 174 183 max = PATH_MAX; 175 184 goto recopy; 176 185 } ··· 195 202 return result; 196 203 197 204 error: 198 - final_putname(result); 205 + putname(result); 199 206 return err; 200 207 } 201 208 ··· 236 243 memcpy((char *)result->name, filename, len); 237 244 result->uptr = NULL; 238 245 result->aname = NULL; 246 + result->refcnt = 1; 239 247 audit_getname(result); 240 248 241 249 return result; 242 250 } 243 251 244 - #ifdef CONFIG_AUDITSYSCALL 245 252 void putname(struct filename *name) 246 253 { 247 - if (unlikely(!audit_dummy_context())) 248 - return audit_putname(name); 249 - final_putname(name); 254 + BUG_ON(name->refcnt <= 0); 255 + 256 + if (--name->refcnt > 0) 257 + return; 258 + 259 + if (name->separate) { 260 + __putname(name->name); 261 + kfree(name); 262 + } else 263 + __putname(name); 250 264 } 251 - #endif 252 265 253 266 static int check_acl(struct inode *inode, int mask) 254 267 {
-3
include/linux/audit.h
··· 128 128 extern void __audit_syscall_exit(int ret_success, long ret_value); 129 129 extern struct filename *__audit_reusename(const __user char *uptr); 130 130 extern void __audit_getname(struct filename *name); 131 - extern void audit_putname(struct filename *name); 132 131 133 132 #define AUDIT_INODE_PARENT 1 /* dentry represents the parent */ 134 133 #define AUDIT_INODE_HIDDEN 2 /* audit record should be hidden */ ··· 351 352 return NULL; 352 353 } 353 354 static inline void audit_getname(struct filename *name) 354 - { } 355 - static inline void audit_putname(struct filename *name) 356 355 { } 357 356 static inline void __audit_inode(struct filename *name, 358 357 const struct dentry *dentry,
+2 -7
include/linux/fs.h
··· 2080 2080 const char *name; /* pointer to actual string */ 2081 2081 const __user char *uptr; /* original userland pointer */ 2082 2082 struct audit_names *aname; 2083 + int refcnt; 2083 2084 bool separate; /* should "name" be freed? */ 2084 2085 }; 2085 2086 ··· 2102 2101 extern struct filename *getname_flags(const char __user *, int, int *); 2103 2102 extern struct filename *getname(const char __user *); 2104 2103 extern struct filename *getname_kernel(const char *); 2104 + extern void putname(struct filename *name); 2105 2105 2106 2106 enum { 2107 2107 FILE_CREATED = 1, ··· 2123 2121 2124 2122 extern struct kmem_cache *names_cachep; 2125 2123 2126 - extern void final_putname(struct filename *name); 2127 - 2128 2124 #define __getname() kmem_cache_alloc(names_cachep, GFP_KERNEL) 2129 2125 #define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) 2130 - #ifndef CONFIG_AUDITSYSCALL 2131 - #define putname(name) final_putname(name) 2132 - #else 2133 - extern void putname(struct filename *name); 2134 - #endif 2135 2126 2136 2127 #ifdef CONFIG_BLOCK 2137 2128 extern int register_blkdev(unsigned int, const char *);
+2 -15
kernel/audit.h
··· 24 24 #include <linux/skbuff.h> 25 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 27 /* AUDIT_NAMES is the number of slots we reserve in the audit_context 34 28 * for saving names from getname(). If we get more names we will allocate 35 29 * a name dynamically and also add those to the list anchored by names_list. */ ··· 68 74 }; 69 75 }; 70 76 71 - /* When fs/namei.c:getname() is called, we store the pointer in name and 72 - * we don't let putname() free it (instead we free all of the saved 73 - * pointers at syscall exit time). 77 + /* When fs/namei.c:getname() is called, we store the pointer in name and bump 78 + * the refcnt in the associated filename struct. 74 79 * 75 80 * Further, in fs/namei.c:path_lookup() we store the inode and device. 76 81 */ ··· 79 86 struct filename *name; 80 87 int name_len; /* number of chars to log */ 81 88 bool hidden; /* don't log this record */ 82 - bool name_put; /* call __putname()? */ 83 89 84 90 unsigned long ino; 85 91 dev_t dev; ··· 200 208 }; 201 209 int fds[2]; 202 210 struct audit_proctitle proctitle; 203 - 204 - #if AUDIT_DEBUG 205 - int put_count; 206 - int ino_count; 207 - #endif 208 211 }; 209 212 210 213 extern u32 audit_ever_enabled;
+11 -94
kernel/auditsc.c
··· 866 866 { 867 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 869 list_for_each_entry_safe(n, next, &context->names_list, list) { 893 870 list_del(&n->list); 894 - if (n->name && n->name_put) 895 - final_putname(n->name); 871 + if (n->name) 872 + putname(n->name); 896 873 if (n->should_free) 897 874 kfree(n); 898 875 } ··· 1688 1711 list_add_tail(&aname->list, &context->names_list); 1689 1712 1690 1713 context->name_count++; 1691 - #if AUDIT_DEBUG 1692 - context->ino_count++; 1693 - #endif 1694 1714 return aname; 1695 1715 } 1696 1716 ··· 1708 1734 list_for_each_entry(n, &context->names_list, list) { 1709 1735 if (!n->name) 1710 1736 continue; 1711 - if (n->name->uptr == uptr) 1737 + if (n->name->uptr == uptr) { 1738 + n->name->refcnt++; 1712 1739 return n->name; 1740 + } 1713 1741 } 1714 1742 return NULL; 1715 1743 } ··· 1728 1752 struct audit_context *context = current->audit_context; 1729 1753 struct audit_names *n; 1730 1754 1731 - if (!context->in_syscall) { 1732 - #if AUDIT_DEBUG == 2 1733 - pr_err("%s:%d(:%d): ignoring getname(%p)\n", 1734 - __FILE__, __LINE__, context->serial, name); 1735 - dump_stack(); 1736 - #endif 1755 + if (!context->in_syscall) 1737 1756 return; 1738 - } 1739 - 1740 - #if AUDIT_DEBUG 1741 - /* The filename _must_ have a populated ->name */ 1742 - BUG_ON(!name->name); 1743 - #endif 1744 1757 1745 1758 n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); 1746 1759 if (!n) ··· 1737 1772 1738 1773 n->name = name; 1739 1774 n->name_len = AUDIT_NAME_FULL; 1740 - n->name_put = true; 1741 1775 name->aname = n; 1776 + name->refcnt++; 1742 1777 1743 1778 if (!context->pwd.dentry) 1744 1779 get_fs_pwd(current->fs, &context->pwd); 1745 - } 1746 - 1747 - /* audit_putname - intercept a putname request 1748 - * @name: name to intercept and delay for putname 1749 - * 1750 - * If we have stored the name from getname in the audit context, 1751 - * then we delay the putname until syscall exit. 1752 - * Called from include/linux/fs.h:putname(). 1753 - */ 1754 - void audit_putname(struct filename *name) 1755 - { 1756 - struct audit_context *context = current->audit_context; 1757 - 1758 - BUG_ON(!context); 1759 - if (!name->aname || !context->in_syscall) { 1760 - #if AUDIT_DEBUG == 2 1761 - pr_err("%s:%d(:%d): final_putname(%p)\n", 1762 - __FILE__, __LINE__, context->serial, name); 1763 - if (context->name_count) { 1764 - struct audit_names *n; 1765 - int i = 0; 1766 - 1767 - list_for_each_entry(n, &context->names_list, list) 1768 - pr_err("name[%d] = %p = %s\n", i++, n->name, 1769 - n->name->name ?: "(null)"); 1770 - } 1771 - #endif 1772 - final_putname(name); 1773 - } 1774 - #if AUDIT_DEBUG 1775 - else { 1776 - ++context->put_count; 1777 - if (context->put_count > context->name_count) { 1778 - pr_err("%s:%d(:%d): major=%d in_syscall=%d putname(%p)" 1779 - " name_count=%d put_count=%d\n", 1780 - __FILE__, __LINE__, 1781 - context->serial, context->major, 1782 - context->in_syscall, name->name, 1783 - context->name_count, context->put_count); 1784 - dump_stack(); 1785 - } 1786 - } 1787 - #endif 1788 1780 } 1789 1781 1790 1782 /** ··· 1763 1841 1764 1842 if (!name) 1765 1843 goto out_alloc; 1766 - 1767 - #if AUDIT_DEBUG 1768 - /* The struct filename _must_ have a populated ->name */ 1769 - BUG_ON(!name->name); 1770 - #endif 1771 1844 1772 1845 /* 1773 1846 * If we have a pointer to an audit_names entry already, then we can ··· 1810 1893 n = audit_alloc_name(context, AUDIT_TYPE_UNKNOWN); 1811 1894 if (!n) 1812 1895 return; 1813 - if (name) 1814 - /* no need to set ->name_put as the original will cleanup */ 1896 + if (name) { 1815 1897 n->name = name; 1898 + name->refcnt++; 1899 + } 1816 1900 1817 1901 out: 1818 1902 if (parent) { ··· 1918 2000 if (found_parent) { 1919 2001 found_child->name = found_parent->name; 1920 2002 found_child->name_len = AUDIT_NAME_FULL; 1921 - /* don't call __putname() */ 1922 - found_child->name_put = false; 2003 + found_child->name->refcnt++; 1923 2004 } 1924 2005 } 1925 2006