blob: 554c34f6e296b66d8cecc3a71b1cccda552f4c79 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2010-2016 Google, Inc. All rights reserved.
* Copyright (c) 2009-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/* Dr. Memory: the memory debugger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License, and no later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#ifdef UNIX
# include <unistd.h>
# include <signal.h>
# ifdef MACOS
# define _XOPEN_SOURCE 700 /* required to get POSIX, etc. defines out of ucontext.h */
# define __need_struct_ucontext64 /* seems to be missing from Mac headers */
# endif
# include <ucontext.h>
# include <errno.h>
/* just use single-arg handlers */
typedef void (*handler_t)(int);
typedef void (*handler_3_t)(int, siginfo_t *, void *);
#endif
#include <setjmp.h>
jmp_buf mark;
#ifdef UNIX
static void
signal_handler(int sig)
{
if (sig == SIGSEGV || sig == SIGBUS)
longjmp(mark, 1);
else
exit(1);
}
static void
intercept_signal(int sig, handler_t handler)
{
int rc;
struct sigaction act;
act.sa_sigaction = (handler_3_t) handler;
rc = sigemptyset(&act.sa_mask); /* block no signals within handler */
assert(rc == 0);
act.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
rc = sigaction(sig, &act, NULL);
assert(rc == 0);
}
#else
/* sort of a hack to avoid the MessageBox of the unhandled exception spoiling
* our batch runs
*/
# include <windows.h>
/* top-level exception handler */
static LONG
our_top_handler(struct _EXCEPTION_POINTERS * pExceptionInfo)
{
if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
longjmp(mark, 1);
}
return EXCEPTION_EXECUTE_HANDLER; /* => global unwind and silent death */
}
BOOL
is_pre_win8(void)
{
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
if (GetVersionEx(&info)) {
return (info.dwMajorVersion < 6 ||
(info.dwMajorVersion == 6 && info.dwMinorVersion < 2));
}
return FALSE;
}
#endif
static void *p2;
static void *p3;
int
main()
{
void *p1;
int x, *arr;
char c;
p1 = malloc(64);
free(p1);
printf("malloc\n");
for (x = 0; x < 20; x++) {
/* ensure we flag an error if reading the padding even though it
* is safe to access
*/
p1 = malloc(3);
c = *(((char *)p1)+3); /* error: unaddressable */
free(p1);
}
printf("malloc small\n");
p1 = malloc(0);
free(p1);
printf("malloc 0\n");
p1 = malloc(512*1024);
if (*(((char *)p1)+3) == 0) /* error: uninitialized */
c = 2;
/* PR 488643: test realloc via mremap */
p1 = realloc(p1, 1024*1024);
free(p1);
printf("malloc big\n");
p1 = calloc(3, sizeof(int));
x = *((int *)p1); /* ok: initialized to 0 */
free(p1);
printf("calloc\n");
p1 = malloc(64);
if (*(((char *)p1)+3) == 0) /* error: uninitialized */
c = 2;
p1 = realloc(p1, 128);
p1 = realloc(p1, sizeof(int)*2);
arr = (int *) p1;
arr[0] = 1;
arr[1] = 2;
p1 = realloc(p1, sizeof(int)*3);
arr = (int *) p1;
arr[2] = 3; /* shouldn't produce unaddr */
if ((arr[0] + arr[1] + arr[2]) != 6) /* shouldn't produce uninit */
printf("realloc\n");
free(p1);
arr = NULL;
/* PR 416535: test realloc(NULL, ), and on some linuxes, nested
* tailcall (PR 418138)
*/
p1 = realloc(NULL, 32);
free(p1);
/* PR 493870: test realloc(non-NULL, 0) */
p1 = malloc(37);
p1 = realloc(p1, 0);
/* get a 2nd malloc at same spot to test PR 493880 */
p1 = malloc(37);
free(p1);
#ifdef WINDOWS
/* HeapReAlloc has different behavior: (,0) does allocate a 0-sized chunk */
p1 = HeapAlloc(GetProcessHeap(), 0, 0xab);
p1 = HeapReAlloc(GetProcessHeap(), 0, p1, 0);
HeapFree(GetProcessHeap(), 0, p1);
#endif
printf("realloc\n");
/* invalid free: crashes so we have a try/except.
* glibc catches invalid free only at certain points near real mallocs.
*/
#ifdef UNIX
intercept_signal(SIGSEGV, signal_handler);
intercept_signal(SIGBUS, signal_handler); /* We see SIGBUS on Mac */
#else
SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) our_top_handler);
#endif
if (setjmp(mark) == 0)
free((void *)0x1230); /* i#916: addr must be 0x10 aligned */
printf("invalid free\n");
#if 0 /* avoiding double free b/c glibc reports it and aborts */
free(p1);
printf("double free\n");
#endif
#ifdef WINDOWS
p1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(int));
x = *((int *)p1); /* ok: initialized to 0 */
HeapFree(GetProcessHeap(), 0, p1);
p1 = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, NULL, sizeof(int));
HeapFree(GetProcessHeap(), 0, p1);
{ /* test failure of HeapFree due to invalid params */
HANDLE newheap = HeapCreate(0, 0, 0);
BOOL ok = TRUE;
char save[8]; /* to recover on win7 */
p1 = HeapAlloc(newheap, HEAP_ZERO_MEMORY, sizeof(int));
memcpy(save, (char *)p1 - sizeof(save), sizeof(save));
if (setjmp(mark) == 0) { /* crashes on win7 (i#515) */
/* i#1161: we cannot recover from a heap exception on win8 */
if (is_pre_win8())
ok = HeapFree(GetProcessHeap(), 0, p1);
else
ok = HeapFree(newheap, 0, (void*)0x300);
if (!ok) /* invalid Heap fails w/ 87 "The parameter is incorrect." */
printf("HeapFree failed %d\n", GetLastError());
} else
printf("HeapFree failed 87\n"); /* match non-crash error */
/* restore so we can try to free (else crashes again on win7) */
memcpy((char *)p1 - sizeof(save), save, sizeof(save));
ok = HeapFree(newheap, 0xffffffff, p1);
if (!ok) /* invalid flags do not cause failure */
printf("HeapFree failed %d\n", GetLastError());
HeapDestroy(newheap);
}
#endif
/* Test leaks. Avoid non-determinism due to the order of drmem's hashtable walk:
* for this test drmem's malloc table has 12 bits, so be sure to get the
* following allocs all in order in the table by not wrapping around in the
* bottom 12 bits. We assume all the allocs below take < 512 bytes. Note: this
* isn't always sufficient, but .res matches out-of-order now
*/
{
static char *p;
p = malloc(8); /* static so no leak */
free(p);
if (0xfff - ((int)(size_t)p & 0xfff) < 512) /* truncation ok in cast */
p = calloc(1, 0xfff - ((int)(size_t)p & 0xfff)); /* calloc for i#927 */
else
p = NULL; /* prevent from pointing where p1 will be (i#340) */
}
/* error: both leaked, though one points to other neither is reachable
* once p1 goes out of scope, so one direct and one indirect leak
*/
p1 = calloc(1, 42); /* calloc for i#927 */
*((void**)p1) = malloc(17);
/* not a leak: still reachable through persistent pointer p2 */
p2 = calloc(1, 8); /* calloc for i#927 */
*((void**)p2) = malloc(19);
/* PR 513954: app-added size field not a leak */
p3 = calloc(1, 24); /* calloc for i#927 */
*((size_t*)p3) = 24;
p3 = (void *) (((char*)p3) + 8); /* align to 8 so matches malloc alignment */
if (setjmp(mark) == 0) {
c = *(char *)0x100; /* i#1015: unaddr on wild access */
}
printf("all done\n");
return 0;
}