diff options
Diffstat (limited to 'fs/ceph')
| -rw-r--r-- | fs/ceph/crypto.c | 50 | ||||
| -rw-r--r-- | fs/ceph/mds_client.c | 12 | ||||
| -rw-r--r-- | fs/ceph/mds_client.h | 1 |
3 files changed, 50 insertions, 13 deletions
diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index 64d240759277..1cc11f77413c 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -298,6 +298,11 @@ out: * Otherwise, base64 decode the string, and then ask fscrypt to format it * for userland presentation. * + * Though the fscrypt/crypto subsystems broadly expect all buffers to be in the + * linear-mapped region, this function slightly relaxes those requirements: + * fname->ctext, fname->name, and oname->name may be vmalloc(), but not + * tname->name. + * * Returns 0 on success or negative error code on error. */ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, @@ -305,11 +310,15 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, { struct inode *dir = fname->dir; struct fscrypt_str _tname = FSTR_INIT(NULL, 0); + struct fscrypt_str _oname; struct fscrypt_str iname; char *name = fname->name; int name_len = fname->name_len; int ret; + if (WARN_ON_ONCE(tname && is_vmalloc_addr(tname->name))) + return -EIO; + /* Sanity check that the resulting name will fit in the buffer */ if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX) return -EIO; @@ -350,31 +359,50 @@ int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, goto out_inode; } + if ((!tname || !tname->name) && + (fname->ctext_len == 0 || + unlikely(is_vmalloc_addr(fname->ctext)) || + unlikely(is_vmalloc_addr(oname->name)))) { + ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); + if (ret) + goto out_inode; + tname = &_tname; + } + if (fname->ctext_len == 0) { int declen; - if (!tname) { - ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); - if (ret) - goto out_inode; - tname = &_tname; - } - - declen = base64_decode(name, name_len, - tname->name, false, BASE64_IMAP); + declen = base64_decode(name, name_len, tname->name, false, + BASE64_IMAP); if (declen <= 0) { ret = -EIO; goto out; } iname.name = tname->name; iname.len = declen; + } else if (unlikely(is_vmalloc_addr(fname->ctext))) { + memcpy(tname->name, fname->ctext, fname->ctext_len); + + iname.name = tname->name; + iname.len = fname->ctext_len; } else { iname.name = fname->ctext; iname.len = fname->ctext_len; } - ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, oname); - if (!ret && (dir != fname->dir)) { + _oname.name = unlikely(is_vmalloc_addr(oname->name)) ? + tname->name : oname->name; + _oname.len = oname->len; + + ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, &_oname); + if (ret) + goto out; + + if (unlikely(is_vmalloc_addr(oname->name))) + memcpy(oname->name, _oname.name, _oname.len); + oname->len = _oname.len; + + if (dir != fname->dir) { char tmp_buf[BASE64_CHARS(NAME_MAX)]; name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%llu", diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ec886ca9b526..0b6ff5b704f6 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -541,9 +541,13 @@ static int parse_reply_info_readdir(void **p, void *end, * to do the base64_decode in-place. It's * safe because the decoded string should * always be shorter, which is 3/4 of origin - * string. + * string. If this message was allocated with + * vmalloc() (happens, but rarely), leave it + * NULL and let ceph_fname_to_usr() allocate + * suitable temporary working space instead. */ - tname.name = _name; + if (likely(!is_vmalloc_addr(_name))) + tname.name = _name; /* * Set oname to _name too, and this will be @@ -6589,11 +6593,13 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n", tpath, mask, caller_uid, caller_gid); + mutex_lock(&mdsc->mutex); for (i = 0; i < mdsc->s_cap_auths_num; i++) { struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i]; err = ceph_mds_auth_match(mdsc, s, cred, tpath); if (err < 0) { + mutex_unlock(&mdsc->mutex); put_cred(cred); return err; } else if (err > 0) { @@ -6615,6 +6621,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms, rw_perms_s); if (root_squash_perms && rw_perms_s == NULL) { + mutex_unlock(&mdsc->mutex); doutc(cl, "access allowed\n"); return 0; } @@ -6629,6 +6636,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask) !!(mask & MAY_READ), !!(mask & MAY_WRITE)); } doutc(cl, "access denied\n"); + mutex_unlock(&mdsc->mutex); return -EACCES; } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 3c62e3c3530b..e7a262c9c2ab 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -604,6 +604,7 @@ struct ceph_mds_client { struct rw_semaphore pool_perm_rwsem; struct rb_root pool_perm_tree; + /* protected by mutex */ u32 s_cap_auths_num; struct ceph_mds_cap_auth *s_cap_auths; |
