From: Zhansong Gao Date: Wed, 23 Jul 2025 05:37:39 +0000 (+0800) Subject: client: crash caused by invalid iterator in _readdir_cache_cb X-Git-Tag: v21.0.0~50^2~104^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=9e0488dd0bda18ab2abd96cdb3ec18034c1e85f1;p=ceph.git client: crash caused by invalid iterator in _readdir_cache_cb Capacity of `readdir_cache` may change after `client_lock` is unlocked in iterations of `readdir_cache`, and it can cause the iterator to be invalid, then using the invalid iterator in the next iteration will cause crash. Crash may happen at `Dentry *dn = *pd` (pd points to invalid memory), or at `if (pd >= dir->readdir_cache.end() || *pd != dn)` (pd is smaller than begin() if idx is negative). Use index instead of iterator to solve this problem. Fixes: https://tracker.ceph.com/issues/72247 Signed-off-by: Zhansong Gao --- diff --git a/src/client/Client.cc b/src/client/Client.cc index 35baf658cf3a..8c22fc9ee709 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -9461,25 +9461,22 @@ int Client::_readdir_cache_cb(dir_result_t *dirp, add_dirent_cb_t cb, void *p, dirp->offset, dentry_off_lt()); string dn_name; - while (true) { + for (unsigned idx = pd - dir->readdir_cache.begin(); + idx < dir->readdir_cache.size(); + ++idx) { int mask = caps; if (!dirp->inode->is_complete_and_ordered()) return -EAGAIN; - if (pd == dir->readdir_cache.end()) - break; - Dentry *dn = *pd; + Dentry *dn = dir->readdir_cache[idx]; if (dn->inode == NULL) { ldout(cct, 15) << " skipping null '" << dn->name << "'" << dendl; - ++pd; continue; } if (dn->cap_shared_gen != dir->parent_inode->shared_gen) { ldout(cct, 15) << " skipping mismatch shared gen '" << dn->name << "'" << dendl; - ++pd; continue; } - int idx = pd - dir->readdir_cache.begin(); if (dn->inode->is_dir() && cct->_conf->client_dirsize_rbytes) { mask |= CEPH_STAT_RSTAT; } @@ -9493,9 +9490,8 @@ int Client::_readdir_cache_cb(dir_result_t *dirp, add_dirent_cb_t cb, void *p, return -EAGAIN; } - // the content of readdir_cache may change after _getattr(), so pd may be invalid iterator - pd = dir->readdir_cache.begin() + idx; - if (pd >= dir->readdir_cache.end() || *pd != dn) + // the content of readdir_cache may change after _getattr() + if (idx >= dir->readdir_cache.size() || dir->readdir_cache[idx] != dn) return -EAGAIN; struct ceph_statx stx; @@ -9505,8 +9501,7 @@ int Client::_readdir_cache_cb(dir_result_t *dirp, add_dirent_cb_t cb, void *p, uint64_t next_off = dn->offset + 1; auto dname = _unwrap_name(*diri, dn->name, dn->alternate_name); fill_dirent(&de, dname.c_str(), stx.stx_mode, stx.stx_ino, next_off); - ++pd; - if (pd == dir->readdir_cache.end()) + if (idx + 1 == dir->readdir_cache.size()) next_off = dir_result_t::END; Inode *in = NULL; @@ -9517,6 +9512,7 @@ int Client::_readdir_cache_cb(dir_result_t *dirp, add_dirent_cb_t cb, void *p, dn_name = dn->name; // fill in name while we have lock + // the content of readdir_cache may change after unlocking client_lock.unlock(); r = cb(p, &de, &stx, next_off, in); // _next_ offset client_lock.lock();