aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2017-01-18 09:19:54 -0500
committerMiklos Szeredi <mszeredi@redhat.com>2017-01-18 09:19:54 -0500
commit4c7d0c9cb713a28b133b265d595de2a93ee09712 (patch)
treec240721d9ca9339d3bebb3a4bf62112ffb36740e /fs
parent49def1853334396f948dcb4cedb9347abb318df5 (diff)
ovl: fix possible use after free on redirect dir lookup
ovl_lookup_layer() iterates on path elements of d->name.name but also frees and allocates a new pointer for d->name.name. For the case of lookup in upper layer, the initial d->name.name pointer is stable (dentry->d_name), but for lower layers, the initial d->name.name can be d->redirect, which can be freed during iteration. [SzM] Keep the count of remaining characters in the redirect path and calculate the current position from that. This works becuase only the prefix is modified, the ending always stays the same. Fixes: 02b69b284cd7 ("ovl: lookup redirects") Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/overlayfs/namei.c27
1 files changed, 18 insertions, 9 deletions
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index 9ad48d9202a9..023bb0b03352 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -154,29 +154,38 @@ out_err:
154static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d, 154static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
155 struct dentry **ret) 155 struct dentry **ret)
156{ 156{
157 const char *s = d->name.name; 157 /* Counting down from the end, since the prefix can change */
158 size_t rem = d->name.len - 1;
158 struct dentry *dentry = NULL; 159 struct dentry *dentry = NULL;
159 int err; 160 int err;
160 161
161 if (*s != '/') 162 if (d->name.name[0] != '/')
162 return ovl_lookup_single(base, d, d->name.name, d->name.len, 163 return ovl_lookup_single(base, d, d->name.name, d->name.len,
163 0, "", ret); 164 0, "", ret);
164 165
165 while (*s++ == '/' && !IS_ERR_OR_NULL(base) && d_can_lookup(base)) { 166 while (!IS_ERR_OR_NULL(base) && d_can_lookup(base)) {
167 const char *s = d->name.name + d->name.len - rem;
166 const char *next = strchrnul(s, '/'); 168 const char *next = strchrnul(s, '/');
167 size_t slen = strlen(s); 169 size_t thislen = next - s;
170 bool end = !next[0];
168 171
169 if (WARN_ON(slen > d->name.len) || 172 /* Verify we did not go off the rails */
170 WARN_ON(strcmp(d->name.name + d->name.len - slen, s))) 173 if (WARN_ON(s[-1] != '/'))
171 return -EIO; 174 return -EIO;
172 175
173 err = ovl_lookup_single(base, d, s, next - s, 176 err = ovl_lookup_single(base, d, s, thislen,
174 d->name.len - slen, next, &base); 177 d->name.len - rem, next, &base);
175 dput(dentry); 178 dput(dentry);
176 if (err) 179 if (err)
177 return err; 180 return err;
178 dentry = base; 181 dentry = base;
179 s = next; 182 if (end)
183 break;
184
185 rem -= thislen + 1;
186
187 if (WARN_ON(rem >= d->name.len))
188 return -EIO;
180 } 189 }
181 *ret = dentry; 190 *ret = dentry;
182 return 0; 191 return 0;