Skip to content

Commit a6fcfe9

Browse files
author
Miklos Szeredi
committed
ovl: make redirect/metacopy rejection consistent
When overlayfs finds a file with metacopy and/or redirect attributes and the metacopy and/or redirect features are not enabled, then it refuses to act on those attributes while also issuing a warning. There was an inconsistency in not checking metacopy found from the index. And also only warning on an upper metacopy if it found the next file on the lower layer, while always warning for metacopy found on a lower layer. Fix these inconsistencies and make the logic more straightforward, paving the way for following patches to change when data redirects are allowed. Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 924577e commit a6fcfe9

File tree

1 file changed

+55
-35
lines changed

1 file changed

+55
-35
lines changed

fs/overlayfs/namei.c

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
struct ovl_lookup_data {
1818
struct super_block *sb;
19+
struct dentry *dentry;
1920
const struct ovl_layer *layer;
2021
struct qstr name;
2122
bool is_dir;
@@ -24,6 +25,7 @@ struct ovl_lookup_data {
2425
bool stop;
2526
bool last;
2627
char *redirect;
28+
char *upperredirect;
2729
int metacopy;
2830
/* Referring to last redirect xattr */
2931
bool absolute_redirect;
@@ -1024,6 +1026,31 @@ int ovl_verify_lowerdata(struct dentry *dentry)
10241026
return ovl_maybe_validate_verity(dentry);
10251027
}
10261028

1029+
/*
1030+
* Following redirects/metacopy can have security consequences: it's like a
1031+
* symlink into the lower layer without the permission checks.
1032+
*
1033+
* This is only a problem if the upper layer is untrusted (e.g comes from an USB
1034+
* drive). This can allow a non-readable file or directory to become readable.
1035+
*
1036+
* Only following redirects when redirects are enabled disables this attack
1037+
* vector when not necessary.
1038+
*/
1039+
static bool ovl_check_follow_redirect(struct ovl_lookup_data *d)
1040+
{
1041+
struct ovl_fs *ofs = OVL_FS(d->sb);
1042+
1043+
if (d->metacopy && !ofs->config.metacopy) {
1044+
pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", d->dentry);
1045+
return false;
1046+
}
1047+
if ((d->redirect || d->upperredirect) && !ovl_redirect_follow(ofs)) {
1048+
pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n", d->dentry);
1049+
return false;
1050+
}
1051+
return true;
1052+
}
1053+
10271054
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10281055
unsigned int flags)
10291056
{
@@ -1039,20 +1066,21 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10391066
unsigned int ctr = 0;
10401067
struct inode *inode = NULL;
10411068
bool upperopaque = false;
1042-
char *upperredirect = NULL;
10431069
struct dentry *this;
10441070
unsigned int i;
10451071
int err;
10461072
bool uppermetacopy = false;
10471073
int metacopy_size = 0;
10481074
struct ovl_lookup_data d = {
10491075
.sb = dentry->d_sb,
1076+
.dentry = dentry,
10501077
.name = dentry->d_name,
10511078
.is_dir = false,
10521079
.opaque = false,
10531080
.stop = false,
10541081
.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
10551082
.redirect = NULL,
1083+
.upperredirect = NULL,
10561084
.metacopy = 0,
10571085
};
10581086

@@ -1094,8 +1122,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
10941122

10951123
if (d.redirect) {
10961124
err = -ENOMEM;
1097-
upperredirect = kstrdup(d.redirect, GFP_KERNEL);
1098-
if (!upperredirect)
1125+
d.upperredirect = kstrdup(d.redirect, GFP_KERNEL);
1126+
if (!d.upperredirect)
10991127
goto out_put_upper;
11001128
if (d.redirect[0] == '/')
11011129
poe = roe;
@@ -1113,6 +1141,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
11131141
for (i = 0; !d.stop && i < ovl_numlower(poe); i++) {
11141142
struct ovl_path lower = ovl_lowerstack(poe)[i];
11151143

1144+
if (!ovl_check_follow_redirect(&d)) {
1145+
err = -EPERM;
1146+
goto out_put;
1147+
}
1148+
11161149
if (!ovl_redirect_follow(ofs))
11171150
d.last = i == ovl_numlower(poe) - 1;
11181151
else if (d.is_dir || !ofs->numdatalayer)
@@ -1126,13 +1159,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
11261159
if (!this)
11271160
continue;
11281161

1129-
if ((uppermetacopy || d.metacopy) && !ofs->config.metacopy) {
1130-
dput(this);
1131-
err = -EPERM;
1132-
pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", dentry);
1133-
goto out_put;
1134-
}
1135-
11361162
/*
11371163
* If no origin fh is stored in upper of a merge dir, store fh
11381164
* of lower dir and set upper parent "impure".
@@ -1185,23 +1211,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
11851211
ctr++;
11861212
}
11871213

1188-
/*
1189-
* Following redirects can have security consequences: it's like
1190-
* a symlink into the lower layer without the permission checks.
1191-
* This is only a problem if the upper layer is untrusted (e.g
1192-
* comes from an USB drive). This can allow a non-readable file
1193-
* or directory to become readable.
1194-
*
1195-
* Only following redirects when redirects are enabled disables
1196-
* this attack vector when not necessary.
1197-
*/
1198-
err = -EPERM;
1199-
if (d.redirect && !ovl_redirect_follow(ofs)) {
1200-
pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n",
1201-
dentry);
1202-
goto out_put;
1203-
}
1204-
12051214
if (d.stop)
12061215
break;
12071216

@@ -1212,6 +1221,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
12121221
}
12131222
}
12141223

1224+
if (!ovl_check_follow_redirect(&d)) {
1225+
err = -EPERM;
1226+
goto out_put;
1227+
}
1228+
12151229
/* Defer lookup of lowerdata in data-only layers to first access */
12161230
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
12171231
d.metacopy = 0;
@@ -1298,28 +1312,34 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
12981312

12991313
/*
13001314
* It's safe to assign upperredirect here: the previous
1301-
* assignment of happens only if upperdentry is non-NULL, and
1315+
* assignment happens only if upperdentry is non-NULL, and
13021316
* this one only if upperdentry is NULL.
13031317
*/
1304-
upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
1305-
if (IS_ERR(upperredirect)) {
1306-
err = PTR_ERR(upperredirect);
1307-
upperredirect = NULL;
1318+
d.upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
1319+
if (IS_ERR(d.upperredirect)) {
1320+
err = PTR_ERR(d.upperredirect);
1321+
d.upperredirect = NULL;
13081322
goto out_free_oe;
13091323
}
1324+
13101325
err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL);
13111326
if (err < 0)
13121327
goto out_free_oe;
1313-
uppermetacopy = err;
1328+
d.metacopy = uppermetacopy = err;
13141329
metacopy_size = err;
1330+
1331+
if (!ovl_check_follow_redirect(&d)) {
1332+
err = -EPERM;
1333+
goto out_free_oe;
1334+
}
13151335
}
13161336

13171337
if (upperdentry || ctr) {
13181338
struct ovl_inode_params oip = {
13191339
.upperdentry = upperdentry,
13201340
.oe = oe,
13211341
.index = index,
1322-
.redirect = upperredirect,
1342+
.redirect = d.upperredirect,
13231343
};
13241344

13251345
/* Store lowerdata redirect for lazy lookup */
@@ -1361,7 +1381,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
13611381
kfree(origin_path);
13621382
}
13631383
dput(upperdentry);
1364-
kfree(upperredirect);
1384+
kfree(d.upperredirect);
13651385
out:
13661386
kfree(d.redirect);
13671387
ovl_revert_creds(old_cred);

0 commit comments

Comments
 (0)