blob: 9e47c8d73dc1e0f3ac1ba84620ff9d793559b639 [file]
/* **********************************************************
* Copyright (c) 2011-2024 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 "drsyscall.h"
#include "drsyscall_os.h"
#include "drsyscall_windows.h"
#include <stddef.h> /* offsetof */
/* for NtGdi* syscalls */
#include <wingdi.h> /* usually from windows.h; required by winddi.h + ENUMLOGFONTEXDVW */
#define NT_BUILD_ENVIRONMENT 1 /* for d3dnthal.h */
#include <d3dnthal.h>
#include <winddi.h> /* required by ntgdityp.h and prntfont.h */
#include <prntfont.h>
#include "../wininc/ntgdityp.h"
#include <ntgdi.h>
#include <winspool.h> /* for DRIVER_INFO_2W */
#include <dxgiformat.h> /* for DXGI_FORMAT */
/* for NtUser* syscalls */
#include "../wininc/ndk_extypes.h" /* required by ntuser.h */
#include "../wininc/ntuser.h"
#include "../wininc/ntuser_win8.h"
#include <commctrl.h> /* for EM_GETCUEBANNER */
/***************************************************************************/
/* System calls with wrappers in user32.dll.
*/
drsys_sysnum_t sysnum_UserSystemParametersInfo = {-1,0};
drsys_sysnum_t sysnum_UserMenuInfo = {-1,0};
drsys_sysnum_t sysnum_UserMenuItemInfo = {-1,0};
drsys_sysnum_t sysnum_UserGetAltTabInfo = {-1,0};
drsys_sysnum_t sysnum_UserGetRawInputBuffer = {-1,0};
drsys_sysnum_t sysnum_UserGetRawInputData = {-1,0};
drsys_sysnum_t sysnum_UserGetRawInputDeviceInfo = {-1,0};
drsys_sysnum_t sysnum_UserTrackMouseEvent = {-1,0};
drsys_sysnum_t sysnum_UserLoadKeyboardLayoutEx = {-1,0};
drsys_sysnum_t sysnum_UserCreateWindowStation = {-1,0};
drsys_sysnum_t sysnum_UserMessageCall = {-1,0};
drsys_sysnum_t sysnum_UserCreateAcceleratorTable = {-1,0};
drsys_sysnum_t sysnum_UserCopyAcceleratorTable = {-1,0};
drsys_sysnum_t sysnum_UserSetScrollInfo = {-1,0};
/* Table that maps usercall names to secondary syscall numbers.
* Number can be 0 so we store +1.
*/
#define USERCALL_TABLE_HASH_BITS 8
static hashtable_t usercall_table;
/***************************************************************************
* NtUserCall* secondary system call numbers
*/
#define NONE -1
static const char * const usercall_names[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
#type"."#name,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
#define NUM_USERCALL_NAMES (sizeof(usercall_names)/sizeof(usercall_names[0]))
static const char * const usercall_primary[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
#type,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win10_1803_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w15,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win10_1709_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w14,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win10_1703_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w13,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win10_1607_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w12,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win10_1511_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w11,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win10_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w10,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win81_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w81,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win8_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w8,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win7_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w7,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int winvistaSP2_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
vistaSP2,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int winvistaSP01_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
vistaSP01,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win2003_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w2003,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int winxp_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
xp,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
static const int win2k_usercall_nums[] = {
#define USERCALL(type, name, w2k, xp, w2003, vistaSP01, vistaSP2, w7, w8, w81, w10,\
w11, w12, w13, w14, w15) \
w2k,
#include "drsyscall_usercallx.h"
#undef USERCALL
};
/***************************************************************************
* System calls with wrappers in gdi32.dll.
*/
drsys_sysnum_t sysnum_GdiCreatePaletteInternal = {-1,0};
drsys_sysnum_t sysnum_GdiCheckBitmapBits = {-1,0};
drsys_sysnum_t sysnum_GdiHfontCreate = {-1,0};
drsys_sysnum_t sysnum_GdiDoPalette = {-1,0};
drsys_sysnum_t sysnum_GdiExtTextOutW = {-1,0};
drsys_sysnum_t sysnum_GdiDescribePixelFormat = {-1,0};
drsys_sysnum_t sysnum_GdiGetRasterizerCaps = {-1,0};
drsys_sysnum_t sysnum_GdiPolyPolyDraw = {-1,0};
/***************************************************************************
* TOP-LEVEL
*/
uint
wingdi_get_secondary_syscall_num(const char *name, uint primary_num)
{
drsys_sysnum_t num;
const char *skip_primary;
num.secondary = (uint)(ptr_uint_t) hashtable_lookup(&usercall_table, (void *)name);
if (num.secondary == 0) {
LOG(SYSCALL_VERBOSE, "WARNING: could not find usercall %s\n", name);
return -1;
}
num.secondary = num.secondary - 1/*+1 in usercall table*/;
num.number = primary_num;
/* add secondary usercall with & without primary prefix */
name2num_entry_add(name, num, false/*no Zw*/, false);
skip_primary = strstr(name, ".");
if (skip_primary != NULL &&
/* don't add unknown w/o primary */
strstr(name, ".UNKNOWN") == NULL) {
name2num_entry_add(skip_primary + 1/*"."*/, num, false/*no Zw*/, false);
}
return num.secondary;
}
void
wingdi_add_usercall(const char *name, int num)
{
IF_DEBUG(bool ok;)
/* We might be called from sysnum file parsing prior to drsyscall_wingdi_init: */
if (usercall_table.table == NULL) {
/* We duplicate all strings to handle synum files (i#1908) */
hashtable_init(&usercall_table, USERCALL_TABLE_HASH_BITS,
HASH_STRING, true/*strdup*/);
}
LOG(SYSCALL_VERBOSE + 1, "name2num usercall: adding %s => %d\n", name, num);
IF_DEBUG(ok =)
hashtable_add(&usercall_table, (void *)name,
(void *)(ptr_uint_t)(num + 1/*avoid 0*/));
DOLOG(1, {
if (!ok)
LOG(1, "Dup usercall entry for %s\n", name);
});
ASSERT(ok, "no dup entries in usercall_table");
}
drmf_status_t
drsyscall_wingdi_init(void *drcontext, app_pc ntdll_base, dr_os_version_info_t *ver,
bool use_usercall_table)
{
uint i;
const int *usercalls;
if (usercall_table.table == NULL) { /* may be initialized earlier */
hashtable_init(&usercall_table, USERCALL_TABLE_HASH_BITS,
HASH_STRING, true/*strdup*/);
}
if (!use_usercall_table) {
/* i#1908: while the usercall numbers don't change as much, they do
* shift around, and it's better to use our unknown syscall heuristics
* rather than get something completely wrong. Our syscall file supports
* usercall numbers to get the right behavior.
*/
return DRMF_SUCCESS;
}
LOG(1, "Windows version is %d.%d.%d\n", ver->version, ver->service_pack_major,
ver->service_pack_minor);
switch (ver->version) {
case DR_WINDOWS_VERSION_10_1803: usercalls = win10_1803_usercall_nums; break;
case DR_WINDOWS_VERSION_10_1709: usercalls = win10_1709_usercall_nums; break;
case DR_WINDOWS_VERSION_10_1703: usercalls = win10_1703_usercall_nums; break;
case DR_WINDOWS_VERSION_10_1607: usercalls = win10_1607_usercall_nums; break;
case DR_WINDOWS_VERSION_10_1511: usercalls = win10_1511_usercall_nums; break;
case DR_WINDOWS_VERSION_10: usercalls = win10_usercall_nums; break;
case DR_WINDOWS_VERSION_8_1: usercalls = win81_usercall_nums; break;
case DR_WINDOWS_VERSION_8: usercalls = win8_usercall_nums; break;
case DR_WINDOWS_VERSION_7: usercalls = win7_usercall_nums; break;
case DR_WINDOWS_VERSION_VISTA: {
if (ver->service_pack_major >= 2)
usercalls = winvistaSP2_usercall_nums;
else
usercalls = winvistaSP01_usercall_nums;
break;
}
case DR_WINDOWS_VERSION_2003: usercalls = win2003_usercall_nums; break;
case DR_WINDOWS_VERSION_XP: usercalls = winxp_usercall_nums; break;
case DR_WINDOWS_VERSION_2000: usercalls = win2k_usercall_nums; break;
case DR_WINDOWS_VERSION_NT:
default:
return DRMF_WARNING_UNSUPPORTED_KERNEL;
}
/* Set up hashtable to translate usercall names to numbers */
for (i = 0; i < NUM_USERCALL_NAMES; i++) {
if (usercalls[i] != NONE) {
wingdi_add_usercall(usercall_names[i], usercalls[i]);
}
}
return DRMF_SUCCESS;
}
void
drsyscall_wingdi_exit(void)
{
hashtable_delete(&usercall_table);
}
void
drsyscall_wingdi_thread_init(void *drcontext)
{
}
void
drsyscall_wingdi_thread_exit(void *drcontext)
{
}
/***************************************************************************
* CUSTOM SYSCALL DATA STRUCTURE HANDLING
*/
/* XXX i#488: if too many params can take atoms or strings, should perhaps
* query to verify really an atom to avoid false negatives with
* bad string pointers
*/
static bool
is_atom(void *ptr)
{
/* top 2 bytes are guaranteed to be 0 */
return ((ptr_uint_t)ptr) < 0x10000;
}
/* XXX i#488: see is_atom comment */
static bool
is_int_resource(void *ptr)
{
/* top 2 bytes are guaranteed to be 0 */
return IS_INTRESOURCE(ptr);
}
bool
handle_large_string_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
LARGE_STRING ls;
LARGE_STRING *arg = (LARGE_STRING *) start;
drsys_param_type_t type_val = DRSYS_TYPE_LARGE_STRING;
const char *type_name = "LARGE_STRING";
ASSERT(size == sizeof(LARGE_STRING), "invalid size");
/* I've seen an atom (or int resource?) here
* XXX i#488: avoid false neg: not too many of these now though
* so we allow on all syscalls
*/
if (is_atom(start))
return true; /* handled */
/* we assume OUT fields just have their Buffer as OUT */
if (ii->arg->pre) {
if (!report_memarg(ii, arg_info, (byte *)&arg->Length,
sizeof(arg->Length), "LARGE_STRING.Length"))
return true;
/* this will include LARGE_STRING.bAnsi */
if (!report_memarg(ii, arg_info,
/* we assume no padding (can't take & or offsetof bitfield) */
(byte *)&arg->Length + sizeof(arg->Length),
sizeof(ULONG/*+bAnsi*/), "LARGE_STRING.MaximumLength"))
return true;
if (!report_memarg(ii, arg_info, (byte *)&arg->Buffer,
sizeof(arg->Buffer), "LARGE_STRING.Buffer"))
return true;
}
if (safe_read((void*)start, sizeof(ls), &ls)) {
if (ii->arg->pre) {
if (!report_memarg_ex(ii, arg_info->param, DRSYS_PARAM_BOUNDS,
(byte *)ls.Buffer, ls.MaximumLength,
"LARGE_STRING capacity", DRSYS_TYPE_LARGE_STRING, NULL,
DRSYS_TYPE_INVALID))
return true;
if (TEST(SYSARG_READ, arg_info->flags)) {
if (!report_memarg(ii, arg_info,
(byte *)ls.Buffer, ls.Length, "LARGE_STRING content"))
return true;
}
} else if (TEST(SYSARG_WRITE, arg_info->flags)) {
if (!report_memarg(ii, arg_info,
(byte *)ls.Buffer, ls.Length, "LARGE_STRING content"))
return true;
}
} else
WARN("WARNING: unable to read syscall param\n");
return true; /* handled */
}
bool
handle_devmodew_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
/* DEVMODEW is var-len by windows ver plus optional private driver data appended */
/* can't use a DEVMODEW as ours may be longer than app's if on older windows */
char buf[offsetof(DEVMODEW,dmFields)]; /* need dmSize and dmDriverExtra */
DEVMODEW *safe;
DEVMODEW *param = (DEVMODEW *) start;
if (ii->arg->pre) {
/* XXX: for writes, are we sure all these fields should be set by the caller?
* That's what my pre-drsyscall code had so going with it for now.
*/
if (!report_memarg_type(ii, arg_info->param, SYSARG_READ,
start, BUFFER_SIZE_BYTES(buf),
"DEVMODEW through dmDriverExtra",
SYSARG_TYPE_DEVMODEW, NULL))
return true;
}
if (safe_read(start, BUFFER_SIZE_BYTES(buf), buf)) {
safe = (DEVMODEW *) buf;
ASSERT(safe->dmSize > offsetof(DEVMODEW, dmFormName), "invalid size");
/* there's some padding in the middle */
if (!report_memarg(ii, arg_info, (byte *) &param->dmFields,
((byte *) &param->dmCollate) + sizeof(safe->dmCollate) -
(byte *) &param->dmFields,
"DEVMODEW dmFields through dmCollate"))
return true;
if (!report_memarg(ii, arg_info, (byte *) &param->dmFormName,
(start + safe->dmSize) - (byte *) (&param->dmFormName),
"DEVMODEW dmFormName onward"))
return true;
if (!report_memarg(ii, arg_info, start + safe->dmSize, safe->dmDriverExtra,
"DEVMODEW driver extra info"))
return true;;
} else
WARN("WARNING: unable to read syscall param\n");
return true; /* handled */
}
bool
handle_wndclassexw_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
WNDCLASSEXW safe;
/* i#499: it seems that cbSize is not set for NtUserGetClassInfo when using
* user32!GetClassInfo so we use sizeof for writes. I suspect that once
* they add any more new fields they will start using it. We could
* alternatively keep the check here and treat this is a user32.dll bug and
* suppress it.
*/
bool use_cbSize = TEST(SYSARG_READ, arg_info->flags);
if (ii->arg->pre && use_cbSize) {
if (!report_memarg_type(ii, arg_info->param, SYSARG_READ, start,
sizeof(safe.cbSize), "WNDCLASSEX.cbSize",
SYSARG_TYPE_WNDCLASSEXW, NULL))
return true;
}
if (safe_read(start, sizeof(safe), &safe)) {
if (!report_memarg(ii, arg_info, start,
use_cbSize ? safe.cbSize : sizeof(WNDCLASSEX), "WNDCLASSEX"))
return true;
/* For WRITE there is no capacity here so nothing to check (i#505) */
if ((ii->arg->pre && TEST(SYSARG_READ, arg_info->flags)) ||
(!ii->arg->pre && TEST(SYSARG_WRITE, arg_info->flags))) {
/* lpszMenuName can be from MAKEINTRESOURCE, and
* lpszClassName can be an atom
*/
if ((!use_cbSize || safe.cbSize > offsetof(WNDCLASSEX, lpszMenuName)) &&
!is_atom((void *)safe.lpszMenuName)) {
handle_cwstring(ii, "WNDCLASSEXW.lpszMenuName",
(byte *) safe.lpszMenuName, 0,
arg_info->param, arg_info->flags, NULL, true);
if (ii->abort)
return true;
}
if ((!use_cbSize || safe.cbSize > offsetof(WNDCLASSEX, lpszClassName)) &&
!is_int_resource((void *)safe.lpszClassName)) {
handle_cwstring(ii, "WNDCLASSEXW.lpszClassName",
/* docs say 256 is max length: we read until
* NULL though
*/
(byte *) safe.lpszClassName, 0,
arg_info->param, arg_info->flags, NULL, true);
if (ii->abort)
return true;
}
}
} else
WARN("WARNING: unable to read syscall param\n");
return true; /* handled */
}
bool
handle_clsmenuname_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
CLSMENUNAME safe;
if (!report_memarg(ii, arg_info, start, size, "CLSMENUNAME"))
return true;
if (ii->arg->pre && !TEST(SYSARG_READ, arg_info->flags)) {
/* looks like even the UNICODE_STRING is not set up: contains garbage,
* so presumably kernel creates it and doesn't just write to Buffer
*/
return true; /* handled */
}
/* FIXME i#487: CLSMENUNAME format is not fully known and doesn't seem
* to match this, on win7 at least
*/
#if 0 /* disabled: see comment above */
if (safe_read(start, sizeof(safe), &safe)) {
if (!is_atom(safe.pszClientAnsiMenuName)) {
handle_cstring(pre, sysnum, "CLSMENUNAME.lpszMenuName",
safe.pszClientAnsiMenuName, 0, arg_info->flags,
NULL, true);
if (ii->abort)
return true;
}
if (!is_atom(safe.pwszClientUnicodeMenuName)) {
handle_cwstring(ii, "CLSMENUNAME.lpszMenuName",
(byte *) safe.pwszClientUnicodeMenuName, 0,
arg_info->param, arg_info->flags, NULL, true);
if (ii->abort)
return true;
}
/* XXX: I've seen the pusMenuName pointer itself be an atom, though
* perhaps should also handle just the Buffer being an atom?
*/
if (!is_atom(safe.pusMenuName)) {
handle_unicode_string_access(ii, arg_info,
(byte *) safe.pusMenuName,
sizeof(UNICODE_STRING), false);
if (ii->abort)
return true;
}
} else
WARN("WARNING: unable to read syscall param\n");
#endif
return true; /* handled */
}
bool
handle_menuiteminfow_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
MENUITEMINFOW *real = (MENUITEMINFOW *) start;
MENUITEMINFOW safe;
bool check_dwTypeData = false;
/* user must set cbSize for set or get */
if (ii->arg->pre) {
if (!report_memarg_type(ii, arg_info->param, SYSARG_READ,
start, sizeof(safe.cbSize), "MENUITEMINFOW.cbSize",
SYSARG_TYPE_MENUITEMINFOW, NULL))
return true;
}
if (safe_read(start, sizeof(safe), &safe)) {
if (ii->arg->pre) {
if (!report_memarg_ex(ii, arg_info->param, DRSYS_PARAM_BOUNDS,
start, safe.cbSize, "MENUITEMINFOW",
DRSYS_TYPE_MENUITEMINFOW, NULL, DRSYS_TYPE_INVALID))
return true;
}
if (TEST(MIIM_BITMAP, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, hbmpItem)) {
if (!report_memarg(ii, arg_info, (byte *) &real->hbmpItem,
sizeof(real->hbmpItem), "MENUITEMINFOW.hbmpItem"))
return true;
}
if (TEST(MIIM_CHECKMARKS, safe.fMask)) {
if (safe.cbSize > offsetof(MENUITEMINFOW, hbmpChecked)) {
if (!report_memarg(ii, arg_info, (byte *) &real->hbmpChecked,
sizeof(real->hbmpChecked), "MENUITEMINFOW.hbmpChecked"))
return true;
}
if (safe.cbSize > offsetof(MENUITEMINFOW, hbmpUnchecked)) {
if (!report_memarg(ii, arg_info, (byte *) &real->hbmpUnchecked,
sizeof(real->hbmpUnchecked), "MENUITEMINFOW.hbmpUnchecked"))
return true;
}
}
if (TEST(MIIM_DATA, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, dwItemData)) {
if (!report_memarg(ii, arg_info, (byte *) &real->dwItemData,
sizeof(real->dwItemData), "MENUITEMINFOW.dwItemData"))
return true;
}
if (TEST(MIIM_FTYPE, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, fType)) {
if (!report_memarg(ii, arg_info, (byte *) &real->fType,
sizeof(real->fType), "MENUITEMINFOW.fType"))
return true;
}
if (TEST(MIIM_ID, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, wID)) {
if (!report_memarg(ii, arg_info, (byte *) &real->wID,
sizeof(real->wID), "MENUITEMINFOW.wID"))
return true;
}
if (TEST(MIIM_STATE, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, fState)) {
if (!report_memarg(ii, arg_info, (byte *) &real->fState,
sizeof(real->fState), "MENUITEMINFOW.fState"))
return true;
}
if (TEST(MIIM_STRING, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, dwTypeData)) {
if (!report_memarg(ii, arg_info, (byte *) &real->dwTypeData,
sizeof(real->dwTypeData), "MENUITEMINFOW.dwTypeData"))
return true;
check_dwTypeData = true;
}
if (TEST(MIIM_SUBMENU, safe.fMask) &&
safe.cbSize > offsetof(MENUITEMINFOW, hSubMenu)) {
if (!report_memarg(ii, arg_info, (byte *) &real->hSubMenu,
sizeof(real->hSubMenu), "MENUITEMINFOW.hSubMenu"))
return true;
}
if (TEST(MIIM_TYPE, safe.fMask) &&
!TESTANY(MIIM_BITMAP | MIIM_FTYPE | MIIM_STRING, safe.fMask)) {
if (safe.cbSize > offsetof(MENUITEMINFOW, fType)) {
if (!report_memarg(ii, arg_info, (byte *) &real->fType,
sizeof(real->fType), "MENUITEMINFOW.fType"))
return true;
}
if (safe.cbSize > offsetof(MENUITEMINFOW, dwTypeData)) {
if (!report_memarg(ii, arg_info, (byte *) &real->dwTypeData,
sizeof(real->dwTypeData), "MENUITEMINFOW.dwTypeData"))
return true;
check_dwTypeData = true;
}
}
if (check_dwTypeData) {
/* i#1463: when retrieving, kernel sets safe.cch so we don't have
* to walk the string. When setting, cch is ignored.
*/
if (TEST(SYSARG_WRITE, arg_info->flags)) {
if (safe.cbSize > offsetof(MENUITEMINFOW, cch)) {
if (ii->arg->pre) {
/* User must set cch to capacity of dwTypeData */
if (!report_memarg(ii, arg_info, (byte *) &real->cch,
sizeof(real->cch), "MENUITEMINFOW.cch"))
return true;
}
if (!report_memarg(ii, arg_info, (byte *) safe.dwTypeData,
(safe.cch + 1/*null*/) * sizeof(wchar_t),
"MENUITEMINFOW.dwTypeData"))
return true;
}
} else {
handle_cwstring(ii, "MENUITEMINFOW.dwTypeData",
(byte *) safe.dwTypeData, 0, arg_info->param,
arg_info->flags, NULL, true);
if (ii->abort)
return true;
}
}
} else
WARN("WARNING: unable to read syscall param\n");
return true; /* handled */
}
bool
handle_bitmapinfo_access(sysarg_iter_info_t *ii,
const sysinfo_arg_t *arg_info,
app_pc start, uint size)
{
/* bmiColors is variable-length and the number of entries in the
* array depends on the values of the biBitCount and biClrUsed
* members of the BITMAPINFOHEADER struct.
*/
BITMAPINFOHEADER bmi;
size = sizeof(bmi);
if (safe_read(start, sizeof(bmi), &bmi)) {
if (bmi.biSize != sizeof(bmi))
WARN("WARNING: biSize: %d != sizeof(bmi): %d", bmi.biSize, sizeof(bmi));
switch(bmi.biBitCount) {
case 0:
break;
case 1:
/* bmiColors contains two entries */
size += 2*sizeof(RGBQUAD);
break;
case 4:
/* If bmiClrUsed is 0 then bmiColors contains 16 entries,
* otherwise bmiColors contains the number in bmiClrUsed.
*/
if (bmi.biClrUsed == 0)
size += 16*sizeof(RGBQUAD);
else
size += bmi.biClrUsed*sizeof(RGBQUAD);
break;
case 8:
/* Same as case 4, except max of 256 entries */
if (bmi.biClrUsed == 0)
size += 256*sizeof(RGBQUAD);
else
size += bmi.biClrUsed*sizeof(RGBQUAD);
break;
case 16:
case 32:
/* If biCompression is BI_RGB, then bmiColors is not used. If it is
* BI_BITFIELDS, then it contains 3 DWORD color masks. If it's a
* palette-based device, the color table starts immediately following
* the 3 DWORD color masks.
*/
if (bmi.biCompression == BI_BITFIELDS)
size += 3*sizeof(DWORD);
if (bmi.biClrUsed != 0)
size += bmi.biClrUsed*sizeof(RGBQUAD);
break;
case 24:
/* bmiColors is not used unless used on pallete-based devices */
if (bmi.biClrUsed != 0)
size += bmi.biClrUsed*sizeof(RGBQUAD);
break;
default:
WARN("WARNING: biBitCount should not be %d\n", bmi.biBitCount);
break;
}
}
if (!report_memarg(ii, arg_info, start, size, NULL))
return true;
return true;
}
static void
handle_logfont(sysarg_iter_info_t *ii,
byte *start, size_t size, int ordinal, uint arg_flags, LOGFONTW *safe)
{
LOGFONTW *font = (LOGFONTW *) start;
if (ii->arg->pre && TEST(SYSARG_WRITE, arg_flags)) {
if (!report_memarg_type(ii, ordinal, arg_flags, start, size, "LOGFONTW",
DRSYS_TYPE_LOGFONTW, NULL))
return;
} else {
size_t check_sz;
if (size == 0) {
/* i#873: existing code passes in 0 for the size, which violates
* the MSDN docs, yet the kernel doesn't care and still returns
* success. Thus we don't report as an error and we make
* it work.
*/
size = sizeof(LOGFONTW);
}
check_sz = MIN(size - offsetof(LOGFONTW, lfFaceName),
sizeof(font->lfFaceName));
ASSERT(size >= offsetof(LOGFONTW, lfFaceName), "invalid size");
if (!report_memarg_type(ii, ordinal, arg_flags, start,
offsetof(LOGFONTW, lfFaceName), "LOGFONTW",
DRSYS_TYPE_LOGFONTW, NULL))
return;
handle_cwstring(ii, "LOGFONTW.lfFaceName",
(byte *) &font->lfFaceName, check_sz, ordinal, arg_flags,
(safe == NULL) ? NULL : (wchar_t *)&safe->lfFaceName, true);
if (ii->abort)
return;
}
}
static void
handle_nonclientmetrics(sysarg_iter_info_t *ii,
byte *start, size_t size_specified,
int ordinal, uint arg_flags, NONCLIENTMETRICSW *safe)
{
NONCLIENTMETRICSW *ptr_arg = (NONCLIENTMETRICSW *) start;
NONCLIENTMETRICSW *ptr_safe;
NONCLIENTMETRICSW ptr_local;
size_t size;
if (safe != NULL)
ptr_safe = safe;
else {
if (!safe_read(start, sizeof(ptr_local), &ptr_local)) {
WARN("WARNING: unable to read syscall param\n");
return;
}
ptr_safe = &ptr_local;
}
/* Turns out that despite user32!SystemParametersInfoA requiring both uiParam
* and cbSize, it turns around and calls NtUserSystemParametersInfo w/o
* initializing cbSize! Plus, it passes the A size instead of the W size!
* Ditto on SET where it keeps the A size in the temp struct cbSize.
* So we don't check that ptr_arg->cbSize is defined for pre-write
* and we pretty much ignore the uiParam and cbSize values except
* post-write (kernel puts in the right size). Crazy.
*/
LOG(2, "NONCLIENTMETRICSW %s: sizeof(NONCLIENTMETRICSW)=%x, cbSize=%x, uiParam=%x\n",
TEST(SYSARG_WRITE, arg_flags) ? "write" : "read",
sizeof(NONCLIENTMETRICSW), ptr_safe->cbSize, size_specified);
/* win7 seems to set cbSize properly, always */
if (win_ver.version >= DR_WINDOWS_VERSION_7 ||
(!ii->arg->pre && TEST(SYSARG_WRITE, arg_flags)))
size = ptr_safe->cbSize;
else {
/* MAX to handle future additions. I don't think older versions
* have smaller NONCLIENTMETRICSW than anywhere we're compiling.
*/
size = MAX(sizeof(NONCLIENTMETRICSW), size_specified);
}
if (ii->arg->pre && TEST(SYSARG_WRITE, arg_flags)) {
if (!report_memarg_type(ii, ordinal, arg_flags, start, size, "NONCLIENTMETRICSW",
DRSYS_TYPE_NONCLIENTMETRICSW, NULL))
return;
} else {
size_t offs = 0;
size_t check_sz = MIN(size, offsetof(NONCLIENTMETRICSW, lfCaptionFont));
if (!report_memarg_type(ii, ordinal, arg_flags, start, check_sz,
"NONCLIENTMETRICSW A",
DRSYS_TYPE_NONCLIENTMETRICSW, NULL))
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, sizeof(LOGFONTW));
handle_logfont(ii, (byte *) &ptr_arg->lfCaptionFont,
check_sz, ordinal, arg_flags, &ptr_safe->lfCaptionFont);
if (ii->abort)
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, offsetof(NONCLIENTMETRICSW, lfSmCaptionFont) -
offsetof(NONCLIENTMETRICSW, iSmCaptionWidth));
if (!report_memarg_type(ii, ordinal, arg_flags, (byte *) &ptr_arg->iSmCaptionWidth,
check_sz, "NONCLIENTMETRICSW B",
DRSYS_TYPE_NONCLIENTMETRICSW, NULL))
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, sizeof(LOGFONTW));
handle_logfont(ii, (byte *) &ptr_arg->lfSmCaptionFont,
check_sz, ordinal, arg_flags, &ptr_safe->lfSmCaptionFont);
if (ii->abort)
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, offsetof(NONCLIENTMETRICSW, lfMenuFont) -
offsetof(NONCLIENTMETRICSW, iMenuWidth));
if (!report_memarg_type(ii, ordinal, arg_flags, (byte *) &ptr_arg->iMenuWidth,
check_sz, "NONCLIENTMETRICSW B",
DRSYS_TYPE_NONCLIENTMETRICSW, NULL))
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, sizeof(LOGFONTW));
handle_logfont(ii, (byte *) &ptr_arg->lfMenuFont,
check_sz, ordinal, arg_flags, &ptr_safe->lfMenuFont);
if (ii->abort)
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, sizeof(LOGFONTW));
handle_logfont(ii, (byte *) &ptr_arg->lfStatusFont,
check_sz, ordinal, arg_flags, &ptr_safe->lfStatusFont);
if (ii->abort)
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, sizeof(LOGFONTW));
handle_logfont(ii, (byte *) &ptr_arg->lfMessageFont,
check_sz, ordinal, arg_flags, &ptr_safe->lfMessageFont);
if (ii->abort)
return;
offs += check_sz;
if (offs >= size)
return;
/* there is another field on Vista */
check_sz = size - offs;
if (!report_memarg_type(ii, ordinal, arg_flags, ((byte *)ptr_arg) + offs,
check_sz, "NONCLIENTMETRICSW C",
DRSYS_TYPE_NONCLIENTMETRICSW, NULL))
return;
}
}
static void
handle_iconmetrics(sysarg_iter_info_t *ii,
byte *start, int ordinal, uint arg_flags, ICONMETRICSW *safe)
{
ICONMETRICSW *ptr_arg = (ICONMETRICSW *) start;
ICONMETRICSW *ptr_safe;
ICONMETRICSW ptr_local;
size_t size;
if (safe != NULL)
ptr_safe = safe;
else {
if (!safe_read(start, sizeof(ptr_local), &ptr_local)) {
WARN("WARNING: unable to read syscall param\n");
return;
}
ptr_safe = &ptr_local;
}
size = ptr_safe->cbSize;
if (ii->arg->pre && TEST(SYSARG_WRITE, arg_flags)) {
if (!report_memarg_type(ii, ordinal, arg_flags, start, size, "ICONMETRICSW",
DRSYS_TYPE_ICONMETRICSW, NULL))
return;
} else {
size_t offs = 0;
size_t check_sz = MIN(size, offsetof(ICONMETRICSW, lfFont));
if (!report_memarg_type(ii, ordinal, arg_flags, start, check_sz, "ICONMETRICSW A",
DRSYS_TYPE_ICONMETRICSW, NULL))
return;
offs += check_sz;
if (offs >= size)
return;
check_sz = MIN(size - offs, sizeof(LOGFONTW));
handle_logfont(ii, (byte *) &ptr_arg->lfFont,
check_sz, ordinal, arg_flags, &ptr_safe->lfFont);
if (ii->abort)
return;
offs += check_sz;
if (offs >= size)
return;
/* currently no more args, but here for forward compat */
check_sz = size - offs;
if (!report_memarg_type(ii, ordinal, arg_flags, ((byte *)ptr_arg) + offs,
check_sz, "ICONMETRICSW B",
DRSYS_TYPE_ICONMETRICSW, NULL))
return;
}
}
static void
handle_serialkeys(sysarg_iter_info_t *ii,
byte *start, int ordinal, uint arg_flags, SERIALKEYSW *safe)
{
SERIALKEYSW *ptr_safe;
SERIALKEYSW ptr_local;
size_t size;
if (safe != NULL)
ptr_safe = safe;
else {
if (!safe_read(start, sizeof(ptr_local), &ptr_local)) {
WARN("WARNING: unable to read syscall param\n");
return;
}
ptr_safe = &ptr_local;
}
size = ptr_safe->cbSize;
if (!report_memarg_type(ii, ordinal, arg_flags, start, size, "SERIALKEYSW",
DRSYS_TYPE_SERIALKEYSW, NULL))
return;
handle_cwstring(ii, "SERIALKEYSW.lpszActivePort",
(byte *) ptr_safe->lpszActivePort, 0, ordinal, arg_flags, NULL, true);
if (ii->abort)
return;
handle_cwstring(ii, "SERIALKEYSW.lpszPort",
(byte *) ptr_safe->lpszPort, 0, ordinal, arg_flags, NULL, true);
}
static void
handle_cwstring_field(sysarg_iter_info_t *ii, const char *id,
int ordinal, uint arg_flags,
byte *struct_start, size_t struct_size, size_t cwstring_offs)
{
wchar_t *ptr;
if (struct_size <= cwstring_offs)
return;
if (!safe_read(struct_start + cwstring_offs, sizeof(ptr), &ptr)) {
WARN("WARNING: unable to read syscall param\n");
return;
}
handle_cwstring(ii, id, (byte *)ptr, 0, ordinal, arg_flags, NULL, true);
}
bool
wingdi_process_arg(sysarg_iter_info_t *iter_info,
const sysinfo_arg_t *arg_info, app_pc start, uint size)
{
switch (arg_info->misc) {
case SYSARG_TYPE_LARGE_STRING:
return handle_large_string_access(iter_info, arg_info, start, size);
case SYSARG_TYPE_DEVMODEW:
return handle_devmodew_access(iter_info, arg_info, start, size);
case SYSARG_TYPE_WNDCLASSEXW:
return handle_wndclassexw_access(iter_info, arg_info, start, size);
case SYSARG_TYPE_CLSMENUNAME:
return handle_clsmenuname_access(iter_info, arg_info, start, size);
case SYSARG_TYPE_MENUITEMINFOW:
return handle_menuiteminfow_access(iter_info, arg_info, start, size);
case SYSARG_TYPE_BITMAPINFO:
return handle_bitmapinfo_access(iter_info, arg_info, start, size);
}
return false; /* not handled */
}
/***************************************************************************
* CUSTOM SYSCALL HANDLING
*/
static void
handle_UserSystemParametersInfo(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
UINT uiAction = (UINT) pt->sysarg[0];
UINT uiParam = (UINT) pt->sysarg[1];
# define PV_PARAM_ORDINAL 2
byte *pvParam = (byte *) pt->sysarg[PV_PARAM_ORDINAL];
bool get = true;
size_t sz = 0;
bool uses_pvParam = false; /* also considered used if sz>0 */
bool uses_uiParam = false;
switch (uiAction) {
case SPI_GETBEEP: get = true; sz = sizeof(BOOL); break;
case SPI_SETBEEP: get = false; uses_uiParam = true; break;
case SPI_GETMOUSE: get = true; sz = 3 * sizeof(INT); break;
case SPI_SETMOUSE: get = false; sz = 3 * sizeof(INT); break;
case SPI_GETBORDER: get = true; sz = sizeof(int); break;
case SPI_SETBORDER: get = false; uses_uiParam = true; break;
case SPI_GETKEYBOARDSPEED: get = true; sz = sizeof(DWORD); break;
case SPI_SETKEYBOARDSPEED: get = false; uses_uiParam = true; break;
case SPI_GETSCREENSAVETIMEOUT: get = true; sz = sizeof(int); break;
case SPI_SETSCREENSAVETIMEOUT: get = false; uses_uiParam = true; break;
case SPI_GETSCREENSAVEACTIVE: get = true; sz = sizeof(BOOL); break;
case SPI_SETSCREENSAVEACTIVE: get = false; uses_uiParam = true; break;
/* XXX: no official docs for these 2: */
case SPI_GETGRIDGRANULARITY: get = true; sz = sizeof(int); break;
case SPI_SETGRIDGRANULARITY: get = false; uses_uiParam = true; break;
case SPI_GETDESKWALLPAPER: {
/* uiParam is size in characters */
handle_cwstring(ii, "pvParam", pvParam, uiParam * sizeof(wchar_t),
PV_PARAM_ORDINAL, SYSARG_WRITE, NULL, true);
if (ii->abort)
return;
get = true;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_SETDESKWALLPAPER: {
sysinfo_arg_t arg = {PV_PARAM_ORDINAL, sizeof(UNICODE_STRING),
SYSARG_READ|SYSARG_COMPLEX_TYPE,
SYSARG_TYPE_UNICODE_STRING};
handle_unicode_string_access(ii, &arg, pvParam, sizeof(UNICODE_STRING), false);
if (ii->abort)
return;
get = false;
uses_pvParam = true;
break;
}
case SPI_SETDESKPATTERN: get = false; break;
case SPI_GETKEYBOARDDELAY: get = true; sz = sizeof(int); break;
case SPI_SETKEYBOARDDELAY: get = false; uses_uiParam = true; break;
case SPI_ICONHORIZONTALSPACING: {
if (pvParam != NULL) {
get = true;
sz = sizeof(int);
} else {
get = false;
uses_uiParam = true;
}
break;
}
case SPI_ICONVERTICALSPACING: {
if (pvParam != NULL) {
get = true;
sz = sizeof(int);
} else {
get = false;
uses_uiParam = true;
}
break;
}
case SPI_GETICONTITLEWRAP: get = true; sz = sizeof(BOOL); break;
case SPI_SETICONTITLEWRAP: get = false; uses_uiParam = true; break;
case SPI_GETMENUDROPALIGNMENT: get = true; sz = sizeof(int); break;
case SPI_SETMENUDROPALIGNMENT: get = false; uses_uiParam = true; break;
case SPI_SETDOUBLECLKWIDTH: get = false; uses_uiParam = true; break;
case SPI_SETDOUBLECLKHEIGHT: get = false; uses_uiParam = true; break;
case SPI_GETICONTITLELOGFONT: {
handle_logfont(ii, pvParam, uiParam, PV_PARAM_ORDINAL, SYSARG_WRITE, NULL);
if (ii->abort)
return;
get = true;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_SETICONTITLELOGFONT: {
handle_logfont(ii, pvParam, uiParam, PV_PARAM_ORDINAL, SYSARG_READ, NULL);
if (ii->abort)
return;
get = false;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_SETDOUBLECLICKTIME: get = false; uses_uiParam = true; break;
case SPI_SETMOUSEBUTTONSWAP: get = false; uses_uiParam = true; break;
/* XXX: no official docs: */
case SPI_GETFASTTASKSWITCH: get = true; sz = sizeof(int); break;
case SPI_GETDRAGFULLWINDOWS: get = true; sz = sizeof(BOOL); break;
case SPI_SETDRAGFULLWINDOWS: get = false; uses_uiParam = true; break;
case SPI_GETNONCLIENTMETRICS: {
handle_nonclientmetrics(ii, pvParam, uiParam, PV_PARAM_ORDINAL,
SYSARG_WRITE, NULL);
if (ii->abort)
return;
get = true;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_SETNONCLIENTMETRICS: {
handle_nonclientmetrics(ii, pvParam, uiParam, PV_PARAM_ORDINAL, SYSARG_READ, NULL);
if (ii->abort)
return;
get = false;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_GETMINIMIZEDMETRICS: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETMINIMIZEDMETRICS: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETICONMETRICS: {
handle_iconmetrics(ii, pvParam, PV_PARAM_ORDINAL, SYSARG_WRITE, NULL);
if (ii->abort)
return;
get = true;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_SETICONMETRICS: {
handle_iconmetrics(ii, pvParam, PV_PARAM_ORDINAL, SYSARG_READ, NULL);
if (ii->abort)
return;
get = false;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_GETWORKAREA: get = true; sz = sizeof(RECT); break;
case SPI_SETWORKAREA: get = false; sz = sizeof(RECT); break;
case SPI_GETFILTERKEYS: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETFILTERKEYS: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETTOGGLEKEYS: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETTOGGLEKEYS: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETMOUSEKEYS: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETMOUSEKEYS: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETSHOWSOUNDS: get = true; sz = sizeof(BOOL); break;
case SPI_SETSHOWSOUNDS: get = false; uses_uiParam = true; break;
case SPI_GETSTICKYKEYS: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETSTICKYKEYS: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETACCESSTIMEOUT: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETACCESSTIMEOUT: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETSERIALKEYS: {
handle_serialkeys(ii, pvParam, PV_PARAM_ORDINAL, SYSARG_WRITE, NULL);
if (ii->abort)
return;
get = true;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_SETSERIALKEYS: {
handle_serialkeys(ii, pvParam, PV_PARAM_ORDINAL, SYSARG_READ, NULL);
if (ii->abort)
return;
get = false;
uses_uiParam = true;
uses_pvParam = true;
break;
}
case SPI_GETSOUNDSENTRY: {
handle_cwstring_field(ii, "SOUNDSENTRYW.lpszWindowsEffectDLL",
PV_PARAM_ORDINAL, SYSARG_WRITE, pvParam, uiParam,
offsetof(SOUNDSENTRYW, lpszWindowsEffectDLL));
if (ii->abort)
return;
/* rest of struct handled through pvParam check below */
get = true;
uses_uiParam = true;
sz = uiParam;
break;
}
case SPI_SETSOUNDSENTRY: {
handle_cwstring_field(ii, "SOUNDSENTRYW.lpszWindowsEffectDLL",
PV_PARAM_ORDINAL, SYSARG_READ, pvParam, uiParam,
offsetof(SOUNDSENTRYW, lpszWindowsEffectDLL));
if (ii->abort)
return;
/* rest of struct handled through pvParam check below */
get = false;
uses_uiParam = true;
sz = uiParam;
break;
}
case SPI_GETHIGHCONTRAST: {
handle_cwstring_field(ii, "HIGHCONTRASTW.lpszDefaultScheme",
PV_PARAM_ORDINAL, SYSARG_WRITE, pvParam, uiParam,
offsetof(HIGHCONTRASTW, lpszDefaultScheme));
if (ii->abort)
return;
/* rest of struct handled through pvParam check below */
get = true;
uses_uiParam = true;
sz = uiParam;
break;
}
case SPI_SETHIGHCONTRAST: {
handle_cwstring_field(ii, "HIGHCONTRASTW.lpszDefaultScheme",
PV_PARAM_ORDINAL, SYSARG_READ, pvParam, uiParam,
offsetof(HIGHCONTRASTW, lpszDefaultScheme));
if (ii->abort)
return;
/* rest of struct handled through pvParam check below */
get = false;
uses_uiParam = true;
sz = uiParam;
break;
}
case SPI_GETKEYBOARDPREF: get = true; sz = sizeof(BOOL); break;
case SPI_SETKEYBOARDPREF: get = false; uses_uiParam = true; break;
case SPI_GETSCREENREADER: get = true; sz = sizeof(BOOL); break;
case SPI_SETSCREENREADER: get = false; uses_uiParam = true; break;
case SPI_GETANIMATION: get = true; uses_uiParam = true; sz = uiParam; break;
case SPI_SETANIMATION: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETFONTSMOOTHING: get = true; sz = sizeof(BOOL); break;
case SPI_SETFONTSMOOTHING: get = false; uses_uiParam = true; break;
case SPI_SETDRAGWIDTH: get = false; uses_uiParam = true; break;
case SPI_SETDRAGHEIGHT: get = false; uses_uiParam = true; break;
/* XXX: no official docs: */
case SPI_SETHANDHELD: get = false; uses_uiParam = true; break;
case SPI_GETLOWPOWERTIMEOUT: get = true; sz = sizeof(int); break;
case SPI_GETPOWEROFFTIMEOUT: get = true; sz = sizeof(int); break;
case SPI_SETLOWPOWERTIMEOUT: get = false; uses_uiParam = true; break;
case SPI_SETPOWEROFFTIMEOUT: get = false; uses_uiParam = true; break;
case SPI_GETLOWPOWERACTIVE: get = true; sz = sizeof(BOOL); break;
case SPI_GETPOWEROFFACTIVE: get = true; sz = sizeof(BOOL); break;
case SPI_SETLOWPOWERACTIVE: get = false; uses_uiParam = true; break;
case SPI_SETPOWEROFFACTIVE: get = false; uses_uiParam = true; break;
/* XXX: docs say to set uiParam=0 and pvParam=NULL; we don't check init */
case SPI_SETCURSORS: get = false; break;
case SPI_SETICONS: get = false; break;
case SPI_GETDEFAULTINPUTLANG: get = true; sz = sizeof(HKL); break;
case SPI_SETDEFAULTINPUTLANG: get = false; sz = sizeof(HKL); break;
case SPI_SETLANGTOGGLE: get = false; break;
case SPI_GETMOUSETRAILS: get = true; sz = sizeof(int); break;
case SPI_SETMOUSETRAILS: get = false; uses_uiParam = true; break;
case SPI_GETSNAPTODEFBUTTON: get = true; sz = sizeof(BOOL); break;
case SPI_SETSNAPTODEFBUTTON: get = false; uses_uiParam = true; break;
case SPI_GETMOUSEHOVERWIDTH: get = true; sz = sizeof(UINT); break;
case SPI_SETMOUSEHOVERWIDTH: get = false; uses_uiParam = true; break;
case SPI_GETMOUSEHOVERHEIGHT: get = true; sz = sizeof(UINT); break;
case SPI_SETMOUSEHOVERHEIGHT: get = false; uses_uiParam = true; break;
case SPI_GETMOUSEHOVERTIME: get = true; sz = sizeof(UINT); break;
case SPI_SETMOUSEHOVERTIME: get = false; uses_uiParam = true; break;
case SPI_GETWHEELSCROLLLINES: get = true; sz = sizeof(UINT); break;
case SPI_SETWHEELSCROLLLINES: get = false; uses_uiParam = true; break;
case SPI_GETMENUSHOWDELAY: get = true; sz = sizeof(DWORD); break;
case SPI_SETMENUSHOWDELAY: get = false; uses_uiParam = true; break;
case SPI_GETWHEELSCROLLCHARS: get = true; sz = sizeof(UINT); break;
case SPI_SETWHEELSCROLLCHARS: get = false; uses_uiParam = true; break;
case SPI_GETSHOWIMEUI: get = true; sz = sizeof(BOOL); break;
case SPI_SETSHOWIMEUI: get = false; uses_uiParam = true; break;
case SPI_GETMOUSESPEED: get = true; sz = sizeof(int); break;
case SPI_SETMOUSESPEED: get = false; uses_uiParam = true; break;
case SPI_GETSCREENSAVERRUNNING: get = true; sz = sizeof(BOOL); break;
case SPI_SETSCREENSAVERRUNNING: get = false; uses_uiParam = true; break;
case SPI_GETAUDIODESCRIPTION: get = true; uses_uiParam = true; sz = uiParam; break;
/* XXX: docs don't actually say to set uiParam: I'm assuming for symmetry */
case SPI_SETAUDIODESCRIPTION: get = false; uses_uiParam = true; sz = uiParam; break;
case SPI_GETSCREENSAVESECURE: get = true; sz = sizeof(BOOL); break;
case SPI_SETSCREENSAVESECURE: get = false; uses_uiParam = true; break;
case SPI_GETHUNGAPPTIMEOUT: get = true; sz = sizeof(int); break;
case SPI_SETHUNGAPPTIMEOUT: get = false; uses_uiParam = true; break;
case SPI_GETWAITTOKILLTIMEOUT: get = true; sz = sizeof(int); break;
case SPI_SETWAITTOKILLTIMEOUT: get = false; uses_uiParam = true; break;
case SPI_GETWAITTOKILLSERVICETIMEOUT: get = true; sz = sizeof(int); break;
case SPI_SETWAITTOKILLSERVICETIMEOUT: get = false; uses_uiParam = true; break;
case SPI_GETMOUSEDOCKTHRESHOLD: get = true; sz = sizeof(DWORD); break;
/* Note that many of the sets below use pvParam as either an inlined BOOL
* or a pointer to a DWORD (why not inlined?), instead of using uiParam
*/
case SPI_SETMOUSEDOCKTHRESHOLD: get = false; sz = sizeof(DWORD); break;
/* XXX: docs don't say it writes to pvParam: ret val instead? */
case SPI_GETPENDOCKTHRESHOLD: get = true; sz = sizeof(DWORD); break;
case SPI_SETPENDOCKTHRESHOLD: get = false; sz = sizeof(DWORD); break;
case SPI_GETWINARRANGING: get = true; sz = sizeof(BOOL); break;
case SPI_SETWINARRANGING: get = false; uses_pvParam = true; break;
/* XXX: docs don't say it writes to pvParam: ret val instead? */
case SPI_GETMOUSEDRAGOUTTHRESHOLD: get = true; sz = sizeof(DWORD); break;
case SPI_SETMOUSEDRAGOUTTHRESHOLD: get = false; sz = sizeof(DWORD); break;
/* XXX: docs don't say it writes to pvParam: ret val instead? */
case SPI_GETPENDRAGOUTTHRESHOLD: get = true; sz = sizeof(DWORD); break;
case SPI_SETPENDRAGOUTTHRESHOLD: get = false; sz = sizeof(DWORD); break;
/* XXX: docs don't say it writes to pvParam: ret val instead? */
case SPI_GETMOUSESIDEMOVETHRESHOLD: get = true; sz = sizeof(DWORD); break;
case SPI_SETMOUSESIDEMOVETHRESHOLD: get = false; sz = sizeof(DWORD); break;
/* XXX: docs don't say it writes to pvParam: ret val instead? */
case SPI_GETPENSIDEMOVETHRESHOLD: get = true; sz = sizeof(DWORD); break;
case SPI_SETPENSIDEMOVETHRESHOLD: get = false; sz = sizeof(DWORD); break;
case SPI_GETDRAGFROMMAXIMIZE: get = true; sz = sizeof(BOOL); break;
case SPI_SETDRAGFROMMAXIMIZE: get = false; uses_pvParam = true; break;
case SPI_GETSNAPSIZING: get = true; sz = sizeof(BOOL); break;
case SPI_SETSNAPSIZING: get = false; uses_pvParam = true; break;
case SPI_GETDOCKMOVING: get = true; sz = sizeof(BOOL); break;
case SPI_SETDOCKMOVING: get = false; uses_pvParam = true; break;
case SPI_GETACTIVEWINDOWTRACKING: get = true; sz = sizeof(BOOL); break;
case SPI_SETACTIVEWINDOWTRACKING: get = false; uses_pvParam = true; break;
case SPI_GETMENUANIMATION: get = true; sz = sizeof(BOOL); break;
case SPI_SETMENUANIMATION: get = false; uses_pvParam = true; break;
case SPI_GETCOMBOBOXANIMATION: get = true; sz = sizeof(BOOL); break;
case SPI_SETCOMBOBOXANIMATION: get = false; uses_pvParam = true; break;
case SPI_GETLISTBOXSMOOTHSCROLLING: get = true; sz = sizeof(BOOL); break;
case SPI_SETLISTBOXSMOOTHSCROLLING: get = false; uses_pvParam = true; break;
case SPI_GETGRADIENTCAPTIONS: get = true; sz = sizeof(BOOL); break;
case SPI_SETGRADIENTCAPTIONS: get = false; uses_pvParam = true; break;
case SPI_GETKEYBOARDCUES: get = true; sz = sizeof(BOOL); break;
case SPI_SETKEYBOARDCUES: get = false; uses_pvParam = true; break;
case SPI_GETACTIVEWNDTRKZORDER: get = true; sz = sizeof(BOOL); break;
case SPI_SETACTIVEWNDTRKZORDER: get = false; uses_pvParam = true; break;
case SPI_GETHOTTRACKING: get = true; sz = sizeof(BOOL); break;
case SPI_SETHOTTRACKING: get = false; uses_pvParam = true; break;
case SPI_GETMENUFADE: get = true; sz = sizeof(BOOL); break;
case SPI_SETMENUFADE: get = false; uses_pvParam = true; break;
case SPI_GETSELECTIONFADE: get = true; sz = sizeof(BOOL); break;
case SPI_SETSELECTIONFADE: get = false; uses_pvParam = true; break;
case SPI_GETTOOLTIPANIMATION: get = true; sz = sizeof(BOOL); break;
case SPI_SETTOOLTIPANIMATION: get = false; uses_pvParam = true; break;
case SPI_GETTOOLTIPFADE: get = true; sz = sizeof(BOOL); break;
case SPI_SETTOOLTIPFADE: get = false; uses_pvParam = true; break;
case SPI_GETCURSORSHADOW: get = true; sz = sizeof(BOOL); break;
case SPI_SETCURSORSHADOW: get = false; uses_pvParam = true; break;
case SPI_GETMOUSESONAR: get = true; sz = sizeof(BOOL); break;
case SPI_SETMOUSESONAR: get = false; uses_uiParam = true; break;
case SPI_GETMOUSECLICKLOCK: get = true; sz = sizeof(BOOL); break;
case SPI_SETMOUSECLICKLOCK: get = false; uses_pvParam = true; break;
case SPI_GETMOUSEVANISH: get = true; sz = sizeof(BOOL); break;
case SPI_SETMOUSEVANISH: get = false; uses_uiParam = true; break;
case SPI_GETFLATMENU: get = true; sz = sizeof(BOOL); break;
case SPI_SETFLATMENU: get = false; uses_uiParam = true; break;
case SPI_GETDROPSHADOW: get = true; sz = sizeof(BOOL); break;
case SPI_SETDROPSHADOW: get = false; uses_uiParam = true; break;
case SPI_GETBLOCKSENDINPUTRESETS: get = true; sz = sizeof(BOOL); break;
/* yes this is uiParam in the midst of many pvParams */
case SPI_SETBLOCKSENDINPUTRESETS: get = false; uses_uiParam = true; break;
case SPI_GETUIEFFECTS: get = true; sz = sizeof(BOOL); break;
case SPI_SETUIEFFECTS: get = false; uses_pvParam = true; break;
case SPI_GETDISABLEOVERLAPPEDCONTENT: get = true; sz = sizeof(BOOL); break;
case SPI_SETDISABLEOVERLAPPEDCONTENT: get = false; uses_uiParam = true; break;
case SPI_GETCLIENTAREAANIMATION: get = true; sz = sizeof(BOOL); break;
case SPI_SETCLIENTAREAANIMATION: get = false; uses_uiParam = true; break;
case SPI_GETCLEARTYPE: get = true; sz = sizeof(BOOL); break;
case SPI_SETCLEARTYPE: get = false; uses_uiParam = true; break;
case SPI_GETSPEECHRECOGNITION: get = true; sz = sizeof(BOOL); break;
case SPI_SETSPEECHRECOGNITION: get = false; uses_uiParam = true; break;
case SPI_GETFOREGROUNDLOCKTIMEOUT: get = true; sz = sizeof(DWORD); break;
case SPI_SETFOREGROUNDLOCKTIMEOUT: get = false; uses_pvParam = true; break;
case SPI_GETACTIVEWNDTRKTIMEOUT: get = true; sz = sizeof(DWORD); break;
case SPI_SETACTIVEWNDTRKTIMEOUT: get = false; uses_pvParam = true; break;
case SPI_GETFOREGROUNDFLASHCOUNT: get = true; sz = sizeof(DWORD); break;
case SPI_SETFOREGROUNDFLASHCOUNT: get = false; uses_pvParam = true; break;
case SPI_GETCARETWIDTH: get = true; sz = sizeof(DWORD); break;
case SPI_SETCARETWIDTH: get = false; uses_pvParam = true; break;
case SPI_GETMOUSECLICKLOCKTIME: get = true; sz = sizeof(DWORD); break;
/* yes this is uiParam in the midst of many pvParams */
case SPI_SETMOUSECLICKLOCKTIME: get = false; uses_uiParam = true; break;
case SPI_GETFONTSMOOTHINGTYPE: get = true; sz = sizeof(UINT); break;
case SPI_SETFONTSMOOTHINGTYPE: get = false; uses_pvParam = true; break;
case SPI_GETFONTSMOOTHINGCONTRAST: get = true; sz = sizeof(UINT); break;
case SPI_SETFONTSMOOTHINGCONTRAST: get = false; uses_pvParam = true; break;
case SPI_GETFOCUSBORDERWIDTH: get = true; sz = sizeof(UINT); break;
case SPI_SETFOCUSBORDERWIDTH: get = false; uses_pvParam = true; break;
case SPI_GETFOCUSBORDERHEIGHT: get = true; sz = sizeof(UINT); break;
case SPI_SETFOCUSBORDERHEIGHT: get = false; uses_pvParam = true; break;
case SPI_GETFONTSMOOTHINGORIENTATION: get = true; sz = sizeof(UINT); break;
case SPI_SETFONTSMOOTHINGORIENTATION: get = false; uses_pvParam = true; break;
case SPI_GETMESSAGEDURATION: get = true; sz = sizeof(ULONG); break;
case SPI_SETMESSAGEDURATION: get = false; uses_pvParam = true; break;
/* XXX: unknown behavior */
case SPI_LANGDRIVER:
case SPI_SETFASTTASKSWITCH:
case SPI_SETPENWINDOWS:
case SPI_GETWINDOWSEXTENSION:
default:
WARN("WARNING: unhandled UserSystemParametersInfo uiAction 0x%x\n",
uiAction);
}
/* table entry only checked uiAction for definedness */
if (uses_uiParam && ii->arg->pre) {
if (!report_sysarg(ii, 1, SYSARG_READ))
return;
}
if (sz > 0 || uses_pvParam) { /* pvParam is used */
if (ii->arg->pre) {
if (!report_sysarg(ii, 2, get ? SYSARG_WRITE : SYSARG_READ))
return;
}
if (get && sz > 0) {
if (!report_memarg_type(ii, PV_PARAM_ORDINAL, SYSARG_WRITE,
pvParam, sz, "pvParam",
sz == sizeof(int) ? DRSYS_TYPE_INT :
DRSYS_TYPE_STRUCT, NULL))
return;
} else if (ii->arg->pre && sz > 0) {
if (!report_memarg_type(ii, PV_PARAM_ORDINAL, SYSARG_READ, pvParam, sz,
"pvParam", sz == sizeof(int) ? DRSYS_TYPE_INT :
DRSYS_TYPE_STRUCT, NULL))
return;
}
}
if (!get && ii->arg->pre) /* fWinIni used for all SET codes */
report_sysarg(ii, 3, SYSARG_READ);
}
static void
handle_UserMenuInfo(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* 3rd param is bool saying whether it's Set or Get */
BOOL set = (BOOL) pt->sysarg[2];
MENUINFO info;
/* user must set cbSize for set or get */
if (ii->arg->pre) {
if (!report_memarg_type(ii, 1, SYSARG_READ, (byte *) pt->sysarg[1],
sizeof(info.cbSize), "MENUINFOW.cbSize",
DRSYS_TYPE_INT, NULL))
return;
}
if (ii->arg->pre || !set) {
if (safe_read((byte *) pt->sysarg[1], sizeof(info), &info)) {
if (!report_memarg_type(ii, 1, set ? SYSARG_READ : SYSARG_WRITE,
(byte *) pt->sysarg[1], info.cbSize, "MENUINFOW",
DRSYS_TYPE_STRUCT, NULL))
return;
} else
WARN("WARNING: unable to read syscall param\n");
}
}
static void
handle_UserMenuItemInfo(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* 4th param is bool saying whether it's Set or Get */
BOOL set = (BOOL) pt->sysarg[4];
sysinfo_arg_t arg = {3, 0,
(set ? SYSARG_READ : SYSARG_WRITE)|SYSARG_COMPLEX_TYPE,
SYSARG_TYPE_MENUITEMINFOW};
handle_menuiteminfow_access(ii, &arg, (byte *) pt->sysarg[3], 0);
}
static void
handle_UserGetAltTabInfo(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* buffer is ansi or unicode depending on arg 5; size (arg 4) is in chars */
BOOL ansi = (BOOL) pt->sysarg[5];
UINT count = (UINT) pt->sysarg[4];
report_memarg_type(ii, 3, SYSARG_WRITE, (byte *) pt->sysarg[3],
count * (ansi ? sizeof(char) : sizeof(wchar_t)),
"pszItemText", ansi ? DRSYS_TYPE_CARRAY : DRSYS_TYPE_CWARRAY, NULL);
report_sysarg_type(ii, 3, SYSARG_READ, count * (ansi ? sizeof(char) : sizeof(wchar_t)),
ansi ? DRSYS_TYPE_CARRAY : DRSYS_TYPE_CWARRAY, "pszItemText");
}
static void
handle_UserGetRawInputBuffer(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
byte *buf = (byte *) pt->sysarg[0];
UINT size;
if (buf == NULL) {
/* writes out total buffer size needed in bytes to param #1 */
if (!report_memarg_type(ii, 1, SYSARG_WRITE, (byte *) pt->sysarg[1],
sizeof(UINT), "pcbSize", DRSYS_TYPE_INT, NULL))
return;
} else {
if (ii->arg->pre) {
/* FIXME i#485: we don't know the number of array entries so we
* can't check addressability pre-syscall: comes from a prior
* buf==NULL call
*/
} else if (safe_read((byte *) pt->sysarg[1], sizeof(size), &size)) {
/* param #1 holds size of each RAWINPUT array entry */
size = (size * dr_syscall_get_result(drcontext)) +
/* param #2 holds header size */
(UINT) pt->sysarg[2];
report_sysarg_type(ii, 0, SYSARG_READ, size, DRSYS_TYPE_STRUCT, "pData");
if (!report_memarg_type(ii, 0, SYSARG_WRITE, buf, size, "pData",
DRSYS_TYPE_STRUCT, NULL))
return;
} else
WARN("WARNING: unable to read syscall param\n");
}
}
static void
handle_UserGetRawInputData(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
byte *buf = (byte *) pt->sysarg[2];
/* arg #3 is either R or W. when W buf must be NULL and the 2,-3,WI entry
* will do a safe_read but won't do a check so no false pos.
*/
if (buf == NULL || ii->arg->pre) {
uint flags = ((buf == NULL) ? SYSARG_WRITE : SYSARG_READ);
report_memarg_type(ii, 3, flags, (byte *) pt->sysarg[3], sizeof(UINT),
"pcbSize", DRSYS_TYPE_INT, NULL);
}
}
static void
handle_UserGetRawInputDeviceInfo(void *drcontext, cls_syscall_t *pt,
sysarg_iter_info_t *ii)
{
UINT uiCommand = (UINT) pt->sysarg[1];
UINT size;
if (safe_read((byte *) pt->sysarg[3], sizeof(size), &size)) {
/* for uiCommand == RIDI_DEVICEINFO we assume pcbSize (3rd param)
* will be set and we don't bother to check RID_DEVICE_INFO.cbSize
*/
if (uiCommand == RIDI_DEVICENAME) {
/* output is a string and size is in chars
* XXX: I'm assuming a wide string!
*/
size *= sizeof(wchar_t);
}
report_sysarg_type(ii, 2, SYSARG_READ, size, DRSYS_TYPE_STRUCT, "pData");
if (!report_memarg_type(ii, 2, SYSARG_WRITE, (byte *) pt->sysarg[2], size,
"pData", DRSYS_TYPE_STRUCT, NULL))
return;
if (pt->sysarg[2] == 0) {
/* XXX i#486: if buffer is not large enough, returns -1 but still
* sets *pcbSize
*/
if (!report_memarg_type(ii, 3, SYSARG_WRITE, (byte *) pt->sysarg[3],
sizeof(UINT), "pData", DRSYS_TYPE_INT, NULL))
return;
}
} else
WARN("WARNING: unable to read syscall param\n");
}
static void
handle_UserTrackMouseEvent(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
TRACKMOUSEEVENT *safe;
byte buf[offsetof(TRACKMOUSEEVENT, dwFlags) + sizeof(safe->dwFlags)];
/* user must set cbSize and dwFlags */
if (ii->arg->pre) {
report_sysarg_type(ii, 0, SYSARG_READ, offsetof(TRACKMOUSEEVENT, dwFlags) +
sizeof(safe->dwFlags), DRSYS_TYPE_STRUCT,
"TRACKMOUSEEVENT cbSize+dwFlags");
if (!report_memarg_type(ii, 0, SYSARG_READ, (byte *) pt->sysarg[0],
offsetof(TRACKMOUSEEVENT, dwFlags) + sizeof(safe->dwFlags),
"TRACKMOUSEEVENT cbSize+dwFlags",
DRSYS_TYPE_STRUCT, NULL))
return;
}
if (safe_read((byte *) pt->sysarg[0], BUFFER_SIZE_BYTES(buf), buf)) {
uint flags;
safe = (TRACKMOUSEEVENT *) buf;
/* XXX: for non-TME_QUERY are the other fields read? */
flags = TEST(TME_QUERY, safe->dwFlags) ? SYSARG_WRITE : SYSARG_READ;
if ((flags == SYSARG_WRITE || ii->arg->pre) &&
safe->cbSize > BUFFER_SIZE_BYTES(buf)) {
report_sysarg_type(ii, 0, SYSARG_READ, safe->cbSize - BUFFER_SIZE_BYTES(buf),
DRSYS_TYPE_STRUCT, "TRACKMOUSEEVENT cbSize+dwFlags");
if (!report_memarg_type(ii, 0, flags,
((byte *)pt->sysarg[0]) + BUFFER_SIZE_BYTES(buf),
safe->cbSize - BUFFER_SIZE_BYTES(buf),
"TRACKMOUSEEVENT post-dwFlags",
DRSYS_TYPE_STRUCT, NULL))
return;
}
} else
WARN("WARNING: unable to read syscall param\n");
}
static void
handle_UserMessageCall(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* i#1249: behavior depends on both 2nd param (WM_* and other message codes)
* and 6th param (major action requested: FNID_* codes).
* See comments in table: if enough of these turn out to be different we
* might want a secondary table(s) instead.
*/
# define ORD_WPARAM 2
# define ORD_LPARAM 3
# define ORD_RESULT 4
UINT msg = (DWORD) pt->sysarg[1];
WPARAM wparam = (WPARAM) pt->sysarg[ORD_WPARAM];
LPARAM lparam = (LPARAM) pt->sysarg[ORD_LPARAM];
ULONG_PTR result = (ULONG_PTR) pt->sysarg[ORD_RESULT];
DWORD type = (DWORD) pt->sysarg[5];
BOOL ansi = (BOOL) pt->sysarg[6];
bool result_written = true;
/* First, handle result param: whether read or written.
* XXX i#1752: the return value of the syscall is actually the LRESULT, so
* it's not clear whether this param #4 is really used as an OUT param.
* It's NULL in all instances of the syscall observed so far.
*/
if (type == FNID_SENDMESSAGECALLBACK ||
type == FNID_SENDMESSAGEFF ||
type == FNID_SENDMESSAGEWTOOPTION)
result_written = false;
if (!report_memarg_type(ii, ORD_RESULT, result_written ? SYSARG_WRITE : SYSARG_READ,
(byte *) result, sizeof(result), "ResultInfo",
DRSYS_TYPE_UNSIGNED_INT, "ULONG_PTR"))
return;
/* Now handle memory params in the msg code. We assume all FNID_* take in
* codes in the same namespace and that we can ignore "type" here.
* Some will fail on these codes (e.g., FNID_SCROLLBAR won't accept WM_GETTEXT)
* but we'll live with doing the wrong unaddr check pre-syscall for that.
*/
switch (msg) {
case WM_COPYDATA: {
COPYDATASTRUCT safe;
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ,
(byte *) lparam, sizeof(COPYDATASTRUCT),
"WM_COPYDATA", DRSYS_TYPE_STRUCT, "COPYDATASTRUCT"))
return;
if (safe_read((byte *) lparam, sizeof(safe), &safe) &&
!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ,
(byte *) safe.lpData, safe.cbData,
"COPYDATASTRUCT.lpData", DRSYS_TYPE_VOID, NULL))
return;
break;
}
/* XXX: I'm assuming WM_CREATE and WM_NCCREATE are only passed from the
* kernel to the app and never the other way so I'm not handling here
* (CREATESTRUCT is complex to handle).
*/
case WM_GETMINMAXINFO: {
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ|SYSARG_WRITE,
(byte *) lparam, sizeof(MINMAXINFO),
"WM_GETMINMAXINFO", DRSYS_TYPE_STRUCT, "MINMAXINFO"))
return;
break;
}
case WM_GETTEXT: {
if (ansi) {
handle_cstring(ii, ORD_LPARAM, SYSARG_WRITE, "WM_GETTEXT buffer",
(byte *) lparam, wparam, NULL, true);
} else {
handle_cwstring(ii, "WM_GETTEXT buffer", (byte *) lparam,
wparam*sizeof(wchar_t), ORD_LPARAM, SYSARG_WRITE, NULL, true);
}
if (ii->abort)
return;
break;
}
case WM_SETTEXT: {
if (ansi) {
handle_cstring(ii, ORD_LPARAM, SYSARG_READ, "WM_SETTEXT string",
(byte *) lparam, 0, NULL, true);
} else {
handle_cwstring(ii, "WM_GETTEXT string", (byte *) lparam, 0,
ORD_LPARAM, SYSARG_READ, NULL, true);
}
if (ii->abort)
return;
break;
}
case WM_NCCALCSIZE: {
BOOL complex = (BOOL) wparam;
if (complex) {
NCCALCSIZE_PARAMS safe;
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ|SYSARG_WRITE,
(byte *) lparam, sizeof(NCCALCSIZE_PARAMS),
"WM_NCCALCSIZE", DRSYS_TYPE_STRUCT,
"NCCALCSIZE_PARAMS"))
return;
if (safe_read((byte *) lparam, sizeof(safe), &safe) &&
!report_memarg_type(ii, ORD_LPARAM, SYSARG_WRITE,
(byte *) safe.lppos, sizeof(WINDOWPOS),
"NCCALCSIZE_PARAMS.lppos", DRSYS_TYPE_STRUCT,
"WINDOWPOS"))
return;
} else {
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ|SYSARG_WRITE,
(byte *) lparam, sizeof(RECT),
"WM_NCCALCSIZE", DRSYS_TYPE_STRUCT, "RECT"))
return;
}
break;
}
case WM_STYLECHANGED: {
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ,
(byte *) lparam, sizeof(STYLESTRUCT),
"WM_STYLECHANGED", DRSYS_TYPE_STRUCT, "STYLESTRUCT"))
return;
break;
}
case WM_STYLECHANGING: {
/* XXX: only some fields are written */
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ|SYSARG_WRITE,
(byte *) lparam, sizeof(STYLESTRUCT),
"WM_STYLECHANGING", DRSYS_TYPE_STRUCT, "STYLESTRUCT"))
return;
break;
}
case WM_WINDOWPOSCHANGED: {
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ,
(byte *) lparam, sizeof(WINDOWPOS),
"WM_WINDOWPOSCHANGED", DRSYS_TYPE_STRUCT, "WINDOWPOS"))
return;
break;
}
case WM_WINDOWPOSCHANGING: {
/* XXX: only some fields are written */
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_READ|SYSARG_WRITE,
(byte *) lparam, sizeof(WINDOWPOS),
"WM_WINDOWPOSCHANGING", DRSYS_TYPE_STRUCT, "WINDOWPOS"))
return;
break;
}
/* Edit control messages:
*
* For now we only have handling for writes by the kernel for EM_GET*.
* FIXME i#1752: add checking of reads, and go through the rest of the EM_*
* types and find other writes.
* XXX i#1752: add tests for these.
*/
case EM_GETSEL: {
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_WRITE,
(byte *) lparam, sizeof(DWORD),
"EM_GETSEL", DRSYS_TYPE_UNSIGNED_INT, NULL))
return;
if (!report_memarg_type(ii, ORD_WPARAM, SYSARG_WRITE,
(byte *) wparam, sizeof(DWORD),
"EM_GETSEL", DRSYS_TYPE_UNSIGNED_INT, NULL))
return;
break;
}
case EM_GETRECT: {
if (!report_memarg_type(ii, ORD_LPARAM, SYSARG_WRITE,
(byte *) lparam, sizeof(RECT),
"EM_GETRECT", DRSYS_TYPE_STRUCT, "RECT"))
return;
break;
}
case EM_GETLINE: {
/* 1st WORD in buf holds # chars */
WORD chars;
if (safe_read((WORD *) lparam, sizeof(chars), &chars)) {
if (ansi) {
handle_cstring(ii, ORD_LPARAM, SYSARG_WRITE, "EM_GETLINE buffer",
(byte *) lparam, chars*sizeof(char), NULL, true);
} else {
handle_cwstring(ii, "EM_GETLINE buffer", (byte *) lparam,
chars*sizeof(wchar_t), ORD_LPARAM, SYSARG_WRITE,
NULL, true);
}
if (ii->abort)
return;
}
break;
}
case EM_GETCUEBANNER: {
handle_cwstring(ii, "EM_GETCUEBANNER buffer", (byte *) wparam,
lparam*sizeof(wchar_t), ORD_WPARAM, SYSARG_WRITE,
NULL, true);
if (ii->abort)
return;
break;
}
default: {
DO_ONCE({ WARN("WARNING: unhandled NtUserMessageCall types found\n"); });
LOG(SYSCALL_VERBOSE, "WARNING: unhandled NtUserMessageCall message type 0x%x\n",
msg);
break;
}
}
# undef ORD_WPARAM
# undef ORD_LPARAM
# undef ORD_RESULT
}
static void
handle_accel_array(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii,
ACCEL *array, ULONG count, uint arg_flags)
{
ULONG i;
/* first field is BYTE followed by WORD so we have padding to skip */
for (i = 0; i < count; i++) {
if (!report_memarg_ex(ii, 0, mode_from_flags(arg_flags),
(byte *) &array[i].fVirt, sizeof(array[i].fVirt),
"ACCEL.fVirt", DRSYS_TYPE_UNSIGNED_INT, NULL,
DRSYS_TYPE_STRUCT))
return;
if (!report_memarg_ex(ii, 0, mode_from_flags(arg_flags),
(byte *) &array[i].key, sizeof(array[i].key),
"ACCEL.key", DRSYS_TYPE_SIGNED_INT, NULL,
DRSYS_TYPE_STRUCT))
return;
if (!report_memarg_ex(ii, 0, mode_from_flags(arg_flags),
(byte *) &array[i].cmd, sizeof(array[i].cmd),
"ACCEL.cmd", DRSYS_TYPE_SIGNED_INT, NULL,
DRSYS_TYPE_STRUCT))
return;
}
}
static void
handle_UserCreateAcceleratorTable(void *drcontext, cls_syscall_t *pt,
sysarg_iter_info_t *ii)
{
ACCEL *array = (ACCEL *) pt->sysarg[0];
ULONG count = (ULONG) pt->sysarg[1];
handle_accel_array(drcontext, pt, ii, array, count, SYSARG_READ);
}
static void
handle_UserCopyAcceleratorTable(void *drcontext, cls_syscall_t *pt,
sysarg_iter_info_t *ii)
{
ACCEL *array = (ACCEL *) pt->sysarg[1];
ULONG count = (ULONG) pt->sysarg[2];
handle_accel_array(drcontext, pt, ii, array, count, SYSARG_WRITE);
}
static void
handle_UserSetScrollInfo(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* Special-cased b/c some fields are ignored (i#1299) */
SCROLLINFO *si = (SCROLLINFO *) pt->sysarg[2];
SCROLLINFO safe;
if (!ii->arg->pre)
return;
/* User must set cbSize and fMask */
if (!report_memarg_type(ii, 0, SYSARG_READ, (byte *) si,
offsetof(SCROLLINFO, fMask) + sizeof(si->fMask),
"SCROLLINFO cbSize+fMask",
DRSYS_TYPE_STRUCT, "SCROLLINFO"))
return;
if (safe_read((byte *) si, sizeof(safe), &safe)) {
if (TEST(SIF_RANGE, safe.fMask) && safe.cbSize >= offsetof(SCROLLINFO, nPage)) {
if (!report_memarg_type(ii, 0, SYSARG_READ, (byte *) &si->nMin,
sizeof(si->nMin) + sizeof(si->nMax),
"SCROLLINFO nMin+nMax", DRSYS_TYPE_STRUCT,
"SCROLLINFO"))
return;
}
if (TEST(SIF_PAGE, safe.fMask) && safe.cbSize >= offsetof(SCROLLINFO, nPos)) {
if (!report_memarg_type(ii, 0, SYSARG_READ, (byte *) &si->nPage,
sizeof(si->nPage), "SCROLLINFO.nPage",
DRSYS_TYPE_STRUCT, "SCROLLINFO"))
return;
}
if (TEST(SIF_POS, safe.fMask) && safe.cbSize >= offsetof(SCROLLINFO, nTrackPos)) {
if (!report_memarg_type(ii, 0, SYSARG_READ, (byte *) &si->nPos,
sizeof(si->nPos), "SCROLLINFO.nPos",
DRSYS_TYPE_STRUCT, "SCROLLINFO"))
return;
}
/* nTrackPos is ignored on setting, even if SIF_TRACKPOS is set */
}
}
static void
handle_GdiHfontCreate(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
ENUMLOGFONTEXDVW dvw;
ENUMLOGFONTEXDVW *real_dvw = (ENUMLOGFONTEXDVW *) pt->sysarg[0];
if (ii->arg->pre && safe_read((byte *) pt->sysarg[0], sizeof(dvw), &dvw)) {
uint i;
byte *start = (byte *) pt->sysarg[0];
ULONG total_size = (ULONG) pt->sysarg[1];
/* Would be: {0,-1,R,}
* Except not all fields need to be defined.
* If any other syscall turns out to have this param type should
* turn this into a type handler and not a syscall handler.
*/
if (!report_memarg_ex(ii, 0, DRSYS_PARAM_BOUNDS, start,
total_size, "ENUMLOGFONTEXDVW", DRSYS_TYPE_STRUCT, NULL,
DRSYS_TYPE_INVALID))
return;
ASSERT(offsetof(ENUMLOGFONTEXDVW, elfEnumLogfontEx) == 0 &&
offsetof(ENUMLOGFONTEXW, elfLogFont) == 0, "logfont structs changed");
handle_logfont(ii, start, sizeof(LOGFONTW), 0, SYSARG_READ,
&dvw.elfEnumLogfontEx.elfLogFont);
if (ii->abort)
return;
start = (byte *) &real_dvw->elfEnumLogfontEx.elfFullName;
for (i = 0;
i < sizeof(dvw.elfEnumLogfontEx.elfFullName)/sizeof(wchar_t) &&
dvw.elfEnumLogfontEx.elfFullName[i] != L'\0';
i++)
; /* nothing */
if (!report_memarg_type(ii, 0, SYSARG_READ, start, i * sizeof(wchar_t),
"ENUMLOGFONTEXW.elfFullName", DRSYS_TYPE_CWARRAY, NULL))
return;
start = (byte *) &real_dvw->elfEnumLogfontEx.elfStyle;
for (i = 0;
i < sizeof(dvw.elfEnumLogfontEx.elfStyle)/sizeof(wchar_t) &&
dvw.elfEnumLogfontEx.elfStyle[i] != L'\0';
i++)
; /* nothing */
if (!report_memarg_type(ii, 0, SYSARG_READ, start, i * sizeof(wchar_t),
"ENUMLOGFONTEXW.elfStyle", DRSYS_TYPE_CWARRAY, NULL))
return;
start = (byte *) &real_dvw->elfEnumLogfontEx.elfScript;
for (i = 0;
i < sizeof(dvw.elfEnumLogfontEx.elfScript)/sizeof(wchar_t) &&
dvw.elfEnumLogfontEx.elfScript[i] != L'\0';
i++)
; /* nothing */
if (!report_memarg_type(ii, 0, SYSARG_READ, start, i * sizeof(wchar_t),
"ENUMLOGFONTEXW.elfScript", DRSYS_TYPE_CWARRAY, NULL))
return;
/* the dvValues of DESIGNVECTOR are optional: from 0 to 64 bytes */
start = (byte *) &real_dvw->elfDesignVector;
if (dvw.elfDesignVector.dvNumAxes > MM_MAX_NUMAXES) {
dvw.elfDesignVector.dvNumAxes = MM_MAX_NUMAXES;
WARN("WARNING: NtGdiHfontCreate design vector larger than max\n");
}
if ((start + offsetof(DESIGNVECTOR, dvValues) +
dvw.elfDesignVector.dvNumAxes * sizeof(LONG)) -
(byte*) pt->sysarg[0] != total_size) {
WARN("WARNING: NtGdiHfontCreate total size doesn't match\n");
}
if (!report_memarg_type(ii, 0, SYSARG_READ, start,
offsetof(DESIGNVECTOR, dvValues) +
dvw.elfDesignVector.dvNumAxes * sizeof(LONG),
"DESIGNVECTOR", DRSYS_TYPE_STRUCT, NULL))
return;
} else if (ii->arg->pre)
WARN("WARNING: unable to read NtGdiHfontCreate param\n");
}
static void
handle_GdiDoPalette(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* Entry would read: {3,-2,R|SYSARG_SIZE_IN_ELEMENTS,sizeof(PALETTEENTRY)}
* But pPalEntries is an OUT param if !bInbound.
* It's a convenient arg: else would have to look at iFunc.
*/
WORD cEntries = (WORD) pt->sysarg[2];
PALETTEENTRY *pPalEntries = (PALETTEENTRY *) pt->sysarg[3];
bool bInbound = (bool) pt->sysarg[5];
if (bInbound && ii->arg->pre) {
if (!report_memarg_type(ii, 3, SYSARG_READ, (byte *) pPalEntries,
cEntries * sizeof(PALETTEENTRY), "pPalEntries",
DRSYS_TYPE_STRUCT, NULL))
return;
} else if (!bInbound) {
if (!report_memarg_type(ii, 3, SYSARG_WRITE, (byte *) pPalEntries,
cEntries * sizeof(PALETTEENTRY), "pPalEntries",
DRSYS_TYPE_STRUCT, NULL))
return;
}
}
/* Params 0 and 1 and the return type vary */
static void
handle_GdiPolyPolyDraw(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
ULONG *counts = (ULONG *) pt->sysarg[2];
ULONG num_counts = (ULONG) pt->sysarg[3];
int ifunc = (int) pt->sysarg[4];
ULONG num_points = 0;
ULONG i;
if (ifunc == GdiPolyPolyRgn) {
/* Param 0 == fill mode enum value:
* {0, sizeof(POLYFUNCTYPE), SYSARG_INLINED, DRSYS_TYPE_SIGNED_INT}
*/
report_sysarg_type(ii, 0, SYSARG_READ, sizeof(POLYFUNCTYPE),
DRSYS_TYPE_SIGNED_INT, "POLYFUNCTYPE");
} else {
/* Param 0 == HDC:
* {0, sizeof(HDC), SYSARG_INLINED, DRSYS_TYPE_HANDLE}
*/
report_sysarg_type(ii, 0, SYSARG_READ, sizeof(HDC), DRSYS_TYPE_HANDLE, "HDC");
}
/* The length of the POINT array has to be dynamically computed */
for (i = 0; i < num_counts; i++) {
ULONG count;
if (safe_read(&counts[i], sizeof(count), &count)) {
num_points += count;
}
}
/* Param 1 == POINT*.
* XXX: how indicate an array of structs?
*/
report_sysarg_type(ii, 1, SYSARG_READ, sizeof(PPOINT), DRSYS_TYPE_STRUCT, "POINT");
if (!report_memarg_type(ii, 1, SYSARG_READ,
(byte *) pt->sysarg[1], num_points * sizeof(POINT),
"PPOINT", DRSYS_TYPE_STRUCT, "POINT"))
return;
switch (ifunc) {
case GdiPolyBezier:
case GdiPolyLineTo:
case GdiPolyBezierTo:
if (num_counts != 1)
WARN("WARNING: NtGdiPolyPolyDraw: expected 1 count for single polygons\n");
break;
case GdiPolyPolygon:
case GdiPolyPolyLine:
case GdiPolyPolyRgn:
break;
default:
WARN("WARNING: NtGdiPolyPolyDraw: unknown ifunc %d\n", ifunc);
}
if (ifunc == GdiPolyPolyRgn) {
/* Returns HRGN */
report_sysarg_return(drcontext, ii, sizeof(HRGN), DRSYS_TYPE_HANDLE, "HRGN");
} else {
/* Returns BOOL */
report_sysarg_return(drcontext, ii, sizeof(BOOL), DRSYS_TYPE_BOOL, NULL);
}
}
void
wingdi_shadow_process_syscall(void *drcontext, cls_syscall_t *pt, sysarg_iter_info_t *ii)
{
/* handlers here do not check for success so we check up front */
if (!ii->arg->pre) {
if (!os_syscall_succeeded(ii->arg->sysnum, pt->sysinfo, ii->pt))
return;
}
if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserSystemParametersInfo)) {
handle_UserSystemParametersInfo(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserMenuInfo)) {
handle_UserMenuInfo(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserMenuItemInfo)) {
handle_UserMenuItemInfo(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserGetAltTabInfo)) {
handle_UserGetAltTabInfo(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserGetRawInputBuffer)) {
handle_UserGetRawInputBuffer(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserGetRawInputData)) {
handle_UserGetRawInputData(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserGetRawInputDeviceInfo)) {
handle_UserGetRawInputDeviceInfo(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserTrackMouseEvent)) {
handle_UserTrackMouseEvent(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserCreateWindowStation) ||
drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserLoadKeyboardLayoutEx)) {
/* Vista SP1 added one arg (both were 7, now 8)
* FIXME i#487: figure out what it is and whether we need to process it
* for each of the two syscalls.
* Also check whether it's defined after first deciding whether
* we're on SP1: use core's method of checking for export?
*/
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserMessageCall)) {
handle_UserMessageCall(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum,
&sysnum_UserCreateAcceleratorTable)) {
handle_UserCreateAcceleratorTable(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserCopyAcceleratorTable)) {
handle_UserCopyAcceleratorTable(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_UserSetScrollInfo)) {
handle_UserSetScrollInfo(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_GdiCreatePaletteInternal)) {
/* Entry would read: {0, cEntries * 4 + 4, R,} but see comment in ntgdi.h */
if (ii->arg->pre) {
UINT cEntries = (UINT) pt->sysarg[1];
report_memarg_type(ii, 0, SYSARG_READ, (byte *)pt->sysarg[0],
sizeof(LOGPALETTE) - sizeof(PALETTEENTRY) +
sizeof(PALETTEENTRY) * cEntries, "pLogPal",
DRSYS_TYPE_STRUCT, NULL);
report_sysarg_type(ii, 0, SYSARG_READ, sizeof(LOGPALETTE) -
sizeof(PALETTEENTRY) + sizeof(PALETTEENTRY) * cEntries,
DRSYS_TYPE_STRUCT, "pLogPal");
}
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_GdiCheckBitmapBits)) {
/* Entry would read: {7,dwWidth * dwHeight,W,} */
DWORD dwWidth = (DWORD) pt->sysarg[4];
DWORD dwHeight = (DWORD) pt->sysarg[5];
report_memarg_type(ii, 7, SYSARG_WRITE, (byte *)pt->sysarg[7],
dwWidth * dwHeight, "paResults", DRSYS_TYPE_STRUCT, NULL);
report_sysarg_type(ii, 7, SYSARG_READ, dwWidth * dwHeight,
DRSYS_TYPE_STRUCT, "paResults");
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_GdiHfontCreate)) {
handle_GdiHfontCreate(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_GdiDoPalette)) {
handle_GdiDoPalette(drcontext, pt, ii);
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_GdiExtTextOutW)) {
UINT fuOptions = (UINT) pt->sysarg[3];
int cwc = (int) pt->sysarg[6];
INT *pdx = (INT *) pt->sysarg[7];
if (ii->arg->pre && TEST(ETO_PDY, fuOptions)) {
/* pdx contains pairs of INTs. regular entry already checked
* size of singletons of INTs so here we check the extra size.
*/
report_memarg_type(ii, 7, SYSARG_READ, ((byte *)pdx) + cwc*sizeof(INT),
cwc*sizeof(INT), "pdx extra size from ETO_PDY",
DRSYS_TYPE_STRUCT, NULL);
}
} else if (drsys_sysnums_equal(&ii->arg->sysnum, &sysnum_GdiPolyPolyDraw)) {
handle_GdiPolyPolyDraw(drcontext, pt, ii);
}
}
bool
wingdi_syscall_succeeded(drsys_sysnum_t sysnum, syscall_info_t *info, ptr_int_t res,
bool *success DR_PARAM_OUT)
{
/* Custom success criteria */
if (drsys_sysnums_equal(&sysnum, &sysnum_GdiDescribePixelFormat)) {
*success = (res > 0);
return true;
} else if (drsys_sysnums_equal(&sysnum, &sysnum_GdiGetRasterizerCaps)) {
*success = (res == 1);
return true;
}
/* XXX: should all uint return types have SYSINFO_RET_ZERO_FAIL? */
return false;
}