spiegel-keyman/windows/src/engine/mcompile/mc_syskbdnt64.cpp
Marc Durdin fe2c1cd187 fix(windows): incxstr could run over buffer with malformed data
Fixes #4591.

I fixed incxstr in 4 places:

1. Common/Core: kmx_xstring.cpp
2. Engine: xstring.cpp
3. Test project importkeyboard importkeyboard.cpp
4. Test project m-to-p m-to-p.cpp

I updated mcompile to remove its own copy of incxstr (identical to that
in xstring.cpp) to reduce WETness but opted not to do so for the test
apps, which are pretty much throwaway anyway.

I note that there is more work we could do here; we need to check every
character as we increment so we don't miss a `U+0000` end of string with
malformed data. But I would like to tackle that as a separate job at
some point in the future after Core integration.
2021-03-05 12:50:29 +11:00

367 lines
9.8 KiB
C++

/*
Name: syskbdnt64
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Create Date: 10 Sep 2008
Modified Date: 4 May 2010
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 10 Sep 2008 - mcdurdin - I1635 - Initial version
11 Mar 2009 - mcdurdin - I1894 - Fix threading bugs introduced in I1888
30 Nov 2009 - mcdurdin - I934 - Prep for x64 - change UINT to WORD for vkeys
12 Mar 2010 - mcdurdin - I934 - x64 - Complete
12 Mar 2010 - mcdurdin - I2229 - Remove hints and warnings
04 May 2010 - mcdurdin - I2351 - Robustness - verify _td return value
*/
////////////////////////////////////////////////////////////////////////////
//
// Use Windows NT/2000 keyboard DLLs for mapping virtual keys to characters
//
////////////////////////////////////////////////////////////////////////////
#include "pch.h"
#include "../../engine/keyman32/kbd.h" /* DDK kbdlayout */
typedef struct {
PVK_TO_BIT pVkToBit; // Virtual Keys -> Mod bits
DWORD filler;
WORD wMaxModBits; // max Modification bit combination value
BYTE ModNumber[1]; // Mod bits -> Modification Number
} MODIFIERS_x64, *PMODIFIERS_x64;
typedef struct _VK_TO_WCHAR_TABLE_x64 {
PVK_TO_WCHARS1 pVkToWchars;
DWORD filler;
BYTE nModifications;
BYTE cbSize;
BYTE filler2[2];
DWORD filler3;
} VK_TO_WCHAR_TABLE_x64, *PVK_TO_WCHAR_TABLE_x64;
/***************************************************************************\
* VSC_VK - Associate a Virtual Scancode with a Virtual Key
* Vsc - Virtual Scancode
* Vk - Virtual Key | flags
* Used by VKFromVSC() for scancodes prefixed 0xE0 or 0xE1
\***************************************************************************/
typedef struct _VSC_VK_x64 {
BYTE Vsc;
USHORT Vk;
} VSC_VK_x64, *PVSC_VK_x64;
/***************************************************************************\
* VK_VSC - Associate a Virtual Key with a Virtual Scancode
* Vk - Virtual Key
* Vsc - Virtual Scancode
* Used by MapVirtualKey for Virtual Keys not appearing in ausVK[]
\***************************************************************************/
typedef struct _VK_VSC_x64 {
BYTE Vk;
BYTE Vsc;
} VK_VSC_x64, *PVK_VSC_x64;
typedef struct tagKdbLayer_x64
{
/*
* Modifier keys
*/
PMODIFIERS_x64 pCharModifiers;
DWORD filler1;
/*
* Characters
*/
VK_TO_WCHAR_TABLE_x64 *pVkToWcharTable; // ptr to tbl of ptrs to tbl
DWORD filler2;
/*
* Diacritics
*/
PDEADKEY pDeadKey;
DWORD filler3;
/*
* Names of Keys
*/
VSC_LPWSTR *pKeyNames;
DWORD filler4;
VSC_LPWSTR *pKeyNamesExt;
DWORD filler5;
LPWSTR *pKeyNamesDead;
DWORD filler6;
/*
* Scan codes to Virtual Keys
*/
USHORT *pusVSCtoVK;
DWORD filler7;
BYTE bMaxVSCtoVK;
DWORD filler8;
PVSC_VK_x64 pVSCtoVK_E0; // Scancode has E0 prefix
DWORD filler9;
PVSC_VK_x64 pVSCtoVK_E1; // Scancode has E1 prefix
DWORD fillerA;
/*
* Locale-specific special processing
*/
DWORD fLocaleFlags;
DWORD fillerB;
} KBDTABLES_x64, *PKBDTABLES_x64;
typedef PKBDTABLES_x64 (WINAPI *PKBDLAYERDESCRIPTORFUNC)(VOID);
static HMODULE hKbdLibrary_x64 = NULL;
static PKBDTABLES_x64 KbdTables_x64 = NULL;
BOOL LoadNewLibrary_NT_x64(PWSTR filename) {
if(hKbdLibrary_x64) FreeLibrary(hKbdLibrary_x64);
hKbdLibrary_x64 = LoadLibrary(filename);
if(!hKbdLibrary_x64) {
LogError(L"LoadNewLibrary_x64: Exit -- could not load library");
return FALSE;
}
PKBDLAYERDESCRIPTORFUNC KbdLayerDescriptorFunc = (PKBDLAYERDESCRIPTORFUNC) GetProcAddress(hKbdLibrary_x64, "KbdLayerDescriptor");
if(KbdLayerDescriptorFunc)
{
KbdTables_x64 = (*KbdLayerDescriptorFunc)();
if(KbdTables_x64) {
return TRUE;
}
}
FreeLibrary(hKbdLibrary_x64);
hKbdLibrary_x64 = NULL;
KbdTables_x64 = NULL;
LogError(L"LoadNewLibrary_x64: Exit -- failed to find function");
return FALSE;
}
WCHAR CharFromVK_NT_x64(WORD VKey, UINT ShiftFlags, WCHAR *PDeadKey) {
PVK_TO_WCHARS1 pv;
PVK_TO_WCHAR_TABLE_x64 pvt;
int i;
/* MCD 02/12/2002: Fix bug with number pad keys when numlock is on */
if(IsNumberPadKey(VKey)) {
return 0;
}
/* Get the appropriate table column for shift state */
int shift = -1, shiftidx = 0, capslockbit = 0;
for(PVK_TO_BIT pvtb = KbdTables_x64->pCharModifiers->pVkToBit; pvtb->Vk; pvtb++) {
switch(pvtb->Vk) {
case VK_MENU:
if(ShiftFlags & (LALTFLAG|RALTFLAG)) shiftidx |= pvtb->ModBits; break;
case VK_SHIFT:
if(ShiftFlags & CAPITALFLAG) capslockbit = pvtb->ModBits;
if(ShiftFlags & (K_SHIFTFLAG)) shiftidx |= pvtb->ModBits; break;
case VK_CONTROL:
if(ShiftFlags & (LCTRLFLAG|RCTRLFLAG)) shiftidx |= pvtb->ModBits; break;
}
}
/* If the shift modifier is unused, don't produce a character */
if(shiftidx > KbdTables_x64->pCharModifiers->wMaxModBits ||
KbdTables_x64->pCharModifiers->ModNumber[shiftidx] == 0xF) {
return 0;
}
shift = KbdTables_x64->pCharModifiers->ModNumber[shiftidx];
/* Get the appropriate virtual key */
for(i = 0, pvt = KbdTables_x64->pVkToWcharTable; pvt->pVkToWchars; i++, pvt++) {
for(pv = pvt->pVkToWchars; pv->VirtualKey;) {
if(pv->VirtualKey == VKey) {
if(shift >= pvt->nModifications) {
return 0;
}
/* Check for Caps Lock */
if(ShiftFlags & CAPITALFLAG) {
if(pv->Attributes & CAPLOK) {
/* Invert the state of the SHIFT key */
shift = KbdTables_x64->pCharModifiers->ModNumber[shiftidx ^ capslockbit];
} else if(pv->Attributes & SGCAPS) {
pv++; /* Next keystroke has the appropriate capitalised character */
}
}
/* Found the keystroke combination, return the appropriate character (if existing) */
switch(pv->wch[shift]) {
case WCH_DEAD:
pv = (PVK_TO_WCHARS1) ((BYTE *)pv + pvt->cbSize);
*PDeadKey = pv->wch[shift];
return 0xFFFF; /* Don't queue as WM_KEYDOWN -- don't output either */
case WCH_NONE:
return 0x0000; /* Not a valid key, queue as WM_KEYDOWN */
}
return (pv->wch[shift] > 31 && pv->wch[shift] != 127) ? pv->wch[shift] : 0; /* Don't generate 'special chars'; I911 - fix ctrl+bksp */
}
pv = (PVK_TO_WCHARS1) ((BYTE *)pv + pvt->cbSize);
}
}
return 0;
}
WORD ModificationToShift_NT_x64(int mod) {
WORD ShiftFlags = 0;
int shiftidx = -1;
for(int i = 0; i <= KbdTables_x64->pCharModifiers->wMaxModBits; i++) {
if(KbdTables_x64->pCharModifiers->ModNumber[i] == mod) {
shiftidx = i;
break;
}
}
if(shiftidx == -1) return 0xFFFF;
for(PVK_TO_BIT pvtb = KbdTables_x64->pCharModifiers->pVkToBit; pvtb->Vk; pvtb++) {
if(shiftidx & pvtb->ModBits) {
switch(pvtb->Vk) {
case VK_MENU: ShiftFlags |= RALTFLAG; break;
case VK_SHIFT: ShiftFlags |= K_SHIFTFLAG; break;
case VK_CONTROL: ShiftFlags |= LCTRLFLAG; break;
}
}
}
return ShiftFlags;
}
WORD CharToUSVK_NT_x64(WORD ch, WORD *shift)
{
PVK_TO_WCHARS1 pv;
PVK_TO_WCHAR_TABLE_x64 pvt;
int i, j;
/* Find the appropriate keyboard tables */
if(!KbdTables_x64) return 0; /* Could not load keyboard DLL */
/* Map the character back to the virtual key */
for(i = 0, pvt = KbdTables_x64->pVkToWcharTable; pvt->pVkToWchars; i++, pvt++)
for(pv = pvt->pVkToWchars; pv->VirtualKey;)
{
for(j = 0; j < pvt->nModifications; j++)
if(pv->wch[j] == ch)
{
/* Return the new virtual key */
*shift = ModificationToShift_NT_x64(j);
if(*shift == 0xFFFF) return 0;
return pv->VirtualKey;
}
pv = (PVK_TO_WCHARS1) ((BYTE *)pv + pvt->cbSize);
}
/* Remapping not found */
return 0;
}
int GetDeadkeys_NT_x64(WORD DeadKey, WORD *OutputPairs) {
WORD *p = OutputPairs, shift;
for(int i = 0; KbdTables_x64->pDeadKey[i].dwBoth; i++) {
if(HIWORD(KbdTables_x64->pDeadKey[i].dwBoth) == DeadKey) {
WORD vk = CharToUSVK_NT_x64(LOWORD(KbdTables_x64->pDeadKey[i].dwBoth), &shift);
if(vk != 0) {
*p++ = vk;
*p++ = shift;
*p++ = KbdTables_x64->pDeadKey[i].wchComposed;
} else {
LogError(L"Warning: complex deadkey not supported.");
}
}
}
*p = 0;
return (INT_PTR)(p-OutputPairs);
}
WORD VKUSToVKUnderlyingLayout_NT_x64(WORD VKey) {
/* Num lock test -- MCD: 02/12/2002 Fix num lock not working bug */
if((VKey >= VK_NUMPAD0 && VKey <= VK_NUMPAD9) || VKey == VK_DECIMAL) return VKey;
/* Find the appropriate keyboard tables */
if(!KbdTables_x64) /* Could not load keyboard DLL */
return VKey;
if(VKey > 255) return VKey;
UINT scan = USVirtualKeyToScanCode[VKey];
if(scan == 0 || scan >= KbdTables_x64->bMaxVSCtoVK) return VKey;
return KbdTables_x64->pusVSCtoVK[scan];
}
WORD VKUnderlyingLayoutToVKUS_NT_x64(WORD VKey) {
/* Num lock test -- MCD: 02/12/2002 Fix num lock not working bug */
if((VKey >= VK_NUMPAD0 && VKey <= VK_NUMPAD9) || VKey == VK_DECIMAL) return VKey;
/* Find the appropriate keyboard tables */
if(!KbdTables_x64) /* Could not load keyboard DLL */
return VKey;
UINT scan;
for(scan = 1; scan <= KbdTables_x64->bMaxVSCtoVK; scan++) {
if(KbdTables_x64->pusVSCtoVK[scan] == VKey) {
break;
}
}
if(scan > KbdTables_x64->bMaxVSCtoVK || scan > 127) {
// no translation.
return VKey;
}
return ScanCodeToUSVirtualKey[scan];
}
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS
fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(L"kernel32"),"IsWow64Process");
BOOL fStored = FALSE;
BOOL bIsWow64 = FALSE;
BOOL IsWow64() {
if(fStored) return bIsWow64;
bIsWow64 = FALSE;
if(NULL != fnIsWow64Process) {
if(!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)){
// handle error
return FALSE;
}
}
fStored = TRUE;
return bIsWow64;
}