diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index e8d0661c4b..454761d81d 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -81,6 +81,11 @@ typedef struct ExtendedOps { #define V9FS_SEC_MASK 0x0000003C +/* + * Limits the maximum amount of simultaneously open xattr FIDs to prevent + * host memory exhaustion (as each xattr FID contains a xattr value buffer). + */ +#define V9FS_MAX_XATTR_DEFAULT 1024 typedef struct FileOperations FileOperations; typedef struct XattrOperations XattrOperations; @@ -109,6 +114,10 @@ struct FsContext { void *private; mode_t fmode; mode_t dmode; + /* max. amount of simultaneously open xattr FIDs */ + uint32_t xattr_fid_limit; + /* current amount of open xattr FIDs */ + uint32_t xattr_fid_count; }; struct V9fsPath { diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 09454a5404..e737629581 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -265,6 +265,31 @@ static size_t v9fs_string_size(V9fsString *str) return str->size; } +static int xattr_fid_count_inc(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + + if (s->ctx.xattr_fid_limit > 0 && + s->ctx.xattr_fid_count >= s->ctx.xattr_fid_limit) { + error_report_once("9pfs: xattr_fid_count limit exceeded " + "(configurable by option 'max_xattr')."); + return -ENOSPC; + } + s->ctx.xattr_fid_count++; + return 0; +} + +static void xattr_fid_count_decr(V9fsPDU *pdu) +{ + V9fsState *s = pdu->s; + + if (s->ctx.xattr_fid_count > 0) { + s->ctx.xattr_fid_count--; + } else { + error_report_once("9pfs: xattr_fid_count underflow detected"); + } +} + /* * returns 0 if fid got re-opened, 1 if not, < 0 on error */ @@ -397,6 +422,7 @@ static int coroutine_fn free_fid(V9fsPDU *pdu, V9fsFidState *fidp) } } else if (fidp->fid_type == P9_FID_XATTR) { retval = v9fs_xattr_fid_clunk(pdu, fidp); + xattr_fid_count_decr(pdu); } v9fs_path_free(&fidp->path); g_free(fidp); @@ -634,6 +660,14 @@ static void coroutine_fn virtfs_reset(V9fsPDU *pdu) fidp->clunked = true; put_fid(pdu, fidp); } + + /* + * Explicitly reset the xattr FID counter. + * + * free_fid() already decrements the counter for each P9_FID_XATTR, so the + * counter should already be zero, hence this is just a defensive measure. + */ + s->ctx.xattr_fid_count = 0; } #define P9_QID_TYPE_DIR 0x80 @@ -4023,6 +4057,14 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) clunk_fid(s, xattr_fidp->fid); goto out; } + + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* * Read the xattr value */ @@ -4030,6 +4072,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; xattr_fidp->fs.xattr.value = g_malloc0(size); + if (size) { err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, xattr_fidp->fs.xattr.value, @@ -4056,6 +4099,14 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) clunk_fid(s, xattr_fidp->fid); goto out; } + + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + clunk_fid(s, xattr_fidp->fid); + goto out; + } + /* * Read the xattr value */ @@ -4063,6 +4114,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; xattr_fidp->fs.xattr.value = g_malloc0(size); + if (size) { err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, xattr_fidp->fs.xattr.value, @@ -4164,6 +4216,12 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque) goto out_put_fid; } + /* Check xattr FID limit */ + err = xattr_fid_count_inc(pdu); + if (err < 0) { + goto out_put_fid; + } + /* Make the file fid point to xattr */ xattr_fidp = file_fidp; xattr_fidp->fid_type = P9_FID_XATTR; @@ -4425,6 +4483,10 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, s->reclaiming = false; + /* init xattr FID limit */ + s->ctx.xattr_fid_limit = V9FS_MAX_XATTR_DEFAULT; + s->ctx.xattr_fid_count = 0; + rc = 0; out: if (rc) {