diff --git a/include/xkbcommon/xkbcommon-compose.h b/include/xkbcommon/xkbcommon-compose.h index b28e4f843..69b258d27 100644 --- a/include/xkbcommon/xkbcommon-compose.h +++ b/include/xkbcommon/xkbcommon-compose.h @@ -614,6 +614,13 @@ int xkb_compose_state_get_utf8(struct xkb_compose_state *state, char *buffer, size_t size); +/** + * Maximum size of the string returned by xkb_compose_state_get_utf8(). + * + * @memberof xkb_compose_state + */ +#define XKB_COMPOSE_MAX_STRING_SIZE 256 + /** * Get the result keysym for a composed sequence. * diff --git a/src/compose/parser.c b/src/compose/parser.c index a34d10b76..7b677316d 100644 --- a/src/compose/parser.c +++ b/src/compose/parser.c @@ -328,7 +328,7 @@ struct production { xkb_keysym_t lhs[MAX_LHS_LEN]; unsigned int len; xkb_keysym_t keysym; - char string[256]; + char string[XKB_COMPOSE_MAX_STRING_SIZE]; /* At least one of these is true. */ bool has_keysym; bool has_string; @@ -687,8 +687,10 @@ lhs_mod_list_tok: { scanner_warn(s, "right-hand side string must not be empty; skipping line"); goto skip; } - if (val.string.len >= sizeof(production.string)) { - scanner_warn(s, "right-hand side string is too long; skipping line"); + if (val.string.len > sizeof(production.string)) { + scanner_warn(s, + "right-hand side string is too long: expected max: %d, got: %d; " + "skipping line", (int)sizeof(production.string) - 1, (int)val.string.len); goto skip; } strcpy(production.string, val.string.str); diff --git a/test/compose.c b/test/compose.c index 7092989ed..a455bc1e8 100644 --- a/test/compose.c +++ b/test/compose.c @@ -73,7 +73,7 @@ test_compose_seq_va(struct xkb_compose_table *table, va_list ap) { int ret; struct xkb_compose_state *state; - char buffer[XKB_KEYSYM_NAME_MAX_SIZE]; + char buffer[MAX(XKB_COMPOSE_MAX_STRING_SIZE, XKB_KEYSYM_NAME_MAX_SIZE)]; state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS); assert(state); @@ -772,6 +772,27 @@ test_traverse(struct xkb_context *ctx) xkb_compose_table_unref(table); } +static void +test_string_length(struct xkb_context *ctx) +{ + // Invalid: empty string + const char table_string_1[] = " : \"\" X\n"; + assert(test_compose_seq_buffer(ctx, table_string_1, + XKB_KEY_a, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol, + XKB_KEY_b, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, "", XKB_KEY_X, + XKB_KEY_NoSymbol)); + + char long_string[XKB_COMPOSE_MAX_STRING_SIZE] = { 0 }; + memset(long_string, 0x61, XKB_COMPOSE_MAX_STRING_SIZE - 1); + char table_string_2[XKB_COMPOSE_MAX_STRING_SIZE + sizeof(table_string_1) - 1]; + assert(snprintf_safe(table_string_2, sizeof(table_string_2), + " : \"%s\" X\n", long_string)); + assert(test_compose_seq_buffer(ctx, table_string_2, + XKB_KEY_a, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSING, "", XKB_KEY_NoSymbol, + XKB_KEY_b, XKB_COMPOSE_FEED_ACCEPTED, XKB_COMPOSE_COMPOSED, long_string, XKB_KEY_X, + XKB_KEY_NoSymbol)); +} + static void test_decode_escape_sequences(struct xkb_context *ctx) { @@ -935,6 +956,7 @@ main(int argc, char *argv[]) test_include(ctx); test_override(ctx); test_traverse(ctx); + test_string_length(ctx); test_decode_escape_sequences(ctx); test_encode_escape_sequences(ctx); diff --git a/tools/tools-common.c b/tools/tools-common.c index 09045d309..5473eddfa 100644 --- a/tools/tools-common.c +++ b/tools/tools-common.c @@ -156,9 +156,7 @@ tools_print_keycode_state(char *prefix, xkb_keysym_t sym; const xkb_keysym_t *syms; int nsyms; - // FIXME: this buffer is used for xkb_compose_state_get_utf8, - // which can have a length up to 256. Need to import this constant from compose. - char s[MAX(16, XKB_KEYSYM_NAME_MAX_SIZE)]; + char s[MAX(XKB_COMPOSE_MAX_STRING_SIZE, XKB_KEYSYM_NAME_MAX_SIZE)]; xkb_layout_index_t layout; enum xkb_compose_status status;