mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-03-10 20:36:21 +03:00
Zstd: Fix ASan poisoning for pooled Zstd contexts
The Zstd context mempool can reuse buffers that were previously poisoned under AddressSanitizer, leading to false-positive use-after-poison reports during zloop and other stress tests. Explicitly unpoison memory when handing buffers out to Zstd and poison the user-visible region again when buffers are returned to the pool. This makes the allocator ASan-correct while preserving existing pooling behavior. Also fix non-standard void * pointer arithmetic in zstd_free() and remove an early return in zstd_dctx_alloc() so kmem_type/kmem_size are always set on pool hits. This only affects ASan bookkeeping in user space, does not change runtime behavior in non-ASan configurations, and does not affect on-disk formats. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Alexander Moch <mail@alexmoch.com> Closes #18089
This commit is contained in:
parent
a2ac9cd606
commit
e7f9734bc7
@ -264,6 +264,8 @@ static struct zstd_pool *zstd_mempool_dctx;
|
||||
#if defined(ZFS_ASAN_ENABLED)
|
||||
#define ADDRESS_SANITIZER 1
|
||||
#endif
|
||||
|
||||
/* Kernel space. */
|
||||
#if defined(_KERNEL) && defined(ADDRESS_SANITIZER)
|
||||
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
|
||||
void __asan_poison_memory_region(void const volatile *addr, size_t size);
|
||||
@ -271,6 +273,14 @@ void __asan_unpoison_memory_region(void const volatile *addr, size_t size) {};
|
||||
void __asan_poison_memory_region(void const volatile *addr, size_t size) {};
|
||||
#endif
|
||||
|
||||
/* User space. */
|
||||
#if defined(ADDRESS_SANITIZER) && !defined(_KERNEL)
|
||||
#define ZSTD_ASAN_POISON(p, n) __asan_poison_memory_region((p), (n))
|
||||
#define ZSTD_ASAN_UNPOISON(p, n) __asan_unpoison_memory_region((p), (n))
|
||||
#else
|
||||
#define ZSTD_ASAN_POISON(p, n) do { } while (0)
|
||||
#define ZSTD_ASAN_UNPOISON(p, n) do { } while (0)
|
||||
#endif
|
||||
|
||||
static void
|
||||
zstd_mempool_reap(struct zstd_pool *zstd_mempool)
|
||||
@ -408,6 +418,10 @@ zstd_mempool_alloc(struct zstd_pool *zstd_mempool, size_t size)
|
||||
static void
|
||||
zstd_mempool_free(struct zstd_kmem *z)
|
||||
{
|
||||
/* Poison only the user-visible region (exclude header). */
|
||||
ZSTD_ASAN_POISON((char *)z + sizeof (struct zstd_kmem),
|
||||
z->kmem_size - sizeof (struct zstd_kmem));
|
||||
|
||||
mutex_exit(&z->pool->barrier);
|
||||
}
|
||||
|
||||
@ -689,7 +703,6 @@ ZFS_COMPRESS_WRAP_DECL(zfs_zstd_compress)
|
||||
ZFS_DECOMPRESS_WRAP_DECL(zfs_zstd_decompress)
|
||||
ZFS_DECOMPRESS_LEVEL_WRAP_DECL(zfs_zstd_decompress_level)
|
||||
|
||||
|
||||
/* Allocator for zstd compression context using mempool_allocator */
|
||||
static void *
|
||||
zstd_alloc(void *opaque __maybe_unused, size_t size)
|
||||
@ -704,7 +717,9 @@ zstd_alloc(void *opaque __maybe_unused, size_t size)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return ((void*)z + (sizeof (struct zstd_kmem)));
|
||||
void *p = (char *)z + sizeof (struct zstd_kmem);
|
||||
ZSTD_ASAN_UNPOISON(p, size);
|
||||
return (p);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -719,15 +734,15 @@ zstd_dctx_alloc(void *opaque __maybe_unused, size_t size)
|
||||
enum zstd_kmem_type type = ZSTD_KMEM_DEFAULT;
|
||||
|
||||
z = (struct zstd_kmem *)zstd_mempool_alloc(zstd_mempool_dctx, nbytes);
|
||||
if (!z) {
|
||||
if (z) {
|
||||
type = ZSTD_KMEM_POOL;
|
||||
} else {
|
||||
/* Try harder, decompression shall not fail */
|
||||
z = vmem_alloc(nbytes, KM_SLEEP);
|
||||
if (z) {
|
||||
z->pool = NULL;
|
||||
}
|
||||
ZSTDSTAT_BUMP(zstd_stat_alloc_fail);
|
||||
} else {
|
||||
return ((void*)z + (sizeof (struct zstd_kmem)));
|
||||
}
|
||||
|
||||
/* Fallback if everything fails */
|
||||
@ -752,14 +767,17 @@ zstd_dctx_alloc(void *opaque __maybe_unused, size_t size)
|
||||
z->kmem_type = type;
|
||||
z->kmem_size = nbytes;
|
||||
|
||||
return ((void*)z + (sizeof (struct zstd_kmem)));
|
||||
void *p = (char *)z + sizeof (struct zstd_kmem);
|
||||
ZSTD_ASAN_UNPOISON(p, size);
|
||||
return (p);
|
||||
}
|
||||
|
||||
/* Free allocated memory by its specific type */
|
||||
static void
|
||||
zstd_free(void *opaque __maybe_unused, void *ptr)
|
||||
{
|
||||
struct zstd_kmem *z = (ptr - sizeof (struct zstd_kmem));
|
||||
struct zstd_kmem *z =
|
||||
(struct zstd_kmem *)((char *)ptr - sizeof (struct zstd_kmem));
|
||||
enum zstd_kmem_type type;
|
||||
|
||||
ASSERT3U(z->kmem_type, <, ZSTD_KMEM_COUNT);
|
||||
@ -774,6 +792,8 @@ zstd_free(void *opaque __maybe_unused, void *ptr)
|
||||
zstd_mempool_free(z);
|
||||
break;
|
||||
case ZSTD_KMEM_DCTX:
|
||||
/* Poison fallback user region on release. */
|
||||
ZSTD_ASAN_POISON(ptr, z->kmem_size - sizeof (struct zstd_kmem));
|
||||
mutex_exit(&zstd_dctx_fallback.barrier);
|
||||
break;
|
||||
default:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user