Skip to content

Commit c88f0b0

Browse files
amotinmmatuska
authored andcommitted
ZIO: Add overflow checks for linear buffers
Since we use a limited set of kmem caches, quite often we have unused memory after the end of the buffer. Put there up to a 512-byte canary when built with debug to detect buffer overflows at the free time. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Alexander Motin <[email protected]> Sponsored by: iXsystems, Inc. Closes openzfs#15553
1 parent db2db50 commit c88f0b0

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

lib/libspl/include/assert.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ libspl_assert(const char *buf, const char *file, const char *func, int line)
6464
#undef verify
6565
#endif
6666

67+
#define PANIC(fmt, a...) \
68+
libspl_assertf(__FILE__, __FUNCTION__, __LINE__, fmt, ## a)
69+
6770
#define VERIFY(cond) \
6871
(void) ((!(cond)) && \
6972
libspl_assert(#cond, __FILE__, __FUNCTION__, __LINE__))

module/zfs/zio.c

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,53 @@ zio_fini(void)
306306
* ==========================================================================
307307
*/
308308

309+
#ifdef ZFS_DEBUG
310+
static const ulong_t zio_buf_canary = (ulong_t)0xdeadc0dedead210b;
311+
#endif
312+
313+
/*
314+
* Use empty space after the buffer to detect overflows.
315+
*
316+
* Since zio_init() creates kmem caches only for certain set of buffer sizes,
317+
* allocations of different sizes may have some unused space after the data.
318+
* Filling part of that space with a known pattern on allocation and checking
319+
* it on free should allow us to detect some buffer overflows.
320+
*/
321+
static void
322+
zio_buf_put_canary(ulong_t *p, size_t size, kmem_cache_t **cache, size_t c)
323+
{
324+
#ifdef ZFS_DEBUG
325+
size_t off = P2ROUNDUP(size, sizeof (ulong_t));
326+
ulong_t *canary = p + off / sizeof (ulong_t);
327+
size_t asize = (c + 1) << SPA_MINBLOCKSHIFT;
328+
if (c + 1 < SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT &&
329+
cache[c] == cache[c + 1])
330+
asize = (c + 2) << SPA_MINBLOCKSHIFT;
331+
for (; off < asize; canary++, off += sizeof (ulong_t))
332+
*canary = zio_buf_canary;
333+
#endif
334+
}
335+
336+
static void
337+
zio_buf_check_canary(ulong_t *p, size_t size, kmem_cache_t **cache, size_t c)
338+
{
339+
#ifdef ZFS_DEBUG
340+
size_t off = P2ROUNDUP(size, sizeof (ulong_t));
341+
ulong_t *canary = p + off / sizeof (ulong_t);
342+
size_t asize = (c + 1) << SPA_MINBLOCKSHIFT;
343+
if (c + 1 < SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT &&
344+
cache[c] == cache[c + 1])
345+
asize = (c + 2) << SPA_MINBLOCKSHIFT;
346+
for (; off < asize; canary++, off += sizeof (ulong_t)) {
347+
if (unlikely(*canary != zio_buf_canary)) {
348+
PANIC("ZIO buffer overflow %p (%zu) + %zu %#lx != %#lx",
349+
p, size, (canary - p) * sizeof (ulong_t),
350+
*canary, zio_buf_canary);
351+
}
352+
}
353+
#endif
354+
}
355+
309356
/*
310357
* Use zio_buf_alloc to allocate ZFS metadata. This data will appear in a
311358
* crashdump if the kernel panics, so use it judiciously. Obviously, it's
@@ -322,7 +369,9 @@ zio_buf_alloc(size_t size)
322369
atomic_add_64(&zio_buf_cache_allocs[c], 1);
323370
#endif
324371

325-
return (kmem_cache_alloc(zio_buf_cache[c], KM_PUSHPAGE));
372+
void *p = kmem_cache_alloc(zio_buf_cache[c], KM_PUSHPAGE);
373+
zio_buf_put_canary(p, size, zio_buf_cache, c);
374+
return (p);
326375
}
327376

328377
/*
@@ -338,7 +387,9 @@ zio_data_buf_alloc(size_t size)
338387

339388
VERIFY3U(c, <, SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);
340389

341-
return (kmem_cache_alloc(zio_data_buf_cache[c], KM_PUSHPAGE));
390+
void *p = kmem_cache_alloc(zio_data_buf_cache[c], KM_PUSHPAGE);
391+
zio_buf_put_canary(p, size, zio_data_buf_cache, c);
392+
return (p);
342393
}
343394

344395
void
@@ -351,6 +402,7 @@ zio_buf_free(void *buf, size_t size)
351402
atomic_add_64(&zio_buf_cache_frees[c], 1);
352403
#endif
353404

405+
zio_buf_check_canary(buf, size, zio_buf_cache, c);
354406
kmem_cache_free(zio_buf_cache[c], buf);
355407
}
356408

@@ -361,6 +413,7 @@ zio_data_buf_free(void *buf, size_t size)
361413

362414
VERIFY3U(c, <, SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);
363415

416+
zio_buf_check_canary(buf, size, zio_data_buf_cache, c);
364417
kmem_cache_free(zio_data_buf_cache[c], buf);
365418
}
366419

0 commit comments

Comments
 (0)