Skip to content

Commit 1a892b4

Browse files
committed
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs updates from Miklos Szeredi: "This update contains fixes to the "use mounter's permission to access underlying layers" area, and miscellaneous other fixes and cleanups. No new features this time" * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: ovl: use vfs_get_link() vfs: add vfs_get_link() helper ovl: use generic_readlink ovl: explain error values when removing acl from workdir ovl: Fix info leak in ovl_lookup_temp() ovl: during copy up, switch to mounter's creds early ovl: lookup: do getxattr with mounter's permission ovl: copy_up_xattr(): use strnlen
2 parents 5d89d9f + 7764235 commit 1a892b4

6 files changed

Lines changed: 82 additions & 93 deletions

File tree

fs/namei.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4668,6 +4668,31 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
46684668
}
46694669
EXPORT_SYMBOL(generic_readlink);
46704670

4671+
/**
4672+
* vfs_get_link - get symlink body
4673+
* @dentry: dentry on which to get symbolic link
4674+
* @done: caller needs to free returned data with this
4675+
*
4676+
* Calls security hook and i_op->get_link() on the supplied inode.
4677+
*
4678+
* It does not touch atime. That's up to the caller if necessary.
4679+
*
4680+
* Does not work on "special" symlinks like /proc/$$/fd/N
4681+
*/
4682+
const char *vfs_get_link(struct dentry *dentry, struct delayed_call *done)
4683+
{
4684+
const char *res = ERR_PTR(-EINVAL);
4685+
struct inode *inode = d_inode(dentry);
4686+
4687+
if (d_is_symlink(dentry)) {
4688+
res = ERR_PTR(security_inode_readlink(dentry));
4689+
if (!res)
4690+
res = inode->i_op->get_link(dentry, inode, done);
4691+
}
4692+
return res;
4693+
}
4694+
EXPORT_SYMBOL(vfs_get_link);
4695+
46714696
/* get the link contents into pagecache */
46724697
const char *page_get_link(struct dentry *dentry, struct inode *inode,
46734698
struct delayed_call *callback)

fs/overlayfs/copy_up.c

Lines changed: 20 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
5757
ssize_t list_size, size, value_size = 0;
5858
char *buf, *name, *value = NULL;
5959
int uninitialized_var(error);
60+
size_t slen;
6061

6162
if (!(old->d_inode->i_opflags & IOP_XATTR) ||
6263
!(new->d_inode->i_opflags & IOP_XATTR))
@@ -79,7 +80,16 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
7980
goto out;
8081
}
8182

82-
for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
83+
for (name = buf; list_size; name += slen) {
84+
slen = strnlen(name, list_size) + 1;
85+
86+
/* underlying fs providing us with an broken xattr list? */
87+
if (WARN_ON(slen > list_size)) {
88+
error = -EIO;
89+
break;
90+
}
91+
list_size -= slen;
92+
8393
if (ovl_is_private_xattr(name))
8494
continue;
8595
retry:
@@ -174,40 +184,6 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
174184
return error;
175185
}
176186

177-
static char *ovl_read_symlink(struct dentry *realdentry)
178-
{
179-
int res;
180-
char *buf;
181-
struct inode *inode = realdentry->d_inode;
182-
mm_segment_t old_fs;
183-
184-
res = -EINVAL;
185-
if (!inode->i_op->readlink)
186-
goto err;
187-
188-
res = -ENOMEM;
189-
buf = (char *) __get_free_page(GFP_KERNEL);
190-
if (!buf)
191-
goto err;
192-
193-
old_fs = get_fs();
194-
set_fs(get_ds());
195-
/* The cast to a user pointer is valid due to the set_fs() */
196-
res = inode->i_op->readlink(realdentry,
197-
(char __user *)buf, PAGE_SIZE - 1);
198-
set_fs(old_fs);
199-
if (res < 0) {
200-
free_page((unsigned long) buf);
201-
goto err;
202-
}
203-
buf[res] = '\0';
204-
205-
return buf;
206-
207-
err:
208-
return ERR_PTR(res);
209-
}
210-
211187
static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
212188
{
213189
struct iattr attr = {
@@ -354,19 +330,20 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
354330
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
355331
struct path *lowerpath, struct kstat *stat)
356332
{
333+
DEFINE_DELAYED_CALL(done);
357334
struct dentry *workdir = ovl_workdir(dentry);
358335
int err;
359336
struct kstat pstat;
360337
struct path parentpath;
338+
struct dentry *lowerdentry = lowerpath->dentry;
361339
struct dentry *upperdir;
362340
struct dentry *upperdentry;
363-
const struct cred *old_cred;
364-
char *link = NULL;
341+
const char *link = NULL;
365342

366343
if (WARN_ON(!workdir))
367344
return -EROFS;
368345

369-
ovl_do_check_copy_up(lowerpath->dentry);
346+
ovl_do_check_copy_up(lowerdentry);
370347

371348
ovl_path_upper(parent, &parentpath);
372349
upperdir = parentpath.dentry;
@@ -376,13 +353,11 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
376353
return err;
377354

378355
if (S_ISLNK(stat->mode)) {
379-
link = ovl_read_symlink(lowerpath->dentry);
356+
link = vfs_get_link(lowerdentry, &done);
380357
if (IS_ERR(link))
381358
return PTR_ERR(link);
382359
}
383360

384-
old_cred = ovl_override_creds(dentry->d_sb);
385-
386361
err = -EIO;
387362
if (lock_rename(workdir, upperdir) != NULL) {
388363
pr_err("overlayfs: failed to lock workdir+upperdir\n");
@@ -403,19 +378,16 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
403378
}
404379
out_unlock:
405380
unlock_rename(workdir, upperdir);
406-
revert_creds(old_cred);
407-
408-
if (link)
409-
free_page((unsigned long) link);
381+
do_delayed_call(&done);
410382

411383
return err;
412384
}
413385

414386
int ovl_copy_up(struct dentry *dentry)
415387
{
416-
int err;
388+
int err = 0;
389+
const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
417390

418-
err = 0;
419391
while (!err) {
420392
struct dentry *next;
421393
struct dentry *parent;
@@ -447,6 +419,7 @@ int ovl_copy_up(struct dentry *dentry)
447419
dput(parent);
448420
dput(next);
449421
}
422+
revert_creds(old_cred);
450423

451424
return err;
452425
}

fs/overlayfs/dir.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/cred.h>
1515
#include <linux/posix_acl.h>
1616
#include <linux/posix_acl_xattr.h>
17+
#include <linux/atomic.h>
1718
#include "overlayfs.h"
1819

1920
void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@@ -37,8 +38,10 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry)
3738
{
3839
struct dentry *temp;
3940
char name[20];
41+
static atomic_t temp_id = ATOMIC_INIT(0);
4042

41-
snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry);
43+
/* counter is allowed to wrap, since temp dentries are ephemeral */
44+
snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
4245

4346
temp = lookup_one_len(name, workdir, strlen(name));
4447
if (!IS_ERR(temp) && temp->d_inode) {

fs/overlayfs/inode.c

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@ static int ovl_copy_up_truncate(struct dentry *dentry)
1919
struct dentry *parent;
2020
struct kstat stat;
2121
struct path lowerpath;
22+
const struct cred *old_cred;
2223

2324
parent = dget_parent(dentry);
2425
err = ovl_copy_up(parent);
2526
if (err)
2627
goto out_dput_parent;
2728

2829
ovl_path_lower(dentry, &lowerpath);
29-
err = vfs_getattr(&lowerpath, &stat);
30-
if (err)
31-
goto out_dput_parent;
3230

33-
stat.size = 0;
34-
err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
31+
old_cred = ovl_override_creds(dentry->d_sb);
32+
err = vfs_getattr(&lowerpath, &stat);
33+
if (!err) {
34+
stat.size = 0;
35+
err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
36+
}
37+
revert_creds(old_cred);
3538

3639
out_dput_parent:
3740
dput(parent);
@@ -153,45 +156,18 @@ static const char *ovl_get_link(struct dentry *dentry,
153156
struct inode *inode,
154157
struct delayed_call *done)
155158
{
156-
struct dentry *realdentry;
157-
struct inode *realinode;
158159
const struct cred *old_cred;
159160
const char *p;
160161

161162
if (!dentry)
162163
return ERR_PTR(-ECHILD);
163164

164-
realdentry = ovl_dentry_real(dentry);
165-
realinode = realdentry->d_inode;
166-
167-
if (WARN_ON(!realinode->i_op->get_link))
168-
return ERR_PTR(-EPERM);
169-
170165
old_cred = ovl_override_creds(dentry->d_sb);
171-
p = realinode->i_op->get_link(realdentry, realinode, done);
166+
p = vfs_get_link(ovl_dentry_real(dentry), done);
172167
revert_creds(old_cred);
173168
return p;
174169
}
175170

176-
static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
177-
{
178-
struct path realpath;
179-
struct inode *realinode;
180-
const struct cred *old_cred;
181-
int err;
182-
183-
ovl_path_real(dentry, &realpath);
184-
realinode = realpath.dentry->d_inode;
185-
186-
if (!realinode->i_op->readlink)
187-
return -EINVAL;
188-
189-
old_cred = ovl_override_creds(dentry->d_sb);
190-
err = realinode->i_op->readlink(realpath.dentry, buf, bufsiz);
191-
revert_creds(old_cred);
192-
return err;
193-
}
194-
195171
bool ovl_is_private_xattr(const char *name)
196172
{
197173
return strncmp(name, OVL_XATTR_PREFIX,
@@ -375,7 +351,7 @@ static const struct inode_operations ovl_file_inode_operations = {
375351
static const struct inode_operations ovl_symlink_inode_operations = {
376352
.setattr = ovl_setattr,
377353
.get_link = ovl_get_link,
378-
.readlink = ovl_readlink,
354+
.readlink = generic_readlink,
379355
.getattr = ovl_getattr,
380356
.listxattr = ovl_listxattr,
381357
.update_time = ovl_update_time,

fs/overlayfs/super.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,11 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
273273
{
274274
int res;
275275
char val;
276-
struct inode *inode = dentry->d_inode;
277276

278-
if (!S_ISDIR(inode->i_mode) || !(inode->i_opflags & IOP_XATTR))
277+
if (!d_is_dir(dentry))
279278
return false;
280279

281-
res = __vfs_getxattr(dentry, inode, OVL_XATTR_OPAQUE, &val, 1);
280+
res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
282281
if (res == 1 && val == 'y')
283282
return true;
284283

@@ -419,16 +418,12 @@ static bool ovl_dentry_weird(struct dentry *dentry)
419418
DCACHE_OP_COMPARE);
420419
}
421420

422-
static inline struct dentry *ovl_lookup_real(struct super_block *ovl_sb,
423-
struct dentry *dir,
421+
static inline struct dentry *ovl_lookup_real(struct dentry *dir,
424422
const struct qstr *name)
425423
{
426-
const struct cred *old_cred;
427424
struct dentry *dentry;
428425

429-
old_cred = ovl_override_creds(ovl_sb);
430426
dentry = lookup_one_len_unlocked(name->name, dir, name->len);
431-
revert_creds(old_cred);
432427

433428
if (IS_ERR(dentry)) {
434429
if (PTR_ERR(dentry) == -ENOENT)
@@ -469,6 +464,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
469464
unsigned int flags)
470465
{
471466
struct ovl_entry *oe;
467+
const struct cred *old_cred;
472468
struct ovl_entry *poe = dentry->d_parent->d_fsdata;
473469
struct path *stack = NULL;
474470
struct dentry *upperdir, *upperdentry = NULL;
@@ -479,9 +475,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
479475
unsigned int i;
480476
int err;
481477

478+
old_cred = ovl_override_creds(dentry->d_sb);
482479
upperdir = ovl_upperdentry_dereference(poe);
483480
if (upperdir) {
484-
this = ovl_lookup_real(dentry->d_sb, upperdir, &dentry->d_name);
481+
this = ovl_lookup_real(upperdir, &dentry->d_name);
485482
err = PTR_ERR(this);
486483
if (IS_ERR(this))
487484
goto out;
@@ -514,8 +511,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
514511
bool opaque = false;
515512
struct path lowerpath = poe->lowerstack[i];
516513

517-
this = ovl_lookup_real(dentry->d_sb,
518-
lowerpath.dentry, &dentry->d_name);
514+
this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
519515
err = PTR_ERR(this);
520516
if (IS_ERR(this)) {
521517
/*
@@ -588,6 +584,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
588584
ovl_copyattr(realdentry->d_inode, inode);
589585
}
590586

587+
revert_creds(old_cred);
591588
oe->opaque = upperopaque;
592589
oe->__upperdentry = upperdentry;
593590
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
@@ -606,6 +603,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
606603
out_put_upper:
607604
dput(upperdentry);
608605
out:
606+
revert_creds(old_cred);
609607
return ERR_PTR(err);
610608
}
611609

@@ -834,6 +832,19 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
834832
if (err)
835833
goto out_dput;
836834

835+
/*
836+
* Try to remove POSIX ACL xattrs from workdir. We are good if:
837+
*
838+
* a) success (there was a POSIX ACL xattr and was removed)
839+
* b) -ENODATA (there was no POSIX ACL xattr)
840+
* c) -EOPNOTSUPP (POSIX ACL xattrs are not supported)
841+
*
842+
* There are various other error values that could effectively
843+
* mean that the xattr doesn't exist (e.g. -ERANGE is returned
844+
* if the xattr name is too long), but the set of filesystems
845+
* allowed as upper are limited to "normal" ones, where checking
846+
* for the above two errors is sufficient.
847+
*/
837848
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
838849
if (err && err != -ENODATA && err != -EOPNOTSUPP)
839850
goto out_dput;

include/linux/fs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2934,6 +2934,7 @@ extern int vfs_stat(const char __user *, struct kstat *);
29342934
extern int vfs_lstat(const char __user *, struct kstat *);
29352935
extern int vfs_fstat(unsigned int, struct kstat *);
29362936
extern int vfs_fstatat(int , const char __user *, struct kstat *, int);
2937+
extern const char *vfs_get_link(struct dentry *, struct delayed_call *);
29372938

29382939
extern int __generic_block_fiemap(struct inode *inode,
29392940
struct fiemap_extent_info *fieinfo,

0 commit comments

Comments
 (0)