Skip to content

Commit adcea23

Browse files
authored
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 #15553
1 parent 3e4bef5 commit adcea23

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
@@ -295,6 +295,53 @@ zio_fini(void)
295295
* ==========================================================================
296296
*/
297297

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

314-
return (kmem_cache_alloc(zio_buf_cache[c], KM_PUSHPAGE));
361+
void *p = kmem_cache_alloc(zio_buf_cache[c], KM_PUSHPAGE);
362+
zio_buf_put_canary(p, size, zio_buf_cache, c);
363+
return (p);
315364
}
316365

317366
/*
@@ -327,7 +376,9 @@ zio_data_buf_alloc(size_t size)
327376

328377
VERIFY3U(c, <, SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);
329378

330-
return (kmem_cache_alloc(zio_data_buf_cache[c], KM_PUSHPAGE));
379+
void *p = kmem_cache_alloc(zio_data_buf_cache[c], KM_PUSHPAGE);
380+
zio_buf_put_canary(p, size, zio_data_buf_cache, c);
381+
return (p);
331382
}
332383

333384
void
@@ -340,6 +391,7 @@ zio_buf_free(void *buf, size_t size)
340391
atomic_add_64(&zio_buf_cache_frees[c], 1);
341392
#endif
342393

394+
zio_buf_check_canary(buf, size, zio_buf_cache, c);
343395
kmem_cache_free(zio_buf_cache[c], buf);
344396
}
345397

@@ -350,6 +402,7 @@ zio_data_buf_free(void *buf, size_t size)
350402

351403
VERIFY3U(c, <, SPA_MAXBLOCKSIZE >> SPA_MINBLOCKSHIFT);
352404

405+
zio_buf_check_canary(buf, size, zio_data_buf_cache, c);
353406
kmem_cache_free(zio_data_buf_cache[c], buf);
354407
}
355408

0 commit comments

Comments
 (0)