spiegel-keyman/windows/src/engine/mcompile/mc_syskbdnt32.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

257 lines
6.9 KiB
C++

/*
Name: syskbdnt32
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 PKBDTABLES (WINAPI *PKBDLAYERDESCRIPTORFUNC)(VOID);
static HMODULE hKbdLibrary = NULL;
static PKBDTABLES KbdTables = NULL;
BOOL LoadNewLibrary_NT(PWSTR filename) {
if(hKbdLibrary) FreeLibrary(hKbdLibrary);
hKbdLibrary = LoadLibrary(filename);
if(!hKbdLibrary) {
LogError(L"LoadNewLibrary: Exit -- could not load library");
return FALSE;
}
PKBDLAYERDESCRIPTORFUNC KbdLayerDescriptorFunc = (PKBDLAYERDESCRIPTORFUNC) GetProcAddress(hKbdLibrary, "KbdLayerDescriptor");
if(KbdLayerDescriptorFunc)
{
KbdTables = (*KbdLayerDescriptorFunc)();
if(KbdTables) {
return TRUE;
}
}
FreeLibrary(hKbdLibrary);
hKbdLibrary = NULL;
KbdTables = NULL;
LogError(L"LoadNewLibrary: Exit -- failed to find function");
return FALSE;
}
WCHAR CharFromVK_NT(WORD VKey, UINT ShiftFlags, WCHAR *PDeadKey) {
PVK_TO_WCHARS1 pv;
PVK_TO_WCHAR_TABLE 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->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->pCharModifiers->wMaxModBits ||
KbdTables->pCharModifiers->ModNumber[shiftidx] == 0xF) {
return 0;
}
shift = KbdTables->pCharModifiers->ModNumber[shiftidx];
/* Get the appropriate virtual key */
for(i = 0, pvt = KbdTables->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->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(int mod) {
WORD ShiftFlags = 0;
int shiftidx = -1;
for(int i = 0; i <= KbdTables->pCharModifiers->wMaxModBits; i++) {
if(KbdTables->pCharModifiers->ModNumber[i] == mod) {
shiftidx = i;
break;
}
}
if(shiftidx == -1) return 0xFFFF;
for(PVK_TO_BIT pvtb = KbdTables->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(WORD ch, WORD *shift)
{
PVK_TO_WCHARS1 pv;
PVK_TO_WCHAR_TABLE pvt;
int i, j;
/* Find the appropriate keyboard tables */
if(!KbdTables) return 0; /* Could not load keyboard DLL */
/* Map the character back to the virtual key */
for(i = 0, pvt = KbdTables->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(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(WORD DeadKey, WORD *OutputPairs) {
WORD *p = OutputPairs, shift;
for(int i = 0; KbdTables->pDeadKey[i].dwBoth; i++) {
if(HIWORD(KbdTables->pDeadKey[i].dwBoth) == DeadKey) {
WORD vk = CharToUSVK_NT(LOWORD(KbdTables->pDeadKey[i].dwBoth), &shift);
if(vk != 0) {
*p++ = vk;
*p++ = shift;
*p++ = KbdTables->pDeadKey[i].wchComposed;
} else {
LogError(L"Warning: complex deadkey not supported.");
}
}
}
*p = 0;
return (INT_PTR)(p-OutputPairs);
}
WORD VKUSToVKUnderlyingLayout_NT(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) /* Could not load keyboard DLL */
return VKey;
if(VKey > 255) return VKey;
UINT scan = USVirtualKeyToScanCode[VKey];
if(scan == 0 || scan >= KbdTables->bMaxVSCtoVK) return VKey;
return KbdTables->pusVSCtoVK[scan];
}
WORD VKUnderlyingLayoutToVKUS_NT(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) /* Could not load keyboard DLL */
return VKey;
UINT scan;
for(scan = 1; scan <= KbdTables->bMaxVSCtoVK; scan++) {
if(KbdTables->pusVSCtoVK[scan] == VKey) {
break;
}
}
if(scan > KbdTables->bMaxVSCtoVK || scan > 127) {
// no translation.
return VKey;
}
return ScanCodeToUSVirtualKey[scan];
}