blob: edff76b44347358ea527703faac280e1159bcc51 [file] [edit]
/* **********************************************************
* Copyright (c) 2011-2014 Google, 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.
*/
/* Need this defined and to the latest to get the latest defines and types */
#define _WIN32_WINNT 0x0601 /* == _WIN32_WINNT_WIN7 */
#define WINVER _WIN32_WINNT
#include "dr_api.h"
#include "drmemory.h"
#include "syscall.h"
#include "syscall_os.h"
#include "syscall_windows.h"
#include "slowpath.h"
#include "shadow.h"
#include "gdicheck.h"
#include <stddef.h> /* offsetof */
#include "../wininc/ntuser.h" /* LARGE_STRING */
static drsys_sysnum_t sysnum_GdiCreateDIBSection = {-1,0};
static drsys_sysnum_t sysnum_UserGetDC = {-1,0};
static drsys_sysnum_t sysnum_UserGetDCEx = {-1,0};
static drsys_sysnum_t sysnum_UserGetWindowDC = {-1,0};
static drsys_sysnum_t sysnum_UserBeginPaint = {-1,0};
static drsys_sysnum_t sysnum_UserEndPaint = {-1,0};
static drsys_sysnum_t sysnum_UserReleaseDC = {-1,0};
static drsys_sysnum_t sysnum_GdiGetDCforBitmap = {-1,0};
static drsys_sysnum_t sysnum_GdiDdGetDC = {-1,0};
static drsys_sysnum_t sysnum_GdiDeleteObjectApp = {-1,0};
static drsys_sysnum_t sysnum_GdiCreateMetafileDC = {-1,0};
static drsys_sysnum_t sysnum_GdiCreateCompatibleDC = {-1,0};
static drsys_sysnum_t sysnum_GdiOpenDCW = {-1,0};
void
syscall_wingdi_init(void *drcontext, app_pc ntdll_base)
{
get_sysnum("NtGdiCreateDIBSection", &sysnum_GdiCreateDIBSection,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtUserGetDC", &sysnum_UserGetDC, false/*reqd*/);
get_sysnum("NtUserGetDCEx", &sysnum_UserGetDCEx, false/*reqd*/);
get_sysnum("NtUserGetWindowDC", &sysnum_UserGetWindowDC, false/*reqd*/);
get_sysnum("NtUserBeginPaint", &sysnum_UserBeginPaint, false/*reqd*/);
get_sysnum("NtUserEndPaint", &sysnum_UserEndPaint, false/*reqd*/);
get_sysnum("ReleaseDC", &sysnum_UserReleaseDC,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtGdiGetDCforBitmap", &sysnum_GdiGetDCforBitmap,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtGdiDdGetDC", &sysnum_GdiDdGetDC,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtGdiDeleteObjectApp", &sysnum_GdiDeleteObjectApp,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtGdiCreateMetafileDC", &sysnum_GdiCreateMetafileDC,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtGdiCreateCompatibleDC", &sysnum_GdiCreateCompatibleDC,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
get_sysnum("NtGdiOpenDCW", &sysnum_GdiOpenDCW,
get_windows_version() <= DR_WINDOWS_VERSION_2000);
if (options.check_gdi)
gdicheck_init();
}
void
syscall_wingdi_exit(void)
{
if (options.check_gdi)
gdicheck_exit();
}
void
syscall_wingdi_thread_init(void *drcontext)
{
if (options.check_gdi)
gdicheck_thread_init(drcontext);
}
void
syscall_wingdi_thread_exit(void *drcontext)
{
if (options.check_gdi)
gdicheck_thread_exit(drcontext);
}
/***************************************************************************
* Shadow handling
*/
bool
wingdi_process_syscall_arg(drsys_arg_t *arg)
{
if (arg->containing_type == DRSYS_TYPE_LARGE_STRING) {
/* i#489: LARGE_STRING.MaximumLength and LARGE_STRING.bAnsi end
* up initialized by a series of bit manips that fool us.
*/
LARGE_STRING ls;
if (strcmp(arg->arg_name, "LARGE_STRING.MaximumLength") == 0) {
/* can't take offseof bitfield so we assume no padding */
byte *start = ((byte*) arg->start_addr) - sizeof(ls.Length) -
offsetof(LARGE_STRING, Length);
ASSERT(arg->pre, "LARGE_STRING non-buffer fields are always IN");
if (safe_read((void*) start, sizeof(ls), &ls)) {
LOG(SYSCALL_VERBOSE,
"LARGE_STRING Buffer="PFX" Length=%d MaximumLength=%d\n",
(byte *)ls.Buffer, ls.Length, ls.MaximumLength);
/* Check for undef if looks "suspicious": weak,
* but simpler and more efficient than pattern match on every bb.
*/
if (ls.MaximumLength > ls.Length &&
ls.MaximumLength > 1024 /* suspicious */) {
check_sysmem(MEMREF_CHECK_DEFINEDNESS, arg->sysnum, arg->start_addr,
sizeof(ULONG/*+bAnsi*/), arg->mc, arg->arg_name);
} else {
shadow_set_range(arg->start_addr, (byte *)arg->start_addr +
sizeof(ULONG), SHADOW_DEFINED);
}
} else
WARN("WARNING: unable to read syscall param\n");
return true; /* handled */
}
}
return false; /* not handled */
}
/***************************************************************************
* General (non-shadow/memory checking) system call handling
*/
static bool
handle_GdiCreateDIBSection(bool pre, void *drcontext, cls_syscall_t *pt)
{
byte *dib;
if (pre)
return true;
if (safe_read((byte *) syscall_get_param(drcontext, 8), sizeof(dib), &dib)) {
/* XXX: move this into common/alloc.c since that's currently
* driving all the known allocs, heap and otherwise
*/
byte *dib_base;
size_t dib_size;
if (dr_query_memory(dib, &dib_base, &dib_size, NULL)) {
LOG(SYSCALL_VERBOSE, "NtGdiCreateDIBSection created "PFX"-"PFX"\n",
dib_base, dib_base+dib_size);
client_handle_mmap(drcontext, dib_base, dib_size,
/* XXX: may not be file-backed but treating as
* all-defined and non-heap which is what this param
* does today. could do dr_virtual_query().
*/
true/*file-backed*/);
} else
WARN("WARNING: unable to query DIB section "PFX"\n", dib);
} else
WARN("WARNING: unable to read NtGdiCreateDIBSection param\n");
/* When passed-in section pointer is NULL, the return value is
* HBITMAP but doesn't seem to be a real memory address, which is
* odd, b/c presumably when a section is used it would be a real
* memory address, right? The value is typically large so clearly
* not just a table index. Xref i#539.
*/
return true;
}
/* Caller should check for success and only call if syscall is successful (for !pre) */
static void
syscall_check_gdi(bool pre, void *drcontext, drsys_sysnum_t sysnum, cls_syscall_t *pt,
dr_mcontext_t *mc)
{
app_loc_t loc;
ASSERT(options.check_gdi, "shouldn't be called");
if (drsys_sysnums_equal(&sysnum, &sysnum_UserGetDC) ||
drsys_sysnums_equal(&sysnum, &sysnum_UserGetDCEx) ||
drsys_sysnums_equal(&sysnum, &sysnum_UserGetWindowDC) ||
drsys_sysnums_equal(&sysnum, &sysnum_UserBeginPaint) ||
drsys_sysnums_equal(&sysnum, &sysnum_GdiGetDCforBitmap) ||
drsys_sysnums_equal(&sysnum, &sysnum_GdiDdGetDC)) {
if (!pre) {
HDC hdc = (HDC) dr_syscall_get_result(drcontext);
/* i#1192: NtGdiGetDCforBitmap does not allocate a new DC
* that needs to be freed. We leave it enabled as an
* "allocation" to have more DC's in our table (presumably
* adding in those created before we took over).
*/
uint flags = 0;
if (!drsys_sysnums_equal(&sysnum, &sysnum_GdiGetDCforBitmap))
flags |= GDI_DC_ALLOC_GET;
syscall_to_loc(&loc, sysnum, "");
gdicheck_dc_alloc(hdc, flags, sysnum, mc, &loc);
if (drsys_sysnums_equal(&sysnum, &sysnum_UserBeginPaint)) {
/* we store the hdc for access in EndPaint */
pt->paintDC = hdc;
}
}
} else if (drsys_sysnums_equal(&sysnum, &sysnum_GdiCreateMetafileDC) ||
drsys_sysnums_equal(&sysnum, &sysnum_GdiCreateCompatibleDC) ||
drsys_sysnums_equal(&sysnum, &sysnum_GdiOpenDCW)) {
if (!pre) {
HDC hdc = (HDC) dr_syscall_get_result(drcontext);
uint flags = GDI_DC_ALLOC_CREATE;
if (drsys_sysnums_equal(&sysnum, &sysnum_GdiCreateCompatibleDC) &&
syscall_get_param(drcontext, 0) == 0)
flags |= GDI_DC_ALLOC_DUP_NULL;
syscall_to_loc(&loc, sysnum, "");
gdicheck_dc_alloc(hdc, flags, sysnum, mc, &loc);
}
} else if (drsys_sysnums_equal(&sysnum, &sysnum_UserReleaseDC) ||
drsys_sysnums_equal(&sysnum, &sysnum_UserEndPaint)) {
if (pre) {
HDC hdc;
if (drsys_sysnums_equal(&sysnum, &sysnum_UserReleaseDC))
hdc = (HDC)syscall_get_param(drcontext, 0);
else {
hdc = pt->paintDC;
pt->paintDC = NULL;
}
gdicheck_dc_free(hdc, false/*Get not Create*/, sysnum, mc);
}
} else if (drsys_sysnums_equal(&sysnum, &sysnum_GdiDeleteObjectApp)) {
if (pre)
gdicheck_obj_free((HANDLE)syscall_get_param(drcontext, 0), sysnum, mc);
}
}
bool
wingdi_shared_process_syscall(bool pre, void *drcontext, drsys_sysnum_t sysnum,
cls_syscall_t *pt, dr_mcontext_t *mc,
drsys_syscall_t *syscall)
{
/* handlers here do not check for success so we check up front */
if (!pre) {
bool success;
if (drsys_cur_syscall_result(drcontext, &success, NULL, NULL)
!= DRMF_SUCCESS || !success)
return true;
}
if (sysnum.number == sysnum_GdiCreateDIBSection.number)
return handle_GdiCreateDIBSection(pre, drcontext, pt);
if (options.check_gdi) {
syscall_check_gdi(pre, drcontext, sysnum, pt, mc);
}
return true; /* execute syscall */
}