From 2dcc8fb948cddfa9ed71fda5ca7aa79002e148d6 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 12 Jun 2024 16:23:32 +0700 Subject: [PATCH] fix(developer): prevent non-BMP characters in key part of rule Currently, keys must be a UTF-16 code unit or a virtual key. Non-BMP characters are unsupported. Technically, there is space available in the .kmx `COMP_KEY` structure to accommodate UTF-32 codepoints, from kmx_file.h: KMX_WORD_unaligned Key; KMX_WORD_unaligned _reserved; However, the utility of this is almost nil, as it is very unlikely we will encounter base keyboards (for mnemonic layouts) that generate any characters outside the BMP, so there is little value in adding support for this at this time. The compiler will generate an error if this is encountered. Fixes: #11643 --- .../src/common/include/kmn_compiler_errors.h | 1 + .../src/compiler/kmn-compiler-messages.ts | 2 ++ developer/src/kmcmplib/src/CompMsg.cpp | 1 + developer/src/kmcmplib/src/Compiler.cpp | 16 ++++++++++++---- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/developer/src/common/include/kmn_compiler_errors.h b/developer/src/common/include/kmn_compiler_errors.h index 4973f58abe..78ab21efa1 100644 --- a/developer/src/common/include/kmn_compiler_errors.h +++ b/developer/src/common/include/kmn_compiler_errors.h @@ -179,6 +179,7 @@ #define CERR_ExtendedStringTooLong 0x00004076 #define CERR_VirtualKeyExpansionTooLong 0x00004077 #define CERR_CharacterRangeTooLong 0x00004078 +#define CERR_NonBMPCharactersNotSupportedInKeySection 0x00004079 #define CWARN_TooManyWarnings 0x00002080 #define CWARN_OldVersion 0x00002081 diff --git a/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts b/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts index 32dac9fb66..ec7c81a7a8 100644 --- a/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts +++ b/developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts @@ -571,6 +571,8 @@ export class KmnCompilerMessages { static ERROR_CharacterRangeTooLong = SevError | 0x078; static Error_CharacterRangeTooLong = () => m(this.ERROR_CharacterRangeTooLong, `Character range is too large and cannot be expanded`); + static ERROR_NonBMPCharactersNotSupportedInKeySection = SevError | 0x079; + static Error_NonBMPCharactersNotSupportedInKeySection = () => m(this.ERROR_NonBMPCharactersNotSupportedInKeySection, `Characters with codepoints over U+FFFF are not supported in the key part of the rule`); static WARN_TooManyWarnings = SevWarn | 0x080; static Warn_TooManyWarnings = () => m(this.WARN_TooManyWarnings, `Too many warnings or errors`); diff --git a/developer/src/kmcmplib/src/CompMsg.cpp b/developer/src/kmcmplib/src/CompMsg.cpp index 86a9b4d475..e075a4c808 100644 --- a/developer/src/kmcmplib/src/CompMsg.cpp +++ b/developer/src/kmcmplib/src/CompMsg.cpp @@ -115,6 +115,7 @@ const struct CompilerError CompilerErrors[] = { { CERR_ExtendedStringTooLong , "Extended string is too long" }, { CERR_VirtualKeyExpansionTooLong , "Virtual key expansion is too large" }, { CERR_CharacterRangeTooLong , "Character range is too large and cannot be expanded" }, + { CERR_NonBMPCharactersNotSupportedInKeySection , "Characters with codepoints over U+FFFF are not supported in the key part of the rule" }, { CHINT_UnreachableRule , "This rule will never be matched as another rule takes precedence"}, { CHINT_NonUnicodeFile , "Keyman Developer has detected that the file has ANSI encoding. Consider converting this file to UTF-8"}, diff --git a/developer/src/kmcmplib/src/Compiler.cpp b/developer/src/kmcmplib/src/Compiler.cpp index 8d0f11af8f..b014d8d7c8 100644 --- a/developer/src/kmcmplib/src/Compiler.cpp +++ b/developer/src/kmcmplib/src/Compiler.cpp @@ -1210,7 +1210,7 @@ int GetCompileTargetsFromTargetsStore(const KMX_WCHAR* store) { KMX_BOOL IsValidKeyboardVersion(KMX_WCHAR *dpString) { // I4140 /** version format: /^\d+(\.\d+)*$/ - e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, 11.22.3 are all ok; + e.g. 9.0.3, 1.0, 1.2.3.4, 6.2.1.4.6.4, 11.22.3 are all ok; empty string is not permitted; whitespace is not permitted */ @@ -1471,7 +1471,14 @@ KMX_DWORD ProcessKeyLineImpl(PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX_BOOL IsUnico str = p + 1; if ((msg = GetXString(fk, str, u">", pklKey, GLOBAL_BUFSIZE - 1, (int)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; + if (pklKey[0] == 0) return CERR_ZeroLengthString; + + if(Uni_IsSurrogate1(pklKey[0])) { + // #11643: non-BMP characters do not makes sense for key codes + return CERR_NonBMPCharactersNotSupportedInKeySection; + } + if (xstrlen(pklKey) > 1) AddWarning(CWARN_KeyBadLength); } else { if ((msg = GetXString(fk, str, u">", pklIn, GLOBAL_BUFSIZE - 1, (int)(str - pp), &p, TRUE, IsUnicode)) != CERR_None) return msg; @@ -1653,9 +1660,10 @@ KMX_DWORD ExpandKp(PFILE_KEYBOARD fk, PFILE_KEY kpp, KMX_DWORD storeIndex) default: return CERR_CodeInvalidInKeyStore; } - } - else - { + } else if(Uni_IsSurrogate1(*pn)) { + // #11643: non-BMP characters do not makes sense for key codes + return CERR_NonBMPCharactersNotSupportedInKeySection; + } else { k->Key = *pn; // set the key to store offset. k->ShiftFlags = 0; }