diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-02-22 20:07:13 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-04-11 22:21:24 -0400 |
commit | fd2f7cb5bcac58b63717cd45366bff9a6ab961c6 (patch) | |
tree | 4271223c22f541510331e806ac178df920d3b69c | |
parent | 6e8a1f8741d6f1179cb3b03991caeac9e4bfd9e2 (diff) |
kill struct filename.separate
just make const char iname[] the last member and compare name->name with
name->iname instead of checking name->separate
We need to make sure that out-of-line name doesn't end up allocated adjacent
to struct filename refering to it; fortunately, it's easy to achieve - just
allocate that struct filename with one byte in ->iname[], so that ->iname[0]
will be inside the same object and thus have an address different from that
of out-of-line name [spotted by Boqun Feng <boqun.feng@gmail.com>]
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/namei.c | 29 | ||||
-rw-r--r-- | include/linux/fs.h | 2 |
2 files changed, 17 insertions, 14 deletions
diff --git a/fs/namei.c b/fs/namei.c index 2c4b68c12550..6218e62a0534 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -119,7 +119,7 @@ | |||
119 | * PATH_MAX includes the nul terminator --RR. | 119 | * PATH_MAX includes the nul terminator --RR. |
120 | */ | 120 | */ |
121 | 121 | ||
122 | #define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) | 122 | #define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) |
123 | 123 | ||
124 | struct filename * | 124 | struct filename * |
125 | getname_flags(const char __user *filename, int flags, int *empty) | 125 | getname_flags(const char __user *filename, int flags, int *empty) |
@@ -140,9 +140,8 @@ getname_flags(const char __user *filename, int flags, int *empty) | |||
140 | * First, try to embed the struct filename inside the names_cache | 140 | * First, try to embed the struct filename inside the names_cache |
141 | * allocation | 141 | * allocation |
142 | */ | 142 | */ |
143 | kname = (char *)result + sizeof(*result); | 143 | kname = (char *)result->iname; |
144 | result->name = kname; | 144 | result->name = kname; |
145 | result->separate = false; | ||
146 | 145 | ||
147 | len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX); | 146 | len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX); |
148 | if (unlikely(len < 0)) { | 147 | if (unlikely(len < 0)) { |
@@ -157,15 +156,20 @@ getname_flags(const char __user *filename, int flags, int *empty) | |||
157 | * userland. | 156 | * userland. |
158 | */ | 157 | */ |
159 | if (unlikely(len == EMBEDDED_NAME_MAX)) { | 158 | if (unlikely(len == EMBEDDED_NAME_MAX)) { |
159 | const size_t size = offsetof(struct filename, iname[1]); | ||
160 | kname = (char *)result; | 160 | kname = (char *)result; |
161 | 161 | ||
162 | result = kzalloc(sizeof(*result), GFP_KERNEL); | 162 | /* |
163 | * size is chosen that way we to guarantee that | ||
164 | * result->iname[0] is within the same object and that | ||
165 | * kname can't be equal to result->iname, no matter what. | ||
166 | */ | ||
167 | result = kzalloc(size, GFP_KERNEL); | ||
163 | if (unlikely(!result)) { | 168 | if (unlikely(!result)) { |
164 | __putname(kname); | 169 | __putname(kname); |
165 | return ERR_PTR(-ENOMEM); | 170 | return ERR_PTR(-ENOMEM); |
166 | } | 171 | } |
167 | result->name = kname; | 172 | result->name = kname; |
168 | result->separate = true; | ||
169 | len = strncpy_from_user(kname, filename, PATH_MAX); | 173 | len = strncpy_from_user(kname, filename, PATH_MAX); |
170 | if (unlikely(len < 0)) { | 174 | if (unlikely(len < 0)) { |
171 | __putname(kname); | 175 | __putname(kname); |
@@ -213,8 +217,7 @@ getname_kernel(const char * filename) | |||
213 | return ERR_PTR(-ENOMEM); | 217 | return ERR_PTR(-ENOMEM); |
214 | 218 | ||
215 | if (len <= EMBEDDED_NAME_MAX) { | 219 | if (len <= EMBEDDED_NAME_MAX) { |
216 | result->name = (char *)(result) + sizeof(*result); | 220 | result->name = (char *)result->iname; |
217 | result->separate = false; | ||
218 | } else if (len <= PATH_MAX) { | 221 | } else if (len <= PATH_MAX) { |
219 | struct filename *tmp; | 222 | struct filename *tmp; |
220 | 223 | ||
@@ -224,7 +227,6 @@ getname_kernel(const char * filename) | |||
224 | return ERR_PTR(-ENOMEM); | 227 | return ERR_PTR(-ENOMEM); |
225 | } | 228 | } |
226 | tmp->name = (char *)result; | 229 | tmp->name = (char *)result; |
227 | tmp->separate = true; | ||
228 | result = tmp; | 230 | result = tmp; |
229 | } else { | 231 | } else { |
230 | __putname(result); | 232 | __putname(result); |
@@ -246,7 +248,7 @@ void putname(struct filename *name) | |||
246 | if (--name->refcnt > 0) | 248 | if (--name->refcnt > 0) |
247 | return; | 249 | return; |
248 | 250 | ||
249 | if (name->separate) { | 251 | if (name->name != name->iname) { |
250 | __putname(name->name); | 252 | __putname(name->name); |
251 | kfree(name); | 253 | kfree(name); |
252 | } else | 254 | } else |
@@ -1852,6 +1854,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, | |||
1852 | struct nameidata *nd) | 1854 | struct nameidata *nd) |
1853 | { | 1855 | { |
1854 | int retval = 0; | 1856 | int retval = 0; |
1857 | const char *s = name->name; | ||
1855 | 1858 | ||
1856 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ | 1859 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ |
1857 | nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; | 1860 | nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; |
@@ -1860,7 +1863,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, | |||
1860 | if (flags & LOOKUP_ROOT) { | 1863 | if (flags & LOOKUP_ROOT) { |
1861 | struct dentry *root = nd->root.dentry; | 1864 | struct dentry *root = nd->root.dentry; |
1862 | struct inode *inode = root->d_inode; | 1865 | struct inode *inode = root->d_inode; |
1863 | if (name->name[0]) { | 1866 | if (*s) { |
1864 | if (!d_can_lookup(root)) | 1867 | if (!d_can_lookup(root)) |
1865 | return -ENOTDIR; | 1868 | return -ENOTDIR; |
1866 | retval = inode_permission(inode, MAY_EXEC); | 1869 | retval = inode_permission(inode, MAY_EXEC); |
@@ -1882,7 +1885,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, | |||
1882 | nd->root.mnt = NULL; | 1885 | nd->root.mnt = NULL; |
1883 | 1886 | ||
1884 | nd->m_seq = read_seqbegin(&mount_lock); | 1887 | nd->m_seq = read_seqbegin(&mount_lock); |
1885 | if (name->name[0] == '/') { | 1888 | if (*s == '/') { |
1886 | if (flags & LOOKUP_RCU) { | 1889 | if (flags & LOOKUP_RCU) { |
1887 | rcu_read_lock(); | 1890 | rcu_read_lock(); |
1888 | nd->seq = set_root_rcu(nd); | 1891 | nd->seq = set_root_rcu(nd); |
@@ -1916,7 +1919,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, | |||
1916 | 1919 | ||
1917 | dentry = f.file->f_path.dentry; | 1920 | dentry = f.file->f_path.dentry; |
1918 | 1921 | ||
1919 | if (name->name[0]) { | 1922 | if (*s) { |
1920 | if (!d_can_lookup(dentry)) { | 1923 | if (!d_can_lookup(dentry)) { |
1921 | fdput(f); | 1924 | fdput(f); |
1922 | return -ENOTDIR; | 1925 | return -ENOTDIR; |
@@ -1946,7 +1949,7 @@ static int path_init(int dfd, const struct filename *name, unsigned int flags, | |||
1946 | return -ECHILD; | 1949 | return -ECHILD; |
1947 | done: | 1950 | done: |
1948 | current->total_link_count = 0; | 1951 | current->total_link_count = 0; |
1949 | return link_path_walk(name->name, nd); | 1952 | return link_path_walk(s, nd); |
1950 | } | 1953 | } |
1951 | 1954 | ||
1952 | static void path_cleanup(struct nameidata *nd) | 1955 | static void path_cleanup(struct nameidata *nd) |
diff --git a/include/linux/fs.h b/include/linux/fs.h index b4d71b5e1ff2..d70e333988f1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -2144,7 +2144,7 @@ struct filename { | |||
2144 | const __user char *uptr; /* original userland pointer */ | 2144 | const __user char *uptr; /* original userland pointer */ |
2145 | struct audit_names *aname; | 2145 | struct audit_names *aname; |
2146 | int refcnt; | 2146 | int refcnt; |
2147 | bool separate; /* should "name" be freed? */ | 2147 | const char iname[]; |
2148 | }; | 2148 | }; |
2149 | 2149 | ||
2150 | extern long vfs_truncate(struct path *, loff_t); | 2150 | extern long vfs_truncate(struct path *, loff_t); |