Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(developer): verify context(n) offsets are valid in kmcmplib #13308

Draft
wants to merge 2 commits into
base: beta
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions developer/src/common/include/kmn_compiler_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ namespace KmnCompilerMessages {

HINT_IndexStoreLong = SevHint | 0x0B0,

ERROR_ContextExCannotReferenceIf = SevError | 0x0B1,
ERROR_ContextExCannotReferenceNul = SevError | 0x0B2,

FATAL_BufferOverflow = SevFatal | 0x0C0
// FATAL_Break = SevFatal | 0x0C1, unused
};
Expand Down
14 changes: 13 additions & 1 deletion developer/src/kmc-kmn/src/compiler/kmn-compiler-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,19 @@ export class KmnCompilerMessages {
static HINT_IndexStoreLong = SevHint | 0x0B0;
static Hint_IndexStoreLong = () => m(
this.HINT_IndexStoreLong,
`The store referenced in index() is longer than the store referenced in any()`
`The store referenced in index() is longer than the store referenced in any()`,
);

static ERROR_ContextExCannotReferenceIf = SevError | 0x0B1;
static Error_ContextExCannotReferenceIf = () => m(
this.ERROR_ContextExCannotReferenceIf,
`The offset in context() points to a non-character if() statement in the context`,
);

static ERROR_ContextExCannotReferenceNul = SevError | 0x0B2;
static Error_ContextExCannotReferenceNul = () => m(
this.ERROR_ContextExCannotReferenceNul,
`The offset in context() points to a non-character nul statement in the context`,
);

static FATAL_BufferOverflow = SevFatal | 0x0C0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
store(&TARGETS) 'any'

begin Unicode > use(main)

store(cons) 'bcd'
store(ifx) '1'

group(main) using keys

c ERROR_ContextExCannotReferenceIf:
if(ifx = '1') any(cons) + 'd' > context(1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
store(&TARGETS) 'any'

begin Unicode > use(main)

store(cons) 'bcd'

group(main) using keys

c ERROR_ContextExCannotReferenceNul:
nul any(cons) + 'a' > 'x' context(1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
store(&TARGETS) 'any'

begin Unicode > use(main)

store(cons) 'bcd'
store(ifx) '1'

group(main) using keys

c ERROR_ContextExHasInvalidOffset: `context(3)` should be `context(2)`
if(ifx='1') any(cons) + 'a' > 'x' context(3) 'z'

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
store(&TARGETS) 'any'

begin Unicode > use(main)

store(cons) 'bcd'

group(main) using keys

c ERROR_ContextExHasInvalidOffset: `context(2)` is longer than range
any(cons) + 'a' > 'x' context(2) 'z'

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
store(&TARGETS) 'any'

begin Unicode > use(main)

store(cons) 'bcd'
store(ifx) '1'

group(main) using keys

c ERROR_ContextExHasInvalidOffset: `context(3)` should be `context(2)`
nul any(cons) + 'a' > 'x' context(3) 'z'

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
store(&TARGETS) 'any'

begin Unicode > use(main)

store(cons) 'bcd'

group(main) using keys

c ERROR_ContextExHasInvalidOffset: `context(0)` is lower than 1
any(cons) + 'a' > 'x' context(0) 'z'

23 changes: 21 additions & 2 deletions developer/src/kmc-kmn/test/messages.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,35 @@ describe('KmnCompilerMessages', function () {

it('should generate WARN_IndexStoreShort if a store referenced in index() is shorter than the corresponding any() store', async function() {
await testForMessage(this, ['keyboards', 'warn_index_store_short.kmn'], KmnCompilerMessages.WARN_IndexStoreShort);
callbacks.clear();
await testForMessage(this, ['keyboards', 'warn_index_store_short_key.kmn'], KmnCompilerMessages.WARN_IndexStoreShort);
});

// HINT_IndexStoreLong

it('should generate HINT_IndexStoreLong if a store referenced in index() is longer than the corresponding any() store', async function() {
await testForMessage(this, ['keyboards', 'hint_index_store_long.kmn'], KmnCompilerMessages.HINT_IndexStoreLong);
callbacks.clear();
await testForMessage(this, ['keyboards', 'hint_index_store_long_key.kmn'], KmnCompilerMessages.HINT_IndexStoreLong);
});

// ERROR_ContextExHasInvalidOffset

it('should generate ERROR_ContextExHasInvalidOffset if the offset is lower than 1 or greater than the context length', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_if.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_long.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_nul.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
await testForMessage(this, ['invalid-keyboards', 'error_contextex_has_invalid_offset_zero.kmn'], KmnCompilerMessages.ERROR_ContextExHasInvalidOffset);
});

// ERROR_ContextExCannotReferenceIf

it('should generate ERROR_ContextExCannotReferenceIf if the offset points to an if() statement', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_contextex_cannot_reference_if.kmn'], KmnCompilerMessages.ERROR_ContextExCannotReferenceIf);
});

// ERROR_ContextExCannotReferenceNul

it('should generate ERROR_ContextExCannotReferenceNul if the offset points to a nul statement', async function() {
await testForMessage(this, ['invalid-keyboards', 'error_contextex_cannot_reference_nul.kmn'], KmnCompilerMessages.ERROR_ContextExCannotReferenceNul);
});

});
18 changes: 15 additions & 3 deletions developer/src/kmcmplib/src/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1458,13 +1458,23 @@ KMX_DWORD CheckStatementOffsets(PFILE_KEYBOARD fk, PFILE_GROUP gp, PKMX_WCHAR co
}
} else if (*(p + 1) == CODE_CONTEXTEX) {
int contextOffset = *(p + 2);
if (contextOffset > xstrlen(context))
if (contextOffset > xstrlen(context)) {
return KmnCompilerMessages::ERROR_ContextExHasInvalidOffset;
}

// contextOffset <= 0 is tested in parse stage

for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++);
if(*q == UC_SENTINEL && (*(q + 1) == CODE_IFOPT || *(q + 1) == CODE_IFSYSTEMSTORE)) {
return KmnCompilerMessages::ERROR_ContextExCannotReferenceIf;
}
if(*q == UC_SENTINEL && *(q + 1) == CODE_NUL) {
return KmnCompilerMessages::ERROR_ContextExCannotReferenceNul;
}

// Due to a limitation in earlier versions of KeymanWeb, the minimum version
// for context() referring to notany() is 14.0. See #917 for details.
if (kmcmp::CompileTarget == CKF_KEYMANWEB) {
for (q = context, i = 1; *q && i < contextOffset; q = incxstr(q), i++);
if (*q == UC_SENTINEL && *(q + 1) == CODE_NOTANY) {
if(!VerifyKeyboardVersion(fk, VERSION_140)) {
return KmnCompilerMessages::ERROR_140FeatureOnlyContextAndNotAnyWeb;
Expand Down Expand Up @@ -2267,7 +2277,9 @@ KMX_DWORD GetXStringImpl(PKMX_WCHAR tstr, PFILE_KEYBOARD fk, PKMX_WCHAR str, KMX
}
int n1b;
n1b = atoiW(q);
if (n1b < 1 || n1b >= 0xF000) return KmnCompilerMessages::ERROR_InvalidToken;
if (n1b < 1 || n1b >= 0xF000) {
return KmnCompilerMessages::ERROR_ContextExHasInvalidOffset;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clearer error message, no functional change.

}
tstr[mx++] = UC_SENTINEL;
tstr[mx++] = CODE_CONTEXTEX;
tstr[mx++] = n1b;
Expand Down
8 changes: 4 additions & 4 deletions developer/src/kmcmplib/tests/gtest-compiler.tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1281,10 +1281,10 @@ TEST_F(CompilerTest, GetXStringImpl_type_c_test) {
const KMX_WCHAR tstr_context_offset_valid[] = { UC_SENTINEL, CODE_CONTEXTEX, 1, 0 };
EXPECT_EQ(0, u16cmp(tstr_context_offset_valid, tstr));

// context, CERR_InvalidToke, offset < 1
// context, ERROR_ContextExHasInvalidOffset, offset < 1
fileKeyboard.version = VERSION_60;
u16cpy(str, u"context(0)");
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_ContextExHasInvalidOffset, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));

// context, large offset < 0xF000, valid
fileKeyboard.version = VERSION_60;
Expand All @@ -1293,10 +1293,10 @@ TEST_F(CompilerTest, GetXStringImpl_type_c_test) {
const KMX_WCHAR tstr_context_large_offset_valid[] = { UC_SENTINEL, CODE_CONTEXTEX, 61439, 0 };
EXPECT_EQ(0, u16cmp(tstr_context_large_offset_valid, tstr));

// context, KmnCompilerMessages::ERROR_InvalidToken, too large offset == 0xF000
// context, KmnCompilerMessages::ERROR_ContextExHasInvalidOffset, too large offset == 0xF000
fileKeyboard.version = VERSION_60;
u16cpy(str, u"context(61440)"); //0xF000
EXPECT_EQ(KmnCompilerMessages::ERROR_InvalidToken, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));
EXPECT_EQ(KmnCompilerMessages::ERROR_ContextExHasInvalidOffset, GetXStringImpl(tstr, &fileKeyboard, str, u"", output, 80, 0, &newp, FALSE));

// context, KmnCompilerMessages::ERROR_60FeatureOnly_Contextn
fileKeyboard.version = VERSION_50;
Expand Down