* [RFC] odd check in ceph_encode_encrypted_dname()
@ 2025-02-14 2:47 Al Viro
2025-02-14 3:28 ` Al Viro
2025-02-14 15:30 ` Jeff Layton
0 siblings, 2 replies; 21+ messages in thread
From: Al Viro @ 2025-02-14 2:47 UTC (permalink / raw)
To: Luís Henriques; +Cc: ceph-devel, linux-fsdevel, Ilya Dryomov
AFAICS, this
/* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */
WARN_ON(elen > 240);
if ((elen > 0) && (dir != parent)) {
char tmp_buf[NAME_MAX];
elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld",
elen, buf, dir->i_ino);
memcpy(buf, tmp_buf, elen);
}
could drop the (elen > 0) part of the test. elen comes from
elen = ceph_base64_encode(cryptbuf, len, buf);
and that can't return a non-positive unless the second argument is 0 or
above 1G. The latter is flat-out impossible - right before that call
we have
/* hash the end if the name is long enough */
if (len > CEPH_NOHASH_NAME_MAX) {
u8 hash[SHA256_DIGEST_SIZE];
u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
/*
* hash the extra bytes and overwrite crypttext beyond that
* point with it
*/
sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
memcpy(extra, hash, SHA256_DIGEST_SIZE);
len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
}
which obviously caps it with CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE,
i.e. (180 - SHA256_DIGEST_SIZE) + SHA256_DIGEST_SIZE.
The former would have to come from
if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) {
elen = -ENAMETOOLONG;
goto out;
}
and since fscrypt_fname_encrypted_size() must've returned true, we have
len no less than FSCRYPT_FNAME_MIN_MSG_LEN, i.e. it's 16 or greater.
That stuff went into the tree in dd66df0053ef8 "ceph: add support for encrypted
snapshot names" and as far as I can tell, everything above had been applicable
back then too.
Am I missing something subtle here? Can elen be non-positive at that point?
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-14 2:47 [RFC] odd check in ceph_encode_encrypted_dname() Al Viro @ 2025-02-14 3:28 ` Al Viro 2025-02-14 14:05 ` Luis Henriques 2025-02-14 15:41 ` Jeff Layton 2025-02-14 15:30 ` Jeff Layton 1 sibling, 2 replies; 21+ messages in thread From: Al Viro @ 2025-02-14 3:28 UTC (permalink / raw) To: Luís Henriques; +Cc: ceph-devel, linux-fsdevel, Ilya Dryomov On Fri, Feb 14, 2025 at 02:47:56AM +0000, Al Viro wrote: [snip] > Am I missing something subtle here? Can elen be non-positive at that point? Another fun question: for dentries with name of form _<something>_<inumber> we end up looking at fscrypt_has_encryption_key() not for the parent, but for inode with inumber encoded in dentry name. Fair enough, but... what happens if we run into such dentry in ceph_mdsc_build_path()? There the call of ceph_encode_encrypted_fname() is under if (fscrypt_has_encryption_key(d_inode(parent))) Do we need the keys for both? ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-14 3:28 ` Al Viro @ 2025-02-14 14:05 ` Luis Henriques 2025-02-14 15:41 ` Jeff Layton 1 sibling, 0 replies; 21+ messages in thread From: Luis Henriques @ 2025-02-14 14:05 UTC (permalink / raw) To: Al Viro Cc: Jeff Layton, Luís Henriques, ceph-devel, linux-fsdevel, Ilya Dryomov On Fri, Feb 14 2025, Al Viro wrote: > On Fri, Feb 14, 2025 at 02:47:56AM +0000, Al Viro wrote: > > [snip] > >> Am I missing something subtle here? Can elen be non-positive at that point? It has been a while since I last looked into this code, so the details are quite foggy. I don't think you're missing something and that '(elen > 0)' test could be dropped. Unfortunately, I can only tell that through code analysis -- I don't have a test environment anymore where I could try that. > Another fun question: for dentries with name of form _<something>_<inumber> > we end up looking at fscrypt_has_encryption_key() not for the parent, > but for inode with inumber encoded in dentry name. Fair enough, but... > what happens if we run into such dentry in ceph_mdsc_build_path()? > > There the call of ceph_encode_encrypted_fname() is under > if (fscrypt_has_encryption_key(d_inode(parent))) > > Do we need the keys for both? I'm not sure I totally understand your question, but here are my thoughts: if we have the key for the parent, then we *do* have the key for an inode under that encrypted subtree. This is because AFAIR we can not have nested encryption. Thus, the call to ceph_encode_encrypted_fname() *should* be OK. But I'm CC'ing Jeff as he wrote most of the cephfs fscrypt code and he might correct me. Or maybe he has a better memory than I do. Cheers, -- Luís ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-14 3:28 ` Al Viro 2025-02-14 14:05 ` Luis Henriques @ 2025-02-14 15:41 ` Jeff Layton 2025-02-14 16:05 ` Luis Henriques 1 sibling, 1 reply; 21+ messages in thread From: Jeff Layton @ 2025-02-14 15:41 UTC (permalink / raw) To: Al Viro, Luís Henriques; +Cc: ceph-devel, linux-fsdevel, Ilya Dryomov On Fri, 2025-02-14 at 03:28 +0000, Al Viro wrote: > On Fri, Feb 14, 2025 at 02:47:56AM +0000, Al Viro wrote: > > [snip] > > > Am I missing something subtle here? Can elen be non-positive at that point? > > Another fun question: for dentries with name of form _<something>_<inumber> > we end up looking at fscrypt_has_encryption_key() not for the parent, > but for inode with inumber encoded in dentry name. Fair enough, but... > what happens if we run into such dentry in ceph_mdsc_build_path()? > > There the call of ceph_encode_encrypted_fname() is under > if (fscrypt_has_encryption_key(d_inode(parent))) > > Do we need the keys for both? > That sounds like a bug, but I don't fully recall whether snapshots have a special case here for some reason. Let me rephrase Al's question: If I have a snapshot dir that is prefixed with '_', why does it use a different filename encryption key than other snapshot dirs that don't start with that character? My guess here is that this code ought not overwrite "dir" with the result of parse_longname(), but I don't recall the significance of a snapshot name that starts with '_'. /* Handle the special case of snapshot names that start with '_' */ if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && (iname.name[0] == '_')) { dir = parse_longname(parent, iname.name, &name_len); if (IS_ERR(dir)) return PTR_ERR(dir); iname.name++; /* skip initial '_' */ } iname.len = name_len; if (!fscrypt_has_encryption_key(dir)) { memcpy(buf, d_name->name, d_name->len); elen = d_name->len; goto out; } -- Jeff Layton <jlayton@kernel.org> ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-14 15:41 ` Jeff Layton @ 2025-02-14 16:05 ` Luis Henriques 2025-02-15 4:46 ` Al Viro 0 siblings, 1 reply; 21+ messages in thread From: Luis Henriques @ 2025-02-14 16:05 UTC (permalink / raw) To: Jeff Layton; +Cc: Al Viro, ceph-devel, linux-fsdevel, Ilya Dryomov [ Dropping my previous email from the CC list ] On Fri, Feb 14 2025, Jeff Layton wrote: > On Fri, 2025-02-14 at 03:28 +0000, Al Viro wrote: >> On Fri, Feb 14, 2025 at 02:47:56AM +0000, Al Viro wrote: >> >> [snip] >> >> > Am I missing something subtle here? Can elen be non-positive at that point? >> >> Another fun question: for dentries with name of form _<something>_<inumber> >> we end up looking at fscrypt_has_encryption_key() not for the parent, >> but for inode with inumber encoded in dentry name. Fair enough, but... >> what happens if we run into such dentry in ceph_mdsc_build_path()? >> >> There the call of ceph_encode_encrypted_fname() is under >> if (fscrypt_has_encryption_key(d_inode(parent))) >> >> Do we need the keys for both? >> > > That sounds like a bug, but I don't fully recall whether snapshots have > a special case here for some reason. Let me rephrase Al's question: > > If I have a snapshot dir that is prefixed with '_', why does it use a > different filename encryption key than other snapshot dirs that don't > start with that character? > > My guess here is that this code ought not overwrite "dir" with the > result of parse_longname(), but I don't recall the significance of a > snapshot name that starts with '_'. This bit I _think_ I remember. Imagine this tree structure: /mnt/ceph |-- mydir1 |-- mydir2 If you create a snapshot in /mnt/ceph: mkdir /mnt/ceph/.snap/my-snapshot you'll see this: /mnt/ceph |-- .snap | |-- my-snapshot |-- mydir1 | |-- _my-snapshot_1099511627782 |-- mydir2 |-- _my-snapshot_1099511627782 ('1099511627782' is the inode number where the snapshot was created.) So, IIRC, when encrypting the snapshot name (the "my-snapshot" string), you'll use key from the original inode. That's why we need to handle snapshot names starting with '_' differently. And why we have a customized base64 encoding function. Cheers, -- Luís > /* Handle the special case of snapshot names that start with '_' */ > if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && > (iname.name[0] == '_')) { > dir = parse_longname(parent, iname.name, &name_len); > if (IS_ERR(dir)) > return PTR_ERR(dir); > iname.name++; /* skip initial '_' */ > } > iname.len = name_len; > > if (!fscrypt_has_encryption_key(dir)) { > memcpy(buf, d_name->name, d_name->len); > elen = d_name->len; > goto out; > } > > > -- > Jeff Layton <jlayton@kernel.org> > ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-14 16:05 ` Luis Henriques @ 2025-02-15 4:46 ` Al Viro 2025-02-15 4:47 ` [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes Al Viro ` (2 more replies) 0 siblings, 3 replies; 21+ messages in thread From: Al Viro @ 2025-02-15 4:46 UTC (permalink / raw) To: Luis Henriques; +Cc: Jeff Layton, ceph-devel, linux-fsdevel, Ilya Dryomov On Fri, Feb 14, 2025 at 04:05:42PM +0000, Luis Henriques wrote: > So, IIRC, when encrypting the snapshot name (the "my-snapshot" string), > you'll use key from the original inode. That's why we need to handle > snapshot names starting with '_' differently. And why we have a > customized base64 encoding function. OK... The reason I went looking at that thing was the race with rename() that can end up with UAF in ceph_mdsc_build_path(). We copy the plaintext name under ->d_lock, but then we call ceph_encode_encrypted_fname() which passes dentry->d_name to ceph_encode_encrypted_dname() with no locking whatsoever. Have it race with rename and you've got a lot of unpleasantness. The thing is, we can have all ceph_encode_encrypted_dname() put the plaintext name into buf; that eliminates the need to have a separate qstr (or dentry, in case of ceph_encode_encrypted_fname()) argument and simplifies ceph_encode_encrypted_dname() while we are at it. Proposed fix in git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name WARNING: it's completely untested and needs review. It's split in two commits (massage of ceph_encode_encrypted_dname(), then changing the calling conventions); both patches in followups. Please, review. ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes 2025-02-15 4:46 ` Al Viro @ 2025-02-15 4:47 ` Al Viro 2025-02-15 12:41 ` Jeff Layton 2025-02-15 4:47 ` [PATCH 2/2] ceph: fix a race with rename() in ceph_mdsc_build_path() Al Viro 2025-02-15 15:39 ` [RFC] odd check in ceph_encode_encrypted_dname() Luis Henriques 2 siblings, 1 reply; 21+ messages in thread From: Al Viro @ 2025-02-15 4:47 UTC (permalink / raw) To: Luis Henriques; +Cc: Jeff Layton, ceph-devel, linux-fsdevel, Ilya Dryomov ceph_encode_encrypted_dname() would be better off with plaintext name already copied into buffer; we'll lift that into the callers on the next step, which will allow to fix UAF on races with rename; for now copy it in the very beginning of ceph_encode_encrypted_dname(). That has a pleasant side benefit - we don't need to mess with tmp_buf anymore (i.e. that's 256 bytes off the stack footprint). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> --- fs/ceph/crypto.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 3b3c4d8d401e..09346b91215a 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -265,31 +265,28 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, { struct ceph_client *cl = ceph_inode_to_client(parent); struct inode *dir = parent; - struct qstr iname; + char *p = buf; u32 len; int name_len; int elen; int ret; u8 *cryptbuf = NULL; - iname.name = d_name->name; - name_len = d_name->len; + memcpy(buf, d_name->name, d_name->len); + elen = d_name->len; + + name_len = elen; /* Handle the special case of snapshot names that start with '_' */ - if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && - (iname.name[0] == '_')) { - dir = parse_longname(parent, iname.name, &name_len); + if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') { + dir = parse_longname(parent, p, &name_len); if (IS_ERR(dir)) return PTR_ERR(dir); - iname.name++; /* skip initial '_' */ + p++; /* skip initial '_' */ } - iname.len = name_len; - if (!fscrypt_has_encryption_key(dir)) { - memcpy(buf, d_name->name, d_name->len); - elen = d_name->len; + if (!fscrypt_has_encryption_key(dir)) goto out; - } /* * Convert cleartext d_name to ciphertext. If result is longer than @@ -297,7 +294,7 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, * * See: fscrypt_setup_filename */ - if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) { + if (!fscrypt_fname_encrypted_size(dir, name_len, NAME_MAX, &len)) { elen = -ENAMETOOLONG; goto out; } @@ -310,7 +307,9 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, goto out; } - ret = fscrypt_fname_encrypt(dir, &iname, cryptbuf, len); + ret = fscrypt_fname_encrypt(dir, + &(struct qstr)QSTR_INIT(p, name_len), + cryptbuf, len); if (ret) { elen = ret; goto out; @@ -331,18 +330,13 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, } /* base64 encode the encrypted name */ - elen = ceph_base64_encode(cryptbuf, len, buf); - doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, buf); + elen = ceph_base64_encode(cryptbuf, len, p); + doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, p); /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ WARN_ON(elen > 240); - if ((elen > 0) && (dir != parent)) { - char tmp_buf[NAME_MAX]; - - elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", - elen, buf, dir->i_ino); - memcpy(buf, tmp_buf, elen); - } + if (dir != parent) // leading _ is already there; append _<inum> + elen += 1 + sprintf(p + elen, "_%ld", dir->i_ino); out: kfree(cryptbuf); -- 2.39.5 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes 2025-02-15 4:47 ` [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes Al Viro @ 2025-02-15 12:41 ` Jeff Layton 0 siblings, 0 replies; 21+ messages in thread From: Jeff Layton @ 2025-02-15 12:41 UTC (permalink / raw) To: Al Viro, Luis Henriques; +Cc: ceph-devel, linux-fsdevel, Ilya Dryomov On Sat, 2025-02-15 at 04:47 +0000, Al Viro wrote: > ceph_encode_encrypted_dname() would be better off with plaintext name > already copied into buffer; we'll lift that into the callers on the > next step, which will allow to fix UAF on races with rename; for now > copy it in the very beginning of ceph_encode_encrypted_dname(). > > That has a pleasant side benefit - we don't need to mess with tmp_buf > anymore (i.e. that's 256 bytes off the stack footprint). > > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> > --- > fs/ceph/crypto.c | 40 +++++++++++++++++----------------------- > 1 file changed, 17 insertions(+), 23 deletions(-) > > diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c > index 3b3c4d8d401e..09346b91215a 100644 > --- a/fs/ceph/crypto.c > +++ b/fs/ceph/crypto.c > @@ -265,31 +265,28 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > { > struct ceph_client *cl = ceph_inode_to_client(parent); > struct inode *dir = parent; > - struct qstr iname; > + char *p = buf; > u32 len; > int name_len; > int elen; > int ret; > u8 *cryptbuf = NULL; > > - iname.name = d_name->name; > - name_len = d_name->len; > + memcpy(buf, d_name->name, d_name->len); > + elen = d_name->len; > + > + name_len = elen; > > /* Handle the special case of snapshot names that start with '_' */ > - if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && > - (iname.name[0] == '_')) { > - dir = parse_longname(parent, iname.name, &name_len); > + if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') { > + dir = parse_longname(parent, p, &name_len); > if (IS_ERR(dir)) > return PTR_ERR(dir); > - iname.name++; /* skip initial '_' */ > + p++; /* skip initial '_' */ > } > - iname.len = name_len; > > - if (!fscrypt_has_encryption_key(dir)) { > - memcpy(buf, d_name->name, d_name->len); > - elen = d_name->len; > + if (!fscrypt_has_encryption_key(dir)) > goto out; > - } > > /* > * Convert cleartext d_name to ciphertext. If result is longer than > @@ -297,7 +294,7 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > * > * See: fscrypt_setup_filename > */ > - if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) { > + if (!fscrypt_fname_encrypted_size(dir, name_len, NAME_MAX, &len)) { > elen = -ENAMETOOLONG; > goto out; > } > @@ -310,7 +307,9 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > goto out; > } > > - ret = fscrypt_fname_encrypt(dir, &iname, cryptbuf, len); > + ret = fscrypt_fname_encrypt(dir, > + &(struct qstr)QSTR_INIT(p, name_len), > + cryptbuf, len); > if (ret) { > elen = ret; > goto out; > @@ -331,18 +330,13 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > } > > /* base64 encode the encrypted name */ > - elen = ceph_base64_encode(cryptbuf, len, buf); > - doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, buf); > + elen = ceph_base64_encode(cryptbuf, len, p); > + doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, p); > > /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ > WARN_ON(elen > 240); > - if ((elen > 0) && (dir != parent)) { > - char tmp_buf[NAME_MAX]; > - > - elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", > - elen, buf, dir->i_ino); > - memcpy(buf, tmp_buf, elen); > - } > + if (dir != parent) // leading _ is already there; append _<inum> > + elen += 1 + sprintf(p + elen, "_%ld", dir->i_ino); > > out: > kfree(cryptbuf); Reviewed-by: Jeff Layton <jlayton@kernel.org> ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 2/2] ceph: fix a race with rename() in ceph_mdsc_build_path() 2025-02-15 4:46 ` Al Viro 2025-02-15 4:47 ` [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes Al Viro @ 2025-02-15 4:47 ` Al Viro 2025-02-15 12:42 ` Jeff Layton 2025-02-15 15:39 ` [RFC] odd check in ceph_encode_encrypted_dname() Luis Henriques 2 siblings, 1 reply; 21+ messages in thread From: Al Viro @ 2025-02-15 4:47 UTC (permalink / raw) To: Luis Henriques; +Cc: Jeff Layton, ceph-devel, linux-fsdevel, Ilya Dryomov Lift copying the name into callers of ceph_encode_encrypted_dname() that do not have it already copied; ceph_encode_encrypted_fname() disappears. That fixes a UAF in ceph_mdsc_build_path() - while the initial copy of plaintext into buf is done under ->d_lock, we access the original name again in ceph_encode_encrypted_fname() and that is done without any locking. With ceph_encode_encrypted_dname() using the stable copy the problem goes away. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> --- fs/ceph/caps.c | 18 +++++++----------- fs/ceph/crypto.c | 20 +++----------------- fs/ceph/crypto.h | 18 ++++-------------- fs/ceph/dir.c | 7 +++---- fs/ceph/mds_client.c | 4 ++-- 5 files changed, 19 insertions(+), 48 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index a8d8b56cf9d2..b1a8ff612c41 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -4957,24 +4957,20 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry, cl = ceph_inode_to_client(dir); spin_lock(&dentry->d_lock); if (ret && di->lease_session && di->lease_session->s_mds == mds) { + int len = dentry->d_name.len; doutc(cl, "%p mds%d seq %d\n", dentry, mds, (int)di->lease_seq); rel->dname_seq = cpu_to_le32(di->lease_seq); __ceph_mdsc_drop_dentry_lease(dentry); + memcpy(*p, dentry->d_name.name, len); spin_unlock(&dentry->d_lock); if (IS_ENCRYPTED(dir) && fscrypt_has_encryption_key(dir)) { - int ret2 = ceph_encode_encrypted_fname(dir, dentry, *p); - - if (ret2 < 0) - return ret2; - - rel->dname_len = cpu_to_le32(ret2); - *p += ret2; - } else { - rel->dname_len = cpu_to_le32(dentry->d_name.len); - memcpy(*p, dentry->d_name.name, dentry->d_name.len); - *p += dentry->d_name.len; + len = ceph_encode_encrypted_dname(dir, *p, len); + if (len < 0) + return len; } + rel->dname_len = cpu_to_le32(len); + *p += len; } else { spin_unlock(&dentry->d_lock); } diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 09346b91215a..76a4a7e60270 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -260,23 +260,17 @@ static struct inode *parse_longname(const struct inode *parent, return dir; } -int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, - char *buf) +int ceph_encode_encrypted_dname(struct inode *parent, char *buf, int elen) { struct ceph_client *cl = ceph_inode_to_client(parent); struct inode *dir = parent; char *p = buf; u32 len; - int name_len; - int elen; + int name_len = elen; int ret; u8 *cryptbuf = NULL; - memcpy(buf, d_name->name, d_name->len); - elen = d_name->len; - - name_len = elen; - + buf[elen] = '\0'; /* Handle the special case of snapshot names that start with '_' */ if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') { dir = parse_longname(parent, p, &name_len); @@ -349,14 +343,6 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, return elen; } -int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, - char *buf) -{ - WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)); - - return ceph_encode_encrypted_dname(parent, &dentry->d_name, buf); -} - /** * ceph_fname_to_usr - convert a filename for userland presentation * @fname: ceph_fname to be converted diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h index d0768239a1c9..f752bbb2eb06 100644 --- a/fs/ceph/crypto.h +++ b/fs/ceph/crypto.h @@ -102,10 +102,7 @@ int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode, struct ceph_acl_sec_ctx *as); void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as); -int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, - char *buf); -int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, - char *buf); +int ceph_encode_encrypted_dname(struct inode *parent, char *buf, int len); static inline int ceph_fname_alloc_buffer(struct inode *parent, struct fscrypt_str *fname) @@ -194,17 +191,10 @@ static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, { } -static inline int ceph_encode_encrypted_dname(struct inode *parent, - struct qstr *d_name, char *buf) +static inline int ceph_encode_encrypted_dname(struct inode *parent, char *buf, + int len) { - memcpy(buf, d_name->name, d_name->len); - return d_name->len; -} - -static inline int ceph_encode_encrypted_fname(struct inode *parent, - struct dentry *dentry, char *buf) -{ - return -EOPNOTSUPP; + return len; } static inline int ceph_fname_alloc_buffer(struct inode *parent, diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 62e99e65250d..539954e2ea38 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -422,17 +422,16 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) req->r_inode_drop = CEPH_CAP_FILE_EXCL; } if (dfi->last_name) { - struct qstr d_name = { .name = dfi->last_name, - .len = strlen(dfi->last_name) }; + int len = strlen(dfi->last_name); req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL); if (!req->r_path2) { ceph_mdsc_put_request(req); return -ENOMEM; } + memcpy(req->r_path2, dfi->last_name, len); - err = ceph_encode_encrypted_dname(inode, &d_name, - req->r_path2); + err = ceph_encode_encrypted_dname(inode, req->r_path2, len); if (err < 0) { ceph_mdsc_put_request(req); return err; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 54b3421501e9..7dc854631b55 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2766,8 +2766,8 @@ char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, struct dentry *dentry, } if (fscrypt_has_encryption_key(d_inode(parent))) { - len = ceph_encode_encrypted_fname(d_inode(parent), - cur, buf); + len = ceph_encode_encrypted_dname(d_inode(parent), + buf, len); if (len < 0) { dput(parent); dput(cur); -- 2.39.5 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 2/2] ceph: fix a race with rename() in ceph_mdsc_build_path() 2025-02-15 4:47 ` [PATCH 2/2] ceph: fix a race with rename() in ceph_mdsc_build_path() Al Viro @ 2025-02-15 12:42 ` Jeff Layton 0 siblings, 0 replies; 21+ messages in thread From: Jeff Layton @ 2025-02-15 12:42 UTC (permalink / raw) To: Al Viro, Luis Henriques; +Cc: ceph-devel, linux-fsdevel, Ilya Dryomov On Sat, 2025-02-15 at 04:47 +0000, Al Viro wrote: > Lift copying the name into callers of ceph_encode_encrypted_dname() > that do not have it already copied; ceph_encode_encrypted_fname() > disappears. > > That fixes a UAF in ceph_mdsc_build_path() - while the initial copy > of plaintext into buf is done under ->d_lock, we access the > original name again in ceph_encode_encrypted_fname() and that is > done without any locking. With ceph_encode_encrypted_dname() using > the stable copy the problem goes away. > > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> > --- > fs/ceph/caps.c | 18 +++++++----------- > fs/ceph/crypto.c | 20 +++----------------- > fs/ceph/crypto.h | 18 ++++-------------- > fs/ceph/dir.c | 7 +++---- > fs/ceph/mds_client.c | 4 ++-- > 5 files changed, 19 insertions(+), 48 deletions(-) > > diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c > index a8d8b56cf9d2..b1a8ff612c41 100644 > --- a/fs/ceph/caps.c > +++ b/fs/ceph/caps.c > @@ -4957,24 +4957,20 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry, > cl = ceph_inode_to_client(dir); > spin_lock(&dentry->d_lock); > if (ret && di->lease_session && di->lease_session->s_mds == mds) { > + int len = dentry->d_name.len; > doutc(cl, "%p mds%d seq %d\n", dentry, mds, > (int)di->lease_seq); > rel->dname_seq = cpu_to_le32(di->lease_seq); > __ceph_mdsc_drop_dentry_lease(dentry); > + memcpy(*p, dentry->d_name.name, len); > spin_unlock(&dentry->d_lock); > if (IS_ENCRYPTED(dir) && fscrypt_has_encryption_key(dir)) { > - int ret2 = ceph_encode_encrypted_fname(dir, dentry, *p); > - > - if (ret2 < 0) > - return ret2; > - > - rel->dname_len = cpu_to_le32(ret2); > - *p += ret2; > - } else { > - rel->dname_len = cpu_to_le32(dentry->d_name.len); > - memcpy(*p, dentry->d_name.name, dentry->d_name.len); > - *p += dentry->d_name.len; > + len = ceph_encode_encrypted_dname(dir, *p, len); > + if (len < 0) > + return len; > } > + rel->dname_len = cpu_to_le32(len); > + *p += len; > } else { > spin_unlock(&dentry->d_lock); > } > diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c > index 09346b91215a..76a4a7e60270 100644 > --- a/fs/ceph/crypto.c > +++ b/fs/ceph/crypto.c > @@ -260,23 +260,17 @@ static struct inode *parse_longname(const struct inode *parent, > return dir; > } > > -int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > - char *buf) > +int ceph_encode_encrypted_dname(struct inode *parent, char *buf, int elen) > { > struct ceph_client *cl = ceph_inode_to_client(parent); > struct inode *dir = parent; > char *p = buf; > u32 len; > - int name_len; > - int elen; > + int name_len = elen; > int ret; > u8 *cryptbuf = NULL; > > - memcpy(buf, d_name->name, d_name->len); > - elen = d_name->len; > - > - name_len = elen; > - > + buf[elen] = '\0'; > /* Handle the special case of snapshot names that start with '_' */ > if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') { > dir = parse_longname(parent, p, &name_len); > @@ -349,14 +343,6 @@ int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > return elen; > } > > -int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, > - char *buf) > -{ > - WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)); > - > - return ceph_encode_encrypted_dname(parent, &dentry->d_name, buf); > -} > - > /** > * ceph_fname_to_usr - convert a filename for userland presentation > * @fname: ceph_fname to be converted > diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h > index d0768239a1c9..f752bbb2eb06 100644 > --- a/fs/ceph/crypto.h > +++ b/fs/ceph/crypto.h > @@ -102,10 +102,7 @@ int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode, > struct ceph_acl_sec_ctx *as); > void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, > struct ceph_acl_sec_ctx *as); > -int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, > - char *buf); > -int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, > - char *buf); > +int ceph_encode_encrypted_dname(struct inode *parent, char *buf, int len); > > static inline int ceph_fname_alloc_buffer(struct inode *parent, > struct fscrypt_str *fname) > @@ -194,17 +191,10 @@ static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, > { > } > > -static inline int ceph_encode_encrypted_dname(struct inode *parent, > - struct qstr *d_name, char *buf) > +static inline int ceph_encode_encrypted_dname(struct inode *parent, char *buf, > + int len) > { > - memcpy(buf, d_name->name, d_name->len); > - return d_name->len; > -} > - > -static inline int ceph_encode_encrypted_fname(struct inode *parent, > - struct dentry *dentry, char *buf) > -{ > - return -EOPNOTSUPP; > + return len; > } > > static inline int ceph_fname_alloc_buffer(struct inode *parent, > diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c > index 62e99e65250d..539954e2ea38 100644 > --- a/fs/ceph/dir.c > +++ b/fs/ceph/dir.c > @@ -422,17 +422,16 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) > req->r_inode_drop = CEPH_CAP_FILE_EXCL; > } > if (dfi->last_name) { > - struct qstr d_name = { .name = dfi->last_name, > - .len = strlen(dfi->last_name) }; > + int len = strlen(dfi->last_name); > > req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL); > if (!req->r_path2) { > ceph_mdsc_put_request(req); > return -ENOMEM; > } > + memcpy(req->r_path2, dfi->last_name, len); > > - err = ceph_encode_encrypted_dname(inode, &d_name, > - req->r_path2); > + err = ceph_encode_encrypted_dname(inode, req->r_path2, len); > if (err < 0) { > ceph_mdsc_put_request(req); > return err; > diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c > index 54b3421501e9..7dc854631b55 100644 > --- a/fs/ceph/mds_client.c > +++ b/fs/ceph/mds_client.c > @@ -2766,8 +2766,8 @@ char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, struct dentry *dentry, > } > > if (fscrypt_has_encryption_key(d_inode(parent))) { > - len = ceph_encode_encrypted_fname(d_inode(parent), > - cur, buf); > + len = ceph_encode_encrypted_dname(d_inode(parent), > + buf, len); > if (len < 0) { > dput(parent); > dput(cur); Reviewed-by: Jeff Layton <jlayton@kernel.org> ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-15 4:46 ` Al Viro 2025-02-15 4:47 ` [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes Al Viro 2025-02-15 4:47 ` [PATCH 2/2] ceph: fix a race with rename() in ceph_mdsc_build_path() Al Viro @ 2025-02-15 15:39 ` Luis Henriques 2025-02-17 17:56 ` Viacheslav Dubeyko 2 siblings, 1 reply; 21+ messages in thread From: Luis Henriques @ 2025-02-15 15:39 UTC (permalink / raw) To: Al Viro Cc: Viacheslav Dubeyko, Jeff Layton, ceph-devel, linux-fsdevel, Ilya Dryomov On Sat, Feb 15 2025, Al Viro wrote: > On Fri, Feb 14, 2025 at 04:05:42PM +0000, Luis Henriques wrote: > >> So, IIRC, when encrypting the snapshot name (the "my-snapshot" string), >> you'll use key from the original inode. That's why we need to handle >> snapshot names starting with '_' differently. And why we have a >> customized base64 encoding function. > > OK... The reason I went looking at that thing was the race with rename() > that can end up with UAF in ceph_mdsc_build_path(). > > We copy the plaintext name under ->d_lock, but then we call > ceph_encode_encrypted_fname() which passes dentry->d_name to > ceph_encode_encrypted_dname() with no locking whatsoever. > > Have it race with rename and you've got a lot of unpleasantness. > > The thing is, we can have all ceph_encode_encrypted_dname() put the > plaintext name into buf; that eliminates the need to have a separate > qstr (or dentry, in case of ceph_encode_encrypted_fname()) argument and > simplifies ceph_encode_encrypted_dname() while we are at it. > > Proposed fix in git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name > > WARNING: it's completely untested and needs review. It's split in two commits > (massage of ceph_encode_encrypted_dname(), then changing the calling conventions); > both patches in followups. > > Please, review. I've reviewed both patches and they seem to be OK, so feel free to add my Reviewed-by: Luis Henriques <luis@igalia.com> But as I said, I don't have a test environment at the moment. I'm adding Slava to CC with the hope that he may be able to run some fscrypt-specific tests (including snapshots creation) against these patches. Cheers, -- Luís ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-15 15:39 ` [RFC] odd check in ceph_encode_encrypted_dname() Luis Henriques @ 2025-02-17 17:56 ` Viacheslav Dubeyko 2025-02-17 18:48 ` Luis Henriques 0 siblings, 1 reply; 21+ messages in thread From: Viacheslav Dubeyko @ 2025-02-17 17:56 UTC (permalink / raw) To: luis@igalia.com, viro@zeniv.linux.org.uk Cc: ceph-devel@vger.kernel.org, jlayton@kernel.org, linux-fsdevel@vger.kernel.org, idryomov@gmail.com On Sat, 2025-02-15 at 15:39 +0000, Luis Henriques wrote: > On Sat, Feb 15 2025, Al Viro wrote: > > > On Fri, Feb 14, 2025 at 04:05:42PM +0000, Luis Henriques wrote: > > > > > So, IIRC, when encrypting the snapshot name (the "my-snapshot" string), > > > you'll use key from the original inode. That's why we need to handle > > > snapshot names starting with '_' differently. And why we have a > > > customized base64 encoding function. > > > > OK... The reason I went looking at that thing was the race with rename() > > that can end up with UAF in ceph_mdsc_build_path(). > > > > We copy the plaintext name under ->d_lock, but then we call > > ceph_encode_encrypted_fname() which passes dentry->d_name to > > ceph_encode_encrypted_dname() with no locking whatsoever. > > > > Have it race with rename and you've got a lot of unpleasantness. > > > > The thing is, we can have all ceph_encode_encrypted_dname() put the > > plaintext name into buf; that eliminates the need to have a separate > > qstr (or dentry, in case of ceph_encode_encrypted_fname()) argument and > > simplifies ceph_encode_encrypted_dname() while we are at it. > > > > Proposed fix in git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name > > > > WARNING: it's completely untested and needs review. It's split in two commits > > (massage of ceph_encode_encrypted_dname(), then changing the calling conventions); > > both patches in followups. > > > > Please, review. > > I've reviewed both patches and they seem to be OK, so feel free to add my > > Reviewed-by: Luis Henriques <luis@igalia.com> > > But as I said, I don't have a test environment at the moment. I'm adding > Slava to CC with the hope that he may be able to run some fscrypt-specific > tests (including snapshots creation) against these patches. > > Let me apply the patches and test it. I'll share the testing results ASAP. Thanks, Slava. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-17 17:56 ` Viacheslav Dubeyko @ 2025-02-17 18:48 ` Luis Henriques 2025-02-17 22:04 ` Viacheslav Dubeyko 0 siblings, 1 reply; 21+ messages in thread From: Luis Henriques @ 2025-02-17 18:48 UTC (permalink / raw) To: Viacheslav Dubeyko Cc: viro@zeniv.linux.org.uk, ceph-devel@vger.kernel.org, jlayton@kernel.org, linux-fsdevel@vger.kernel.org, idryomov@gmail.com On Mon, Feb 17 2025, Viacheslav Dubeyko wrote: > On Sat, 2025-02-15 at 15:39 +0000, Luis Henriques wrote: >> On Sat, Feb 15 2025, Al Viro wrote: >> >> > On Fri, Feb 14, 2025 at 04:05:42PM +0000, Luis Henriques wrote: >> > >> > > So, IIRC, when encrypting the snapshot name (the "my-snapshot" string), >> > > you'll use key from the original inode. That's why we need to handle >> > > snapshot names starting with '_' differently. And why we have a >> > > customized base64 encoding function. >> > >> > OK... The reason I went looking at that thing was the race with rename() >> > that can end up with UAF in ceph_mdsc_build_path(). >> > >> > We copy the plaintext name under ->d_lock, but then we call >> > ceph_encode_encrypted_fname() which passes dentry->d_name to >> > ceph_encode_encrypted_dname() with no locking whatsoever. >> > >> > Have it race with rename and you've got a lot of unpleasantness. >> > >> > The thing is, we can have all ceph_encode_encrypted_dname() put the >> > plaintext name into buf; that eliminates the need to have a separate >> > qstr (or dentry, in case of ceph_encode_encrypted_fname()) argument and >> > simplifies ceph_encode_encrypted_dname() while we are at it. >> > >> > Proposed fix in git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name >> > >> > WARNING: it's completely untested and needs review. It's split in two commits >> > (massage of ceph_encode_encrypted_dname(), then changing the calling conventions); >> > both patches in followups. >> > >> > Please, review. >> >> I've reviewed both patches and they seem to be OK, so feel free to add my >> >> Reviewed-by: Luis Henriques <luis@igalia.com> >> >> But as I said, I don't have a test environment at the moment. I'm adding >> Slava to CC with the hope that he may be able to run some fscrypt-specific >> tests (including snapshots creation) against these patches. >> >> > > Let me apply the patches and test it. I'll share the testing results ASAP. Awesome! Thanks a lot. Cheers, -- Luís ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-17 18:48 ` Luis Henriques @ 2025-02-17 22:04 ` Viacheslav Dubeyko 2025-02-18 1:21 ` Al Viro 0 siblings, 1 reply; 21+ messages in thread From: Viacheslav Dubeyko @ 2025-02-17 22:04 UTC (permalink / raw) To: luis@igalia.com Cc: ceph-devel@vger.kernel.org, jlayton@kernel.org, viro@zeniv.linux.org.uk, idryomov@gmail.com, linux-fsdevel@vger.kernel.org On Mon, 2025-02-17 at 18:48 +0000, Luis Henriques wrote: > On Mon, Feb 17 2025, Viacheslav Dubeyko wrote: > > > On Sat, 2025-02-15 at 15:39 +0000, Luis Henriques wrote: > > > On Sat, Feb 15 2025, Al Viro wrote: > > > > > > > On Fri, Feb 14, 2025 at 04:05:42PM +0000, Luis Henriques wrote: > > > > > > > > > So, IIRC, when encrypting the snapshot name (the "my-snapshot" string), > > > > > you'll use key from the original inode. That's why we need to handle > > > > > snapshot names starting with '_' differently. And why we have a > > > > > customized base64 encoding function. > > > > > > > > OK... The reason I went looking at that thing was the race with rename() > > > > that can end up with UAF in ceph_mdsc_build_path(). > > > > > > > > We copy the plaintext name under ->d_lock, but then we call > > > > ceph_encode_encrypted_fname() which passes dentry->d_name to > > > > ceph_encode_encrypted_dname() with no locking whatsoever. > > > > > > > > Have it race with rename and you've got a lot of unpleasantness. > > > > > > > > The thing is, we can have all ceph_encode_encrypted_dname() put the > > > > plaintext name into buf; that eliminates the need to have a separate > > > > qstr (or dentry, in case of ceph_encode_encrypted_fname()) argument and > > > > simplifies ceph_encode_encrypted_dname() while we are at it. > > > > > > > > Proposed fix in git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name > > > > > > > > WARNING: it's completely untested and needs review. It's split in two commits > > > > (massage of ceph_encode_encrypted_dname(), then changing the calling conventions); > > > > both patches in followups. > > > > > > > > Please, review. > > > > > > I've reviewed both patches and they seem to be OK, so feel free to add my > > > > > > Reviewed-by: Luis Henriques <luis@igalia.com> > > > > > > But as I said, I don't have a test environment at the moment. I'm adding > > > Slava to CC with the hope that he may be able to run some fscrypt-specific > > > tests (including snapshots creation) against these patches. > > > > > > > > > > Let me apply the patches and test it. I'll share the testing results ASAP. > > Awesome! Thanks a lot. > As far as I can see, we have issue in the patchset. I was able to reproduce the issue two times. sudo ./check -g encrypt FSTYP -- ceph PLATFORM -- Linux/x86_64 ceph-testing-0001 6.14.0-rc3+ #174 SMP PREEMPT_DYNAMIC Mon Feb 17 21:02:23 UTC 2025 MKFS_OPTIONS -- 127.0.0.1:40364:/scratch MOUNT_OPTIONS -- -o name=fs,secret=<secret>,ms_mode=crc,nowsync,copyfrom 127.0.0.1:40364:/scratch /mnt/scratch generic/395 10s ... 10s generic/396 9s ... 9s generic/397 10s ... _check_dmesg: something found in dmesg (see /home/slavad/XFSTESTS/xfstests-dev/results//generic/397.dmesg) generic/398 1s ... [not run] kernel doesn't support renameat2 syscall generic/399 28s ... [not run] Filesystem ceph not supported in _scratch_mkfs_sized_encrypted generic/419 1s ... [not run] kernel doesn't support renameat2 syscall generic/421 14s ... 13s generic/429 22s ... 22s generic/435 891s ... 848s generic/440 14s ... 13s generic/548 2s ... [not run] xfs_io fiemap failed (old kernel/wrong fs?) generic/549 2s ... [not run] encryption policy '-c 5 -n 6 -f 0' is unusable; probably missing kernel crypto API support generic/550 4s ... [not run] encryption policy '-c 9 -n 9 -f 0' is unusable; probably missing kernel crypto API support generic/576 [not run] fsverity utility required, skipped this test generic/580 15s ... 14s generic/581 22s ... 21s generic/582 2s ... [not run] xfs_io fiemap failed (old kernel/wrong fs?) generic/583 2s ... [not run] encryption policy '-c 5 -n 6 -v 2 -f 0' is unusable; probably missing kernel crypto API support generic/584 3s ... [not run] encryption policy '-c 9 -n 9 -v 2 -f 0' is unusable; probably missing kernel crypto API support generic/592 3s ... [not run] kernel does not support encryption policy: '-c 1 -n 4 -v 2 -f 8' generic/593 15s ... 16s generic/595 19s ... 18s generic/602 2s ... [not run] kernel does not support encryption policy: '-c 1 -n 4 -v 2 -f 16' generic/613 5s ... [not run] _get_encryption_nonce() isn't implemented on ceph generic/621 6s ... [not run] kernel doesn't support renameat2 syscall generic/693 6s ... [not run] encryption policy '-c 1 -n 10 -v 2 -f 0' is unusable; probably missing kernel crypto API support generic/739 [not run] xfs_io set_encpolicy doesn't support -s Ran: generic/395 generic/396 generic/397 generic/398 generic/399 generic/419 generic/421 generic/429 generic/435 generic/440 generic/548 generic/549 generic/550 generic/576 generic/580 generic/581 generic/582 generic/583 generic/584 generic/592 generic/593 generic/595 generic/602 generic/613 generic/621 generic/693 generic/739 Not run: generic/398 generic/399 generic/419 generic/548 generic/549 generic/550 generic/576 generic/582 generic/583 generic/584 generic/592 generic/602 generic/613 generic/621 generic/693 generic/739 Failures: generic/397 Failed 1 of 27 tests [ 324.470491] run fstests generic/397 at 2025-02-17 12:18:13 [ 325.099452] libceph: mon0 (2)127.0.0.1:40489 session established [ 325.100845] libceph: client4422 fsid 5b237794-984b-474e-a140-606ec05b28a3 [ 325.597065] libceph: mon0 (2)127.0.0.1:40489 session established [ 325.598444] libceph: client4425 fsid 5b237794-984b-474e-a140-606ec05b28a3 [ 325.679627] libceph: mon0 (2)127.0.0.1:40489 session established [ 325.687399] libceph: client4428 fsid 5b237794-984b-474e-a140-606ec05b28a3 [ 325.803731] xfs_io (pid 8916) is setting deprecated v1 encryption policy; recommend upgrading to v2. [ 325.883128] libceph: mon0 (2)127.0.0.1:40489 session established [ 325.884512] libceph: client4431 fsid 5b237794-984b-474e-a140-606ec05b28a3 [ 325.946397] libceph: mon0 (2)127.0.0.1:40489 session established [ 325.947903] libceph: client4434 fsid 5b237794-984b-474e-a140-606ec05b28a3 [ 326.321358] fscrypt: AES-256-CBC-CTS using implementation "cts(cbc(ecb(aes- generic)))" [ 326.354897] fscrypt: AES-256-XTS using implementation "xts(ecb(aes-generic))" [ 326.439844] ================================================================== [ 326.440593] BUG: KASAN: stack-out-of-bounds in ceph_encode_encrypted_dname+0x4a5/0x560 [ 326.441355] Write of size 1 at addr ffff8881b6c5f27f by task xfs_io/9048 [ 326.442149] CPU: 0 UID: 0 PID: 9048 Comm: xfs_io Not tainted 6.14.0-rc3+ #172 [ 326.442156] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel- 1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 [ 326.442162] Call Trace: [ 326.442166] <TASK> [ 326.442171] dump_stack_lvl+0x76/0xa0 [ 326.442182] print_report+0xce/0x5f0 [ 326.442187] ? ceph_encode_encrypted_dname+0x4a5/0x560 [ 326.442191] ? kasan_addr_to_slab+0xd/0xb0 [ 326.442198] ? ceph_encode_encrypted_dname+0x4a5/0x560 [ 326.442203] kasan_report+0xbe/0x110 [ 326.442206] ? ceph_encode_encrypted_dname+0x4a5/0x560 [ 326.442212] __asan_report_store1_noabort+0x17/0x30 [ 326.442217] ceph_encode_encrypted_dname+0x4a5/0x560 [ 326.442221] ? lockref_get_not_zero+0xd5/0x1d0 [ 326.442227] ? __pfx_ceph_encode_encrypted_dname+0x10/0x10 [ 326.442232] ? __kasan_check_write+0x14/0x30 [ 326.442237] ? _raw_spin_lock+0x82/0xf0 [ 326.442242] ceph_mdsc_build_path+0x710/0xb00 [ 326.442249] ? __pfx_ceph_mdsc_build_path+0x10/0x10 [ 326.442258] set_request_path_attr.constprop.0+0x286/0xa50 [ 326.442262] ? __kernel_text_address+0x12/0x50 [ 326.442269] __prepare_send_request+0x6ad/0x49f0 [ 326.442277] ? __pfx___prepare_send_request+0x10/0x10 [ 326.442282] ? __kasan_check_write+0x14/0x30 [ 326.442286] ? _raw_spin_lock_irqsave+0x96/0x110 [ 326.442293] ? ceph_get_mds_session+0xd7/0x160 [ 326.442298] ? __pfx_ceph_get_mds_session+0x10/0x10 [ 326.442302] ? _atomic_dec_and_lock+0x19/0xc0 [ 326.442307] ? iput+0x104/0x5c0 [ 326.442312] __do_request+0x172b/0x4590 [ 326.442319] ? __pfx___do_request+0x10/0x10 [ 326.442324] ? __kasan_check_write+0x14/0x30 [ 326.442328] ? _raw_spin_lock+0x82/0xf0 [ 326.442331] ? __pfx__raw_spin_lock+0x10/0x10 [ 326.442335] ceph_mdsc_submit_request+0x6a8/0xe70 [ 326.442341] ceph_mdsc_do_request+0x6f/0x200 [ 326.442346] ceph_atomic_open+0xa18/0x24c0 [ 326.442354] ? __pfx_ceph_atomic_open+0x10/0x10 [ 326.442358] ? __pfx_make_vfsuid+0x10/0x10 [ 326.442363] ? __pfx_map_id_up+0x10/0x10 [ 326.442367] ? generic_permission+0x149/0x670 [ 326.442373] ? ceph_permission+0x54/0x80 [ 326.442379] ? inode_permission+0x130/0x4f0 [ 326.442383] path_openat+0x2262/0x4430 [ 326.442389] ? __pfx_path_openat+0x10/0x10 [ 326.442392] ? stack_depot_save_flags+0x2d/0x800 [ 326.442397] ? local_clock_noinstr+0xe/0xd0 [ 326.442403] ? stack_depot_save+0xe/0x20 [ 326.442407] do_filp_open+0x1d4/0x430 [ 326.442411] ? _raw_spin_lock_irqsave+0x96/0x110 [ 326.442415] ? __pfx_do_filp_open+0x10/0x10 [ 326.442421] ? _raw_spin_unlock+0xe/0x40 [ 326.442426] do_sys_openat2+0x136/0x180 [ 326.442431] ? __pfx_do_sys_openat2+0x10/0x10 [ 326.442436] ? __count_memcg_events+0x19e/0x490 [ 326.442440] __x64_sys_openat+0x128/0x220 [ 326.442445] ? __pfx___x64_sys_openat+0x10/0x10 [ 326.442451] x64_sys_call+0x1998/0x26f0 [ 326.442457] do_syscall_64+0x7c/0x170 [ 326.442463] ? irqentry_exit+0x43/0x50 [ 326.442466] ? clear_bhb_loop+0x15/0x70 [ 326.442472] ? clear_bhb_loop+0x15/0x70 [ 326.442476] ? clear_bhb_loop+0x15/0x70 [ 326.442481] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 326.442485] RIP: 0033:0x748c12b1453b [ 326.442495] Code: 25 00 00 41 00 3d 00 00 41 00 74 4b 64 8b 04 25 18 00 00 00 85 c0 75 67 44 89 e2 48 89 ee bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d 00 f0 ff ff 0f 87 91 00 00 00 48 8b 54 24 28 64 48 2b 14 25 [ 326.442499] RSP: 002b:00007fffdf0d0730 EFLAGS: 00000246 ORIG_RAX: 0000000000000101 [ 326.442504] RAX: ffffffffffffffda RBX: 0000000000000242 RCX: 0000748c12b1453b [ 326.442508] RDX: 0000000000000242 RSI: 00007fffdf0d1599 RDI: 00000000ffffff9c [ 326.442510] RBP: 00007fffdf0d1599 R08: 00007fffdf0d09e0 R09: 0000000000000000 [ 326.442513] R10: 0000000000000180 R11: 0000000000000246 R12: 0000000000000242 [ 326.442515] R13: 0000748c12c1a42c R14: 0000000000000180 R15: 0000000000000060 [ 326.442521] </TASK> [ 326.475514] The buggy address belongs to stack of task xfs_io/9048 [ 326.476100] and is located at offset 287 in frame: [ 326.476555] ceph_mdsc_build_path+0x0/0xb00 [ 326.477120] This frame has 1 object: [ 326.477466] [32, 287) 'buf' [ 326.477899] The buggy address belongs to the physical page: [ 326.478428] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x10af98 pfn:0x1b6c5f [ 326.478437] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) [ 326.478445] raw: 0017ffffc0000000 0000000000000000 dead000000000122 0000000000000000 [ 326.478449] raw: 000000000010af98 0000000000000000 00000000ffffffff 0000000000000000 [ 326.478452] page dumped because: kasan: bad access detected [ 326.478619] Memory state around the buggy address: [ 326.479079] ffff8881b6c5f100: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 [ 326.479749] ffff8881b6c5f180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 326.480431] >ffff8881b6c5f200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 [ 326.481106] ^ [ 326.481770] ffff8881b6c5f280: f3 f3 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 [ 326.482455] ffff8881b6c5f300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 326.483128] ================================================================== [ 326.484102] Disabling lock debugging due to kernel taint [ 326.752698] libceph: mon0 (2)127.0.0.1:40489 session established [ 326.755111] libceph: client4437 fsid 5b237794-984b-474e-a140-606ec05b28a3 [ 327.128197] libceph: mon0 (2)127.0.0.1:40489 session established [ 327.129890] libceph: client4440 fsid 5b237794-984b-474e-a140-606ec05b28a3 (gdb) l *ceph_encode_encrypted_dname+0x4a5 0xffffffff829d6605 is in ceph_encode_encrypted_dname (fs/ceph/crypto.c:273). 268 u32 len; 269 int name_len = elen; 270 int ret; 271 u8 *cryptbuf = NULL; 272 273 buf[elen] = '\0'; 274 /* Handle the special case of snapshot names that start with '_' */ 275 if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') { 276 dir = parse_longname(parent, p, &name_len); 277 if (IS_ERR(dir)) (gdb) l *ceph_mdsc_build_path+0x710 0xffffffff829a4c70 is in ceph_mdsc_build_path (fs/ceph/mds_client.c:2769). 2764 dput(cur); 2765 return ERR_PTR(ret); 2766 } 2767 2768 if (fscrypt_has_encryption_key(d_inode(parent))) { 2769 len = ceph_encode_encrypted_dname(d_inode(parent), 2770 buf, len); 2771 if (len < 0) { 2772 dput(parent); 2773 dput(cur); Thanks, Slava. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-17 22:04 ` Viacheslav Dubeyko @ 2025-02-18 1:21 ` Al Viro 2025-02-18 23:52 ` Al Viro 0 siblings, 1 reply; 21+ messages in thread From: Al Viro @ 2025-02-18 1:21 UTC (permalink / raw) To: Viacheslav Dubeyko Cc: luis@igalia.com, ceph-devel@vger.kernel.org, jlayton@kernel.org, idryomov@gmail.com, linux-fsdevel@vger.kernel.org On Mon, Feb 17, 2025 at 10:04:50PM +0000, Viacheslav Dubeyko wrote: > [ 326.477120] This frame has 1 object: > [ 326.477466] [32, 287) 'buf' > (gdb) l *ceph_encode_encrypted_dname+0x4a5 > 0xffffffff829d6605 is in ceph_encode_encrypted_dname (fs/ceph/crypto.c:273). > 268 u32 len; > 269 int name_len = elen; > 270 int ret; > 271 u8 *cryptbuf = NULL; > 272 > 273 buf[elen] = '\0'; Cute... The fix is obvious (should be char buf[NAME_MAX + 1]; rather than char buf[NAME_MAX]; ), but the funny part is that it had been a bug all along - if you give the mainline a name that has a 255-character component that happens to start with _, you'll get buf[] filled with a copy of that component (no NUL in sight) and have it passed as 'name' to parse_longname() that starts with /* Skip initial '_' */ name++; name_end = strrchr(name, '_'); See the problem? strrchr() expects a NUL-terminated string; giving it an array that has no zero bytes in it is an UB. That one is -stable fodder on its own, IMO... ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-18 1:21 ` Al Viro @ 2025-02-18 23:52 ` Al Viro 2025-02-19 0:58 ` Viacheslav Dubeyko 0 siblings, 1 reply; 21+ messages in thread From: Al Viro @ 2025-02-18 23:52 UTC (permalink / raw) To: Viacheslav Dubeyko Cc: luis@igalia.com, ceph-devel@vger.kernel.org, jlayton@kernel.org, idryomov@gmail.com, linux-fsdevel@vger.kernel.org On Tue, Feb 18, 2025 at 01:21:32AM +0000, Al Viro wrote: > See the problem? strrchr() expects a NUL-terminated string; giving it an > array that has no zero bytes in it is an UB. > > That one is -stable fodder on its own, IMO... FWIW, it's more unpleasant; there are other call chains for parse_longname() where it's not feasible to NUL-terminate in place. I suspect that the patch below is a better way to handle that. Comments? From ed016e5ea89550b567306207ba3ca8b60e147d89 Mon Sep 17 00:00:00 2001 From: Al Viro <viro@zeniv.linux.org.uk> Date: Tue, 18 Feb 2025 17:57:17 -0500 Subject: [PATCH 1/3] [ceph] parse_longname(): strrchr() expects NUL-terminated string ... and parse_longname() is not guaranteed that. That's the reason why it uses kmemdup_nul() to build the argument for kstrtou64(); the problem is, kstrtou64() is not the only thing that need it. Just get a NUL-terminated copy of the entire thing and be done with that... Fixes: dd66df0053ef "ceph: add support for encrypted snapshot names" Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> --- fs/ceph/crypto.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 3b3c4d8d401e..164e7981aecb 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -215,35 +215,30 @@ static struct inode *parse_longname(const struct inode *parent, struct ceph_client *cl = ceph_inode_to_client(parent); struct inode *dir = NULL; struct ceph_vino vino = { .snap = CEPH_NOSNAP }; - char *inode_number; char *name_end; - int orig_len = *name_len; int ret = -EIO; - + /* NUL-terminate */ + char *s __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL); + if (!s) + return ERR_PTR(-ENOMEM); /* Skip initial '_' */ - name++; - name_end = strrchr(name, '_'); + s++; + name_end = strrchr(s, '_'); if (!name_end) { - doutc(cl, "failed to parse long snapshot name: %s\n", name); + doutc(cl, "failed to parse long snapshot name: %s\n", s); return ERR_PTR(-EIO); } - *name_len = (name_end - name); + *name_len = (name_end - s); if (*name_len <= 0) { pr_err_client(cl, "failed to parse long snapshot name\n"); return ERR_PTR(-EIO); } /* Get the inode number */ - inode_number = kmemdup_nul(name_end + 1, - orig_len - *name_len - 2, - GFP_KERNEL); - if (!inode_number) - return ERR_PTR(-ENOMEM); - ret = kstrtou64(inode_number, 10, &vino.ino); + ret = kstrtou64(name_end + 1, 10, &vino.ino); if (ret) { - doutc(cl, "failed to parse inode number: %s\n", name); - dir = ERR_PTR(ret); - goto out; + doutc(cl, "failed to parse inode number: %s\n", s); + return ERR_PTR(ret); } /* And finally the inode */ @@ -252,11 +247,8 @@ static struct inode *parse_longname(const struct inode *parent, /* This can happen if we're not mounting cephfs on the root */ dir = ceph_get_inode(parent->i_sb, vino, NULL); if (IS_ERR(dir)) - doutc(cl, "can't find inode %s (%s)\n", inode_number, name); + doutc(cl, "can't find inode %s (%s)\n", name_end + 1, name); } - -out: - kfree(inode_number); return dir; } -- 2.39.5 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* RE: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-18 23:52 ` Al Viro @ 2025-02-19 0:58 ` Viacheslav Dubeyko 2025-02-19 2:18 ` Al Viro 0 siblings, 1 reply; 21+ messages in thread From: Viacheslav Dubeyko @ 2025-02-19 0:58 UTC (permalink / raw) To: viro@zeniv.linux.org.uk Cc: luis@igalia.com, ceph-devel@vger.kernel.org, jlayton@kernel.org, linux-fsdevel@vger.kernel.org, idryomov@gmail.com On Tue, 2025-02-18 at 23:52 +0000, Al Viro wrote: > On Tue, Feb 18, 2025 at 01:21:32AM +0000, Al Viro wrote: > > > See the problem? strrchr() expects a NUL-terminated string; giving it an > > array that has no zero bytes in it is an UB. > > > > That one is -stable fodder on its own, IMO... > > FWIW, it's more unpleasant; there are other call chains for parse_longname() > where it's not feasible to NUL-terminate in place. I suspect that the > patch below is a better way to handle that. Comments? > Let me test the patch. > From ed016e5ea89550b567306207ba3ca8b60e147d89 Mon Sep 17 00:00:00 2001 > From: Al Viro <viro@zeniv.linux.org.uk> > Date: Tue, 18 Feb 2025 17:57:17 -0500 > Subject: [PATCH 1/3] [ceph] parse_longname(): strrchr() expects NUL-terminated > string > > ... and parse_longname() is not guaranteed that. That's the reason > why it uses kmemdup_nul() to build the argument for kstrtou64(); > the problem is, kstrtou64() is not the only thing that need it. > > Just get a NUL-terminated copy of the entire thing and be done > with that... > > Fixes: dd66df0053ef "ceph: add support for encrypted snapshot names" > Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> > --- > fs/ceph/crypto.c | 32 ++++++++++++-------------------- > 1 file changed, 12 insertions(+), 20 deletions(-) > > diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c > index 3b3c4d8d401e..164e7981aecb 100644 > --- a/fs/ceph/crypto.c > +++ b/fs/ceph/crypto.c > @@ -215,35 +215,30 @@ static struct inode *parse_longname(const struct inode *parent, > struct ceph_client *cl = ceph_inode_to_client(parent); > struct inode *dir = NULL; > struct ceph_vino vino = { .snap = CEPH_NOSNAP }; > - char *inode_number; > char *name_end; > - int orig_len = *name_len; > int ret = -EIO; > - > + /* NUL-terminate */ > + char *s __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL); Maybe, str here? The s looks too extreme for my taste. :) > + if (!s) > + return ERR_PTR(-ENOMEM); > /* Skip initial '_' */ > - name++; > - name_end = strrchr(name, '_'); > + s++; > + name_end = strrchr(s, '_'); > if (!name_end) { > - doutc(cl, "failed to parse long snapshot name: %s\n", name); > + doutc(cl, "failed to parse long snapshot name: %s\n", s); > return ERR_PTR(-EIO); > } > - *name_len = (name_end - name); > + *name_len = (name_end - s); > if (*name_len <= 0) { > pr_err_client(cl, "failed to parse long snapshot name\n"); > return ERR_PTR(-EIO); > } > > /* Get the inode number */ > - inode_number = kmemdup_nul(name_end + 1, > - orig_len - *name_len - 2, > - GFP_KERNEL); > - if (!inode_number) > - return ERR_PTR(-ENOMEM); > - ret = kstrtou64(inode_number, 10, &vino.ino); > + ret = kstrtou64(name_end + 1, 10, &vino.ino); The inode_number explains by itself what we are converting here. But name_end sounds confusing and not informative for my taste. But I could have a bad taste. :) > if (ret) { > - doutc(cl, "failed to parse inode number: %s\n", name); > - dir = ERR_PTR(ret); > - goto out; > + doutc(cl, "failed to parse inode number: %s\n", s); > + return ERR_PTR(ret); > } > > /* And finally the inode */ > @@ -252,11 +247,8 @@ static struct inode *parse_longname(const struct inode *parent, > /* This can happen if we're not mounting cephfs on the root */ > dir = ceph_get_inode(parent->i_sb, vino, NULL); > if (IS_ERR(dir)) > - doutc(cl, "can't find inode %s (%s)\n", inode_number, name); > + doutc(cl, "can't find inode %s (%s)\n", name_end + 1, name); It's not critical, but we use name_end + 1 two times. Potentially, it could be a source of somebody mistake in the future. Thanks, Slava. > } > - > -out: > - kfree(inode_number); > return dir; > } > ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-19 0:58 ` Viacheslav Dubeyko @ 2025-02-19 2:18 ` Al Viro 2025-02-19 23:22 ` Viacheslav Dubeyko 2025-02-21 1:21 ` Viacheslav Dubeyko 0 siblings, 2 replies; 21+ messages in thread From: Al Viro @ 2025-02-19 2:18 UTC (permalink / raw) To: Viacheslav Dubeyko Cc: luis@igalia.com, ceph-devel@vger.kernel.org, jlayton@kernel.org, linux-fsdevel@vger.kernel.org, idryomov@gmail.com On Wed, Feb 19, 2025 at 12:58:54AM +0000, Viacheslav Dubeyko wrote: > On Tue, 2025-02-18 at 23:52 +0000, Al Viro wrote: > > On Tue, Feb 18, 2025 at 01:21:32AM +0000, Al Viro wrote: > > > > > See the problem? strrchr() expects a NUL-terminated string; giving it an > > > array that has no zero bytes in it is an UB. > > > > > > That one is -stable fodder on its own, IMO... > > > > FWIW, it's more unpleasant; there are other call chains for parse_longname() > > where it's not feasible to NUL-terminate in place. I suspect that the > > patch below is a better way to handle that. Comments? > > > > Let me test the patch. That one is on top of mainline (-rc2); the entire branch is git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name The first commit in there is this one, then two posted earlier rebased on top of that (without the "NUL-terminate in place" in the last one, which is what tripped KASAN and is no longer needed due to the first commit). ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-19 2:18 ` Al Viro @ 2025-02-19 23:22 ` Viacheslav Dubeyko 2025-02-21 1:21 ` Viacheslav Dubeyko 1 sibling, 0 replies; 21+ messages in thread From: Viacheslav Dubeyko @ 2025-02-19 23:22 UTC (permalink / raw) To: viro@zeniv.linux.org.uk Cc: luis@igalia.com, ceph-devel@vger.kernel.org, jlayton@kernel.org, linux-fsdevel@vger.kernel.org, idryomov@gmail.com On Wed, 2025-02-19 at 02:18 +0000, Al Viro wrote: > On Wed, Feb 19, 2025 at 12:58:54AM +0000, Viacheslav Dubeyko wrote: > > On Tue, 2025-02-18 at 23:52 +0000, Al Viro wrote: > > > On Tue, Feb 18, 2025 at 01:21:32AM +0000, Al Viro wrote: > > > > > > > See the problem? strrchr() expects a NUL-terminated string; giving it an > > > > array that has no zero bytes in it is an UB. > > > > > > > > That one is -stable fodder on its own, IMO... > > > > > > FWIW, it's more unpleasant; there are other call chains for parse_longname() > > > where it's not feasible to NUL-terminate in place. I suspect that the > > > patch below is a better way to handle that. Comments? > > > > > > > Let me test the patch. > > That one is on top of mainline (-rc2); the entire branch is > > git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name > > The first commit in there is this one, then two posted earlier rebased > on top of that (without the "NUL-terminate in place" in the last one, > which is what tripped KASAN and is no longer needed due to the first > commit). I did run xfstests and I don't see any issues with the patchset. But I need to play with snapshots yet to check that I cannot trigger any issue there. Let me spend some time for this. Thanks, Slava. ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-19 2:18 ` Al Viro 2025-02-19 23:22 ` Viacheslav Dubeyko @ 2025-02-21 1:21 ` Viacheslav Dubeyko 1 sibling, 0 replies; 21+ messages in thread From: Viacheslav Dubeyko @ 2025-02-21 1:21 UTC (permalink / raw) To: viro@zeniv.linux.org.uk Cc: luis@igalia.com, ceph-devel@vger.kernel.org, jlayton@kernel.org, linux-fsdevel@vger.kernel.org, idryomov@gmail.com On Wed, 2025-02-19 at 02:18 +0000, Al Viro wrote: > On Wed, Feb 19, 2025 at 12:58:54AM +0000, Viacheslav Dubeyko wrote: > > On Tue, 2025-02-18 at 23:52 +0000, Al Viro wrote: > > > On Tue, Feb 18, 2025 at 01:21:32AM +0000, Al Viro wrote: > > > > > > > See the problem? strrchr() expects a NUL-terminated string; giving it an > > > > array that has no zero bytes in it is an UB. > > > > > > > > That one is -stable fodder on its own, IMO... > > > > > > FWIW, it's more unpleasant; there are other call chains for parse_longname() > > > where it's not feasible to NUL-terminate in place. I suspect that the > > > patch below is a better way to handle that. Comments? > > > > > > > Let me test the patch. > > That one is on top of mainline (-rc2); the entire branch is > > git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git #d_name > > The first commit in there is this one, then two posted earlier rebased > on top of that (without the "NUL-terminate in place" in the last one, > which is what tripped KASAN and is no longer needed due to the first > commit). I have tested patchset by xfstests and I have played with snapshots manually. I haven't found any new or critical issues. Everything looks good. Tested-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Thanks, Slava. ^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [RFC] odd check in ceph_encode_encrypted_dname() 2025-02-14 2:47 [RFC] odd check in ceph_encode_encrypted_dname() Al Viro 2025-02-14 3:28 ` Al Viro @ 2025-02-14 15:30 ` Jeff Layton 1 sibling, 0 replies; 21+ messages in thread From: Jeff Layton @ 2025-02-14 15:30 UTC (permalink / raw) To: Al Viro, Luís Henriques; +Cc: ceph-devel, linux-fsdevel, Ilya Dryomov On Fri, 2025-02-14 at 02:47 +0000, Al Viro wrote: > AFAICS, this > > /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ > WARN_ON(elen > 240); > if ((elen > 0) && (dir != parent)) { > char tmp_buf[NAME_MAX]; > > elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", > elen, buf, dir->i_ino); > memcpy(buf, tmp_buf, elen); > } > > could drop the (elen > 0) part of the test. elen comes from > elen = ceph_base64_encode(cryptbuf, len, buf); > and that can't return a non-positive unless the second argument is 0 or > above 1G. The latter is flat-out impossible - right before that call > we have > /* hash the end if the name is long enough */ > if (len > CEPH_NOHASH_NAME_MAX) { > u8 hash[SHA256_DIGEST_SIZE]; > u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX; > > /* > * hash the extra bytes and overwrite crypttext beyond that > * point with it > */ > sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash); > memcpy(extra, hash, SHA256_DIGEST_SIZE); > len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE; > } > which obviously caps it with CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE, > i.e. (180 - SHA256_DIGEST_SIZE) + SHA256_DIGEST_SIZE. > > The former would have to come from > if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) { > elen = -ENAMETOOLONG; > goto out; > } > and since fscrypt_fname_encrypted_size() must've returned true, we have > len no less than FSCRYPT_FNAME_MIN_MSG_LEN, i.e. it's 16 or greater. > > That stuff went into the tree in dd66df0053ef8 "ceph: add support for encrypted > snapshot names" and as far as I can tell, everything above had been applicable > back then too. > > Am I missing something subtle here? Can elen be non-positive at that point? > No, I think you nailed it. -- Jeff Layton <jlayton@kernel.org> ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2025-02-21 1:21 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-02-14 2:47 [RFC] odd check in ceph_encode_encrypted_dname() Al Viro 2025-02-14 3:28 ` Al Viro 2025-02-14 14:05 ` Luis Henriques 2025-02-14 15:41 ` Jeff Layton 2025-02-14 16:05 ` Luis Henriques 2025-02-15 4:46 ` Al Viro 2025-02-15 4:47 ` [PATCH 1/2] prep for ceph_encode_encrypted_fname() fixes Al Viro 2025-02-15 12:41 ` Jeff Layton 2025-02-15 4:47 ` [PATCH 2/2] ceph: fix a race with rename() in ceph_mdsc_build_path() Al Viro 2025-02-15 12:42 ` Jeff Layton 2025-02-15 15:39 ` [RFC] odd check in ceph_encode_encrypted_dname() Luis Henriques 2025-02-17 17:56 ` Viacheslav Dubeyko 2025-02-17 18:48 ` Luis Henriques 2025-02-17 22:04 ` Viacheslav Dubeyko 2025-02-18 1:21 ` Al Viro 2025-02-18 23:52 ` Al Viro 2025-02-19 0:58 ` Viacheslav Dubeyko 2025-02-19 2:18 ` Al Viro 2025-02-19 23:22 ` Viacheslav Dubeyko 2025-02-21 1:21 ` Viacheslav Dubeyko 2025-02-14 15:30 ` Jeff Layton
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).