diff --git a/include/sys/sa.h b/include/sys/sa.h index 48e3bcd7cdf3..25bd31bd6ec5 100644 --- a/include/sys/sa.h +++ b/include/sys/sa.h @@ -101,6 +101,11 @@ typedef struct sa_bulk_attr { b[idx++].sa_length = len; \ } +/* + * The on-disk format of sa_hdr_phys_t limits SA lengths to 16-bit values. + */ +#define SA_ATTR_MAX_LEN ((1 << (sizeof (uint16_t) * 8)) - 1) + typedef struct sa_os sa_os_t; typedef enum sa_handle_type { diff --git a/module/zfs/sa.c b/module/zfs/sa.c index 2383252e2447..bb8ad4a927f8 100644 --- a/module/zfs/sa.c +++ b/module/zfs/sa.c @@ -1836,6 +1836,8 @@ sa_update(sa_handle_t *hdl, sa_attr_type_t type, int error; sa_bulk_attr_t bulk; + VERIFY3U(buflen, <=, SA_ATTR_MAX_LEN); + bulk.sa_attr = type; bulk.sa_data_func = NULL; bulk.sa_length = buflen; diff --git a/module/zfs/zfs_sa.c b/module/zfs/zfs_sa.c index c9a9da7528d7..86c565c47407 100644 --- a/module/zfs/zfs_sa.c +++ b/module/zfs/zfs_sa.c @@ -231,6 +231,8 @@ zfs_sa_set_xattr(znode_t *zp) error = nvlist_size(zp->z_xattr_cached, &size, NV_ENCODE_XDR); if (error) goto out; + if (size > SA_ATTR_MAX_LEN) + return (EFBIG); obj = zio_buf_alloc(size); @@ -247,12 +249,9 @@ zfs_sa_set_xattr(znode_t *zp) if (error) { dmu_tx_abort(tx); } else { - error = sa_update(zp->z_sa_hdl, SA_ZPL_DXATTR(zsb), - obj, size, tx); - if (error) - dmu_tx_abort(tx); - else - dmu_tx_commit(tx); + VERIFY0(sa_update(zp->z_sa_hdl, SA_ZPL_DXATTR(zsb), + obj, size, tx)); + dmu_tx_commit(tx); } out_free: zio_buf_free(obj, size); diff --git a/module/zfs/zpl_xattr.c b/module/zfs/zpl_xattr.c index d9d0673051e4..8859fd02006d 100644 --- a/module/zfs/zpl_xattr.c +++ b/module/zfs/zpl_xattr.c @@ -478,8 +478,11 @@ zpl_xattr_set_sa(struct inode *ip, const char *name, const void *value, } /* Update the SA for additions, modifications, and removals. */ - if (!error) + if (!error) { error = -zfs_sa_set_xattr(zp); + if (error && (value != NULL)) + (void) nvlist_remove(nvl, name, DATA_TYPE_BYTE_ARRAY); + } ASSERT3S(error, <=, 0);