Skip to content

Commit

Permalink
ceph: fix invalid pointer access if get_quota_realm return ERR_PTR
Browse files Browse the repository at this point in the history
[ Upstream commit 0f4cf64 ]

This issue is reported by smatch that get_quota_realm() might return
ERR_PTR but we did not handle it. It's not a immediate bug, while we
still should address it to avoid potential bugs if get_quota_realm()
is changed to return other ERR_PTR in future.

Set ceph_snap_realm's pointer in get_quota_realm()'s to address this
issue, the pointer would be set to NULL if get_quota_realm() failed
to get struct ceph_snap_realm, so no ERR_PTR would happen any more.

[ xiubli: minor code style clean up ]

Signed-off-by: Wenchao Hao <haowenchao2@huawei.com>
Reviewed-by: Xiubo Li <xiubli@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
wenchao-hao authored and gregkh committed Feb 5, 2024
1 parent 196b87e commit d8fedfb
Showing 1 changed file with 22 additions and 17 deletions.
39 changes: 22 additions & 17 deletions fs/ceph/quota.c
Expand Up @@ -194,10 +194,10 @@ void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc)
}

/*
* This function walks through the snaprealm for an inode and returns the
* ceph_snap_realm for the first snaprealm that has quotas set (max_files,
* This function walks through the snaprealm for an inode and set the
* realmp with the first snaprealm that has quotas set (max_files,
* max_bytes, or any, depending on the 'which_quota' argument). If the root is
* reached, return the root ceph_snap_realm instead.
* reached, set the realmp with the root ceph_snap_realm instead.
*
* Note that the caller is responsible for calling ceph_put_snap_realm() on the
* returned realm.
Expand All @@ -208,18 +208,19 @@ void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc)
* this function will return -EAGAIN; otherwise, the snaprealms walk-through
* will be restarted.
*/
static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
struct inode *inode,
enum quota_get_realm which_quota,
bool retry)
static int get_quota_realm(struct ceph_mds_client *mdsc, struct inode *inode,
enum quota_get_realm which_quota,
struct ceph_snap_realm **realmp, bool retry)
{
struct ceph_inode_info *ci = NULL;
struct ceph_snap_realm *realm, *next;
struct inode *in;
bool has_quota;

if (realmp)
*realmp = NULL;
if (ceph_snap(inode) != CEPH_NOSNAP)
return NULL;
return 0;

restart:
realm = ceph_inode(inode)->i_snap_realm;
Expand All @@ -245,7 +246,7 @@ static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
break;
ceph_put_snap_realm(mdsc, realm);
if (!retry)
return ERR_PTR(-EAGAIN);
return -EAGAIN;
goto restart;
}

Expand All @@ -254,8 +255,11 @@ static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
iput(in);

next = realm->parent;
if (has_quota || !next)
return realm;
if (has_quota || !next) {
if (realmp)
*realmp = realm;
return 0;
}

ceph_get_snap_realm(mdsc, next);
ceph_put_snap_realm(mdsc, realm);
Expand All @@ -264,14 +268,15 @@ static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
if (realm)
ceph_put_snap_realm(mdsc, realm);

return NULL;
return 0;
}

bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
{
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old->i_sb);
struct ceph_snap_realm *old_realm, *new_realm;
bool is_same;
int ret;

restart:
/*
Expand All @@ -281,9 +286,9 @@ bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
* dropped and we can then restart the whole operation.
*/
down_read(&mdsc->snap_rwsem);
old_realm = get_quota_realm(mdsc, old, QUOTA_GET_ANY, true);
new_realm = get_quota_realm(mdsc, new, QUOTA_GET_ANY, false);
if (PTR_ERR(new_realm) == -EAGAIN) {
get_quota_realm(mdsc, old, QUOTA_GET_ANY, &old_realm, true);
ret = get_quota_realm(mdsc, new, QUOTA_GET_ANY, &new_realm, false);
if (ret == -EAGAIN) {
up_read(&mdsc->snap_rwsem);
if (old_realm)
ceph_put_snap_realm(mdsc, old_realm);
Expand Down Expand Up @@ -485,8 +490,8 @@ bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
bool is_updated = false;

down_read(&mdsc->snap_rwsem);
realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root),
QUOTA_GET_MAX_BYTES, true);
get_quota_realm(mdsc, d_inode(fsc->sb->s_root), QUOTA_GET_MAX_BYTES,
&realm, true);
up_read(&mdsc->snap_rwsem);
if (!realm)
return false;
Expand Down

0 comments on commit d8fedfb

Please sign in to comment.