Add buffer corruption protection (RedZone) for kernel's malloc(9).

It detects both: buffer underflows and buffer overflows bugs at runtime
(on free(9) and realloc(9)) and prints backtraces from where memory was
allocated and from where it was freed.

Tested by:	kris
This commit is contained in:
Pawel Jakub Dawidek 2006-01-31 11:09:21 +00:00
parent f0107b2c5d
commit 847a2a1716
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=155086
8 changed files with 375 additions and 1 deletions

View File

@ -188,6 +188,7 @@ MAN= accept_filter.9 \
psignal.9 \
random.9 \
random_harvest.9 \
redzone.9 \
resettodr.9 \
resource_int_value.9 \
rijndael.9 \

123
share/man/man9/redzone.9 Normal file
View File

@ -0,0 +1,123 @@
.\" Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd January 31, 2006
.Dt REDZONE 9
.Os
.Sh NAME
.Nm RedZone
.Nd "buffer corruptions detector"
.Sh SYNOPSIS
.Cd "options DEBUG_REDZONE"
.Sh DESCRIPTION
.Nm
detects buffer underflow and buffer overflow bugs at runtime.
Currently
.Nm
only detects buffer corruptions for memory allocated with
.Xr malloc 9 .
When such corruption is detected two backtraces are printed on the console.
The first one shows from where memory was allocated, the second one shows from
where memory was freed.
By default the system will not panic when buffer corruption is detected.
This can be changed by setting the
.Va vm.redzone.panic
.Xr sysctl 8
variable to 1.
The amount of extra memory allocated for
.Nm Ns 's
needs is stored in the
.Va vm.redzone.extra_mem
.Xr sysctl 8
variable.
.Sh EXAMPLE
The example below shows the logs from the detection of a buffer underflow and a
buffer overflow.
.Bd -literal -offset indent
REDZONE: Buffer underflow detected. 2 bytes corrupted before 0xc8688580 (16 bytes allocated).
Allocation backtrace:
#0 0xc0583e4e at redzone_setup+0x3c
#1 0xc04a23fa at malloc+0x19e
#2 0xcdeb69ca at redzone_modevent+0x60
#3 0xc04a3f3c at module_register_init+0x82
#4 0xc049d96a at linker_file_sysinit+0x8e
#5 0xc049dc7c at linker_load_file+0xed
#6 0xc04a041f at linker_load_module+0xc4
#7 0xc049e883 at kldload+0x116
#8 0xc05d9b3d at syscall+0x325
#9 0xc05c944f at Xint0x80_syscall+0x1f
Free backtrace:
#0 0xc0583f92 at redzone_check+0xd4
#1 0xc04a2422 at free+0x1c
#2 0xcdeb69a6 at redzone_modevent+0x3c
#3 0xc04a438d at module_unload+0x61
#4 0xc049e0b3 at linker_file_unload+0x89
#5 0xc049e979 at kern_kldunload+0x96
#6 0xc049ea00 at kldunloadf+0x2c
#7 0xc05d9b3d at syscall+0x325
#8 0xc05c944f at Xint0x80_syscall+0x1f
REDZONE: Buffer overflow detected. 4 bytes corrupted after 0xc8688590 (16 bytes allocated).
Allocation backtrace:
#0 0xc0583e4e at redzone_setup+0x3c
#1 0xc04a23fa at malloc+0x19e
#2 0xcdeb69ca at redzone_modevent+0x60
#3 0xc04a3f3c at module_register_init+0x82
#4 0xc049d96a at linker_file_sysinit+0x8e
#5 0xc049dc7c at linker_load_file+0xed
#6 0xc04a041f at linker_load_module+0xc4
#7 0xc049e883 at kldload+0x116
#8 0xc05d9b3d at syscall+0x325
#9 0xc05c944f at Xint0x80_syscall+0x1f
Free backtrace:
#0 0xc0584020 at redzone_check+0x162
#1 0xc04a2422 at free+0x1c
#2 0xcdeb69a6 at redzone_modevent+0x3c
#3 0xc04a438d at module_unload+0x61
#4 0xc049e0b3 at linker_file_unload+0x89
#5 0xc049e979 at kern_kldunload+0x96
#6 0xc049ea00 at kldunloadf+0x2c
#7 0xc05d9b3d at syscall+0x325
#8 0xc05c944f at Xint0x80_syscall+0x1f
.Ed
.Sh SEE ALSO
.Xr sysctl 8 ,
.Xr malloc 9 ,
.Xr memguard 9
.Sh HISTORY
.Nm
first appeared in
.Fx 7.0 .
.Sh AUTHORS
.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org
.Sh BUGS
Currently,
.Nm
does not cooperate with
.Xr memguard 9 .
Allocations from a memory type controlled by
.Xr memguard 9
are simply skipped, so buffer corruptions will not be detected there.

View File

@ -326,6 +326,12 @@ options SYSCTL_DEBUG
#
options DEBUG_MEMGUARD
#
# DEBUG_REDZONE enables buffer underflows and buffer overflows detection for
# malloc(9).
#
options DEBUG_REDZONE
#
# KTRACE enables the system-call tracing facility ktrace(2). To be more
# SMP-friendly, KTRACE uses a worker thread to process most trace events

View File

@ -1867,6 +1867,7 @@ ufs/ufs/ufs_vnops.c optional ffs
vm/default_pager.c standard
vm/device_pager.c standard
vm/phys_pager.c standard
vm/redzone.c optional DEBUG_REDZONE
vm/swap_pager.c standard
vm/uma_core.c standard
vm/uma_dbg.c standard

View File

@ -530,6 +530,9 @@ PQ_NOOPT opt_vmpage.h
# The MemGuard replacement allocator used for tamper-after-free detection
DEBUG_MEMGUARD opt_vm.h
# The RedZone malloc(9) protection
DEBUG_REDZONE opt_vm.h
# Standard SMP options
SMP opt_global.h

View File

@ -65,6 +65,9 @@ __FBSDID("$FreeBSD$");
#ifdef DEBUG_MEMGUARD
#include <vm/memguard.h>
#endif
#ifdef DEBUG_REDZONE
#include <vm/redzone.h>
#endif
#if defined(INVARIANTS) && defined(__i386__)
#include <machine/cpu.h>
@ -259,7 +262,7 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags)
caddr_t va;
uma_zone_t zone;
uma_keg_t keg;
#ifdef DIAGNOSTIC
#if defined(DIAGNOSTIC) || defined(DEBUG_REDZONE)
unsigned long osize = size;
#endif
@ -302,6 +305,10 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags)
return memguard_alloc(size, flags);
#endif
#ifdef DEBUG_REDZONE
size = redzone_size_ntor(size);
#endif
if (size <= KMEM_ZMAX) {
if (size & KMEM_ZMASK)
size = (size & ~KMEM_ZMASK) + KMEM_ZBASE;
@ -330,6 +337,10 @@ malloc(unsigned long size, struct malloc_type *mtp, int flags)
if (va != NULL && !(flags & M_ZERO)) {
memset(va, 0x70, osize);
}
#endif
#ifdef DEBUG_REDZONE
if (va != NULL)
va = redzone_setup(va, osize);
#endif
return ((void *) va);
}
@ -358,6 +369,11 @@ free(void *addr, struct malloc_type *mtp)
}
#endif
#ifdef DEBUG_REDZONE
redzone_check(addr);
addr = redzone_addr_ntor(addr);
#endif
size = 0;
slab = vtoslab((vm_offset_t)addr & (~UMA_SLAB_MASK));
@ -421,6 +437,10 @@ if (memguard_cmp(mtp)) {
} else {
#endif
#ifdef DEBUG_REDZONE
slab = NULL;
alloc = redzone_get_size(addr);
#else
slab = vtoslab((vm_offset_t)addr & ~(UMA_SLAB_MASK));
/* Sanity check */
@ -437,6 +457,7 @@ if (memguard_cmp(mtp)) {
if (size <= alloc
&& (size > (alloc >> REALLOC_FRACTION) || alloc == MINALLOCSIZE))
return (addr);
#endif /* !DEBUG_REDZONE */
#ifdef DEBUG_MEMGUARD
}

181
sys/vm/redzone.c Normal file
View File

@ -0,0 +1,181 @@
/*-
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/stack.h>
#include <sys/sysctl.h>
#include <vm/redzone.h>
SYSCTL_NODE(_vm, OID_AUTO, redzone, CTLFLAG_RW, NULL, "RedZone data");
static u_long redzone_extra_mem = 0;
SYSCTL_ULONG(_vm_redzone, OID_AUTO, extra_mem, CTLFLAG_RD, &redzone_extra_mem,
0, "Extra memory allocated by redzone");
static int redzone_panic = 0;
TUNABLE_INT("vm.redzone.panic", &redzone_panic);
SYSCTL_INT(_vm_redzone, OID_AUTO, panic, CTLFLAG_RW, &redzone_panic, 0,
"Panic when buffer corruption is detected");
#define REDZONE_CHSIZE (16)
#define REDZONE_CFSIZE (16)
#define REDZONE_HSIZE (sizeof(struct stack) + sizeof(u_long) + REDZONE_CHSIZE)
#define REDZONE_FSIZE (REDZONE_CFSIZE)
static u_long
redzone_roundup(u_long n)
{
if (n <= 128)
return (128);
else if (n <= 256)
return (256);
else if (n <= 512)
return (512);
else if (n <= 1024)
return (1024);
else if (n <= 2048)
return (2048);
return (PAGE_SIZE);
}
u_long
redzone_get_size(caddr_t naddr)
{
u_long nsize;
bcopy(naddr - REDZONE_CHSIZE - sizeof(u_long), &nsize, sizeof(nsize));
return (nsize);
}
u_long
redzone_size_ntor(u_long nsize)
{
return (nsize + redzone_roundup(nsize) + REDZONE_FSIZE);
}
void *
redzone_addr_ntor(caddr_t naddr)
{
return (naddr - redzone_roundup(redzone_get_size(naddr)));
}
/*
* Set redzones and remember allocation backtrace.
*/
void *
redzone_setup(caddr_t raddr, u_long nsize)
{
struct stack st;
caddr_t haddr, faddr;
atomic_add_long(&redzone_extra_mem, redzone_size_ntor(nsize) - nsize);
haddr = raddr + redzone_roundup(nsize) - REDZONE_HSIZE;
faddr = haddr + REDZONE_HSIZE + nsize;
/* Redzone header. */
stack_save(&st);
bcopy(&st, haddr, sizeof(st));
haddr += sizeof(st);
bcopy(&nsize, haddr, sizeof(nsize));
haddr += sizeof(nsize);
memset(haddr, 0x42, REDZONE_CHSIZE);
haddr += REDZONE_CHSIZE;
/* Redzone footer. */
memset(faddr, 0x42, REDZONE_CFSIZE);
return (haddr);
}
/*
* Verify redzones.
* This function is called on free() and realloc().
*/
void
redzone_check(caddr_t naddr)
{
struct stack ast, fst;
caddr_t haddr, faddr;
u_int ncorruptions;
u_long nsize;
int i;
haddr = naddr - REDZONE_HSIZE;
bcopy(haddr, &ast, sizeof(ast));
haddr += sizeof(ast);
bcopy(haddr, &nsize, sizeof(nsize));
haddr += sizeof(nsize);
atomic_subtract_long(&redzone_extra_mem,
redzone_size_ntor(nsize) - nsize);
/* Look for buffer underflow. */
ncorruptions = 0;
for (i = 0; i < REDZONE_CHSIZE; i++, haddr++) {
if (*(u_char *)haddr != 0x42)
ncorruptions++;
}
if (ncorruptions > 0) {
printf("REDZONE: Buffer underflow detected. %u byte%s "
"corrupted before %p (%lu bytes allocated).\n",
ncorruptions, ncorruptions == 1 ? "" : "s", naddr, nsize);
printf("Allocation backtrace:\n");
stack_print(&ast);
printf("Free backtrace:\n");
stack_save(&fst);
stack_print(&fst);
if (redzone_panic)
panic("Stopping here.");
}
faddr = naddr + nsize;
/* Look for buffer overflow. */
ncorruptions = 0;
for (i = 0; i < REDZONE_CFSIZE; i++, faddr++) {
if (*(u_char *)faddr != 0x42)
ncorruptions++;
}
if (ncorruptions > 0) {
printf("REDZONE: Buffer overflow detected. %u byte%s corrupted "
"after %p (%lu bytes allocated).\n", ncorruptions,
ncorruptions == 1 ? "" : "s", naddr + nsize, nsize);
printf("Allocation backtrace:\n");
stack_print(&ast);
printf("Free backtrace:\n");
stack_save(&fst);
stack_print(&fst);
if (redzone_panic)
panic("Stopping here.");
}
}

38
sys/vm/redzone.h Normal file
View File

@ -0,0 +1,38 @@
/*-
* Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _VM_REDZONE_H_
#define _VM_REDZONE_H_
u_long redzone_get_size(caddr_t naddr);
u_long redzone_size_ntor(u_long nsize);
void *redzone_addr_ntor(caddr_t naddr);
void *redzone_setup(caddr_t raddr, u_long nsize);
void redzone_check(caddr_t naddr);
#endif /* _VM_REDZONE_H_ */