Skip to content

Commit 502e9e5

Browse files
committed
xkbcomp: Add stricter bounds for keycodes and levels
Our current implementation uses continuous arrays indexed by keycodes and levels. This is simple and good enough for realistic keymaps. However, they are allowed to have big values that will lead to either memory exhaustion or a waste of memory (sparse arrays). Added the much stricter upper bounds `0xfff` for keycodes[^1] and 2048 for levels[^2], which should still be plenty enough and provides stronger memory security. [^1]: Current max keycode is 0x2ff in Linux. [^2]: Should be big enough to satisfy automatically generated keymaps.
1 parent 307ce5a commit 502e9e5

File tree

5 files changed

+85
-3
lines changed

5 files changed

+85
-3
lines changed

src/darray.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,12 @@ typedef darray (unsigned long) darray_ulong;
184184
((arr).alloc = (arr).size) * sizeof(*(arr).item)); \
185185
} while (0)
186186

187+
#define darray_max_alloc(itemSize) (UINT_MAX / (itemSize))
188+
187189
static inline unsigned
188190
darray_next_alloc(unsigned alloc, unsigned need, unsigned itemSize)
189191
{
190-
assert(need < UINT_MAX / itemSize / 2); /* Overflow. */
192+
assert(need < darray_max_alloc(itemSize) / 2); /* Overflow. */
191193
if (alloc == 0)
192194
alloc = 4;
193195
while (alloc < need)

src/keymap.h

+30
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,36 @@ struct xkb_mod_set {
387387
xkb_mod_mask_t explicit_vmods;
388388
};
389389

390+
/*
391+
* Our current implementation with continuous arrays does not allow efficient
392+
* mapping of keycodes. Allowing the API max valid keycode XKB_KEYCODE_MAX could
393+
* result in memory exhaustion or memory waste (sparse arrays) with huge enough
394+
* valid values. Let’s be more conservative for now, based on the existing Linux
395+
* keycodes.
396+
*/
397+
#define XKB_KEYCODE_MAX_IMPL 0xfff
398+
static_assert(XKB_KEYCODE_MAX_IMPL < XKB_KEYCODE_MAX,
399+
"Valid keycodes");
400+
static_assert(XKB_KEYCODE_MAX_IMPL < darray_max_alloc(sizeof(xkb_atom_t)),
401+
"Max keycodes names");
402+
static_assert(XKB_KEYCODE_MAX_IMPL < darray_max_alloc(sizeof(struct xkb_key)),
403+
"Max keymap keys");
404+
405+
/*
406+
* Our current implementation with continuous arrays does not allow efficient
407+
* mapping of levels. Allowing the max valid level UINT32_MAX could result in
408+
* memory exhaustion or memory waste (sparse arrays) with huge enough valid
409+
* values. Let’s be more conservative for now. This value should be big enough
410+
* to satisfy automatically generated keymaps.
411+
*/
412+
#define XKB_LEVEL_MAX_IMPL 2048
413+
static_assert(XKB_LEVEL_MAX_IMPL < XKB_LEVEL_INVALID,
414+
"Valid levels");
415+
static_assert(XKB_LEVEL_MAX_IMPL < darray_max_alloc(sizeof(xkb_atom_t)),
416+
"Max key types level names");
417+
static_assert(XKB_LEVEL_MAX_IMPL < darray_max_alloc(sizeof(struct xkb_level)),
418+
"Max keys levels");
419+
390420
/* Common keyboard description structure */
391421
struct xkb_keymap {
392422
struct xkb_context *ctx;

src/xkbcomp/expr.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -445,9 +445,10 @@ ExprResolveLevel(struct xkb_context *ctx, const ExprDef *expr,
445445
if (!ok)
446446
return false;
447447

448-
if (result < 1) {
448+
if (result < 1 || result > XKB_LEVEL_MAX_IMPL) {
449449
log_err(ctx, XKB_ERROR_UNSUPPORTED_SHIFT_LEVEL,
450-
"Shift level %d is out of range\n", result);
450+
"Shift level %d is out of range (1..%u)\n",
451+
result, XKB_LEVEL_MAX_IMPL);
451452
return false;
452453
}
453454

src/xkbcomp/keycodes.c

+8
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,14 @@ AddKeyName(KeyNamesInfo *info, xkb_keycode_t kc, xkb_atom_t name,
191191

192192
report = report && ((same_file && verbosity > 0) || verbosity > 7);
193193

194+
/* Check keycode is not too huge for our continuous array */
195+
if (kc > XKB_KEYCODE_MAX_IMPL) {
196+
log_err(info->ctx, XKB_LOG_MESSAGE_NO_ID,
197+
"Keycode too big: must be < %#"PRIx32", got %#"PRIx32"; "
198+
"Key ignored\n", XKB_KEYCODE_MAX_IMPL, kc);
199+
return false;
200+
}
201+
194202
if (kc >= darray_size(info->key_names))
195203
darray_resize0(info->key_names, kc + 1);
196204

test/buffercomp.c

+41
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,46 @@ test_recursive(struct xkb_context *ctx)
198198
}
199199
}
200200

201+
/* Test some limits related to allocations */
202+
static void
203+
test_alloc_limits(struct xkb_context *ctx)
204+
{
205+
const char * const keymaps[] = {
206+
/* Keycodes */
207+
"xkb_keymap {\n"
208+
/* Valid keycode value, but we should not handle it
209+
* with our *continuous* array! */
210+
" xkb_keycodes { <> = 0xfffffffe; };\n"
211+
" xkb_types { };\n"
212+
" xkb_compat { };\n"
213+
" xkb_symbols { key <> {[a]}; };\n"
214+
"};",
215+
/* Key types */
216+
"xkb_keymap {\n"
217+
" xkb_keycodes { };\n"
218+
" xkb_types {\n"
219+
" type \"X\" { map[none] = 0xfffffffe; };\n" /* Invalid level index */
220+
" };\n"
221+
" xkb_compat { };\n"
222+
" xkb_symbols { };\n"
223+
"};",
224+
"xkb_keymap {\n"
225+
" xkb_keycodes { };\n"
226+
" xkb_types {\n"
227+
" type \"X\" {levelname[0xfffffffe]=\"x\";};\n" /* Invalid level index */
228+
" };\n"
229+
" xkb_compat { };\n"
230+
" xkb_symbols { };\n"
231+
"};"
232+
};
233+
for (unsigned int k = 0; k < ARRAY_SIZE(keymaps); k++) {
234+
fprintf(stderr, "------\n*** %s: #%u ***\n", __func__, k);
235+
const struct xkb_keymap *keymap =
236+
test_compile_buffer(ctx, keymaps[k], strlen(keymaps[k]));
237+
assert(!keymap);
238+
}
239+
}
240+
201241
/* Test various multi-{keysym,action} syntaxes */
202242
static void
203243
test_multi_keysyms_actions(struct xkb_context *ctx)
@@ -421,6 +461,7 @@ main(int argc, char *argv[])
421461
test_encodings(ctx);
422462
test_component_syntax_error(ctx);
423463
test_recursive(ctx);
464+
test_alloc_limits(ctx);
424465
test_multi_keysyms_actions(ctx);
425466
test_invalid_symbols_fields(ctx);
426467
test_prebuilt_keymap_roundtrip(ctx);

0 commit comments

Comments
 (0)