From 641e5f73d3bd5b3d32cafd551013d3bfd2a52732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Fri, 4 Aug 2023 12:19:49 +0200 Subject: [PATCH] mc: recover from invalid memory cache size If we access the mmap file outside its boundaries a SIGBUS is raised. We can now safely recover if the file has unexpected size. Reviewed-by: Alexey Tikhonov Reviewed-by: Sumit Bose --- src/responder/nss/nsssrv_mmap_cache.c | 86 +++++++++++++++++---- src/sss_client/nss_mc_common.c | 42 +++++++--- 2 files changed, 135 insertions(+), 29 deletions(-) diff --git a/src/responder/nss/nsssrv_mmap_cache.c b/src/responder/nss/nsssrv_mmap_cache.c index 12c2996596..bd814f3bc7 100644 --- a/src/responder/nss/nsssrv_mmap_cache.c +++ b/src/responder/nss/nsssrv_mmap_cache.c @@ -722,6 +722,57 @@ static errno_t sss_mmap_cache_invalidate(struct sss_mc_ctx *mcc, return EOK; } +static errno_t sss_mmap_cache_validate_or_reinit(struct sss_mc_ctx **_mcc) +{ + struct sss_mc_ctx *mcc = *_mcc; + struct stat fdstat; + bool reinit = false; + errno_t ret; + + /* No mcc initialized? Memory cache may be disabled. */ + if (mcc == NULL || mcc->fd < 0) { + ret = EINVAL; + reinit = false; + goto done; + } + + if (fstat(mcc->fd, &fdstat) == -1) { + ret = errno; + DEBUG(SSSDBG_CRIT_FAILURE, + "Unable to stat memory cache [file=%s, fd=%d] [%d]: %s\n", + mcc->file, mcc->fd, ret, sss_strerror(ret)); + reinit = true; + goto done; + } + + if (fdstat.st_nlink == 0) { + DEBUG(SSSDBG_CRIT_FAILURE, "Memory cache file was removed\n"); + ret = ENOENT; + reinit = true; + goto done; + } + + if (fdstat.st_size != mcc->mmap_size) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Memory cache is corrupted, invalid size [file=%s, fd=%d, " + "expected_size=%zu, real_size=%zu]\n", + mcc->file, mcc->fd, mcc->mmap_size, fdstat.st_size); + ret = EINVAL; + reinit = true; + goto done; + } + + ret = EOK; + reinit = false; + +done: + if (reinit) { + return sss_mmap_cache_reinit(talloc_parent(mcc), -1, -1, -1, -1, _mcc); + } + + return ret; +} + /*************************************************************************** * passwd map ***************************************************************************/ @@ -744,9 +795,9 @@ errno_t sss_mmap_cache_pw_store(struct sss_mc_ctx **_mcc, size_t pos; int ret; - if (mcc == NULL) { - /* cache not initialized? */ - return EINVAL; + ret = sss_mmap_cache_validate_or_reinit(&mcc); + if (ret != EOK) { + return ret; } ret = snprintf(uidstr, 11, "%ld", (long)uid); @@ -815,9 +866,9 @@ errno_t sss_mmap_cache_pw_invalidate_uid(struct sss_mc_ctx *mcc, uid_t uid) char *uidstr; errno_t ret; - if (mcc == NULL) { - /* cache not initialized? */ - return EINVAL; + ret = sss_mmap_cache_validate_or_reinit(&mcc); + if (ret != EOK) { + return ret; } uidstr = talloc_asprintf(NULL, "%ld", (long)uid); @@ -886,9 +937,9 @@ int sss_mmap_cache_gr_store(struct sss_mc_ctx **_mcc, size_t pos; int ret; - if (mcc == NULL) { - /* cache not initialized? */ - return EINVAL; + ret = sss_mmap_cache_validate_or_reinit(&mcc); + if (ret != EOK) { + return ret; } ret = snprintf(gidstr, 11, "%ld", (long)gid); @@ -953,9 +1004,9 @@ errno_t sss_mmap_cache_gr_invalidate_gid(struct sss_mc_ctx *mcc, gid_t gid) char *gidstr; errno_t ret; - if (mcc == NULL) { - /* cache not initialized? */ - return EINVAL; + ret = sss_mmap_cache_validate_or_reinit(&mcc); + if (ret != EOK) { + return ret; } gidstr = talloc_asprintf(NULL, "%ld", (long)gid); @@ -1018,9 +1069,9 @@ errno_t sss_mmap_cache_initgr_store(struct sss_mc_ctx **_mcc, size_t pos; int ret; - if (mcc == NULL) { - /* cache not initialized? */ - return EINVAL; + ret = sss_mmap_cache_validate_or_reinit(&mcc); + if (ret != EOK) { + return ret; } /* array of gids + name + unique_name */ @@ -1087,8 +1138,9 @@ errno_t sss_mmap_cache_sid_store(struct sss_mc_ctx **_mcc, size_t rec_len; int ret; - if (mcc == NULL) { - return EINVAL; + ret = sss_mmap_cache_validate_or_reinit(&mcc); + if (ret != EOK) { + return ret; } ret = snprintf(idkey, sizeof(idkey), "%d-%ld", diff --git a/src/sss_client/nss_mc_common.c b/src/sss_client/nss_mc_common.c index 3128861bf3..e227c0bae3 100644 --- a/src/sss_client/nss_mc_common.c +++ b/src/sss_client/nss_mc_common.c @@ -69,13 +69,43 @@ static void sss_mt_unlock(struct sss_cli_mc_ctx *ctx) #endif } +static errno_t sss_nss_mc_validate(struct sss_cli_mc_ctx *ctx) +{ + struct stat fdstat; + + /* No mc ctx initialized?*/ + if (ctx == NULL || ctx->fd < 0) { + return EINVAL; + } + + if (fstat(ctx->fd, &fdstat) == -1) { + return errno; + } + + /* Memcache was removed. */ + if (fdstat.st_nlink == 0) { + return ENOENT; + } + + /* Invalid size. */ + if (fdstat.st_size != ctx->mmap_size) { + return ERANGE; + } + + return EOK; +} + errno_t sss_nss_check_header(struct sss_cli_mc_ctx *ctx) { struct sss_mc_header h; bool copy_ok; int count; int ret; - struct stat fdstat; + + ret = sss_nss_mc_validate(ctx); + if (ret != EOK) { + return ret; + } /* retry barrier protected reading max 5 times then give up */ for (count = 5; count > 0; count--) { @@ -115,16 +145,6 @@ errno_t sss_nss_check_header(struct sss_cli_mc_ctx *ctx) } } - ret = fstat(ctx->fd, &fdstat); - if (ret == -1) { - return EIO; - } - - if (fdstat.st_nlink == 0) { - /* memory cache was removed; we need to reinitialize it. */ - return EINVAL; - } - return 0; }