xserver/xkb/XKBMAlloc.c

872 lines
31 KiB
C
Raw Normal View History

2003-11-14 15:54:54 +00:00
/************************************************************
Copyright (c) 1993 by Silicon Graphics Computer Systems, Inc.
Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting
documentation, and that the name of Silicon Graphics not be
used in advertising or publicity pertaining to distribution
2003-11-14 15:54:54 +00:00
of the software without specific prior written permission.
Silicon Graphics makes no representation about the suitability
2003-11-14 15:54:54 +00:00
of this software for any purpose. It is provided "as is"
without any express or implied warranty.
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2003-11-14 15:54:54 +00:00
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
2003-11-14 15:54:54 +00:00
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
THE USE OR PERFORMANCE OF THIS SOFTWARE.
********************************************************/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
2003-11-14 15:54:54 +00:00
#include <stdio.h>
#include <X11/X.h>
#include <X11/Xproto.h>
2003-11-14 15:54:54 +00:00
#include "misc.h"
#include "inputstr.h"
#include <X11/keysym.h>
#define XKBSRV_NEED_FILE_FUNCS
#include <xkbsrv.h>
2003-11-14 15:54:54 +00:00
/***====================================================================***/
Status
XkbAllocClientMap(XkbDescPtr xkb, unsigned which, unsigned nTotalTypes)
2003-11-14 15:54:54 +00:00
{
XkbClientMapPtr map;
if ((xkb == NULL) ||
((nTotalTypes > 0) && (nTotalTypes < XkbNumRequiredTypes)))
return BadValue;
if ((which & XkbKeySymsMask) &&
((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))) {
DebugF("bad keycode (%d,%d) in XkbAllocClientMap\n",
xkb->min_key_code, xkb->max_key_code);
return BadValue;
}
if (xkb->map == NULL) {
map = calloc(1, sizeof(XkbClientMapRec));
if (map == NULL)
return BadAlloc;
xkb->map = map;
}
else
map = xkb->map;
if ((which & XkbKeyTypesMask) && (nTotalTypes > 0)) {
if (map->types == NULL) {
map->types = calloc(nTotalTypes, sizeof(XkbKeyTypeRec));
if (map->types == NULL)
return BadAlloc;
map->num_types = 0;
map->size_types = nTotalTypes;
}
else if (map->size_types < nTotalTypes) {
XkbKeyTypeRec *prev_types = map->types;
map->types =
reallocarray(map->types, nTotalTypes, sizeof(XkbKeyTypeRec));
if (map->types == NULL) {
free(prev_types);
map->num_types = map->size_types = 0;
return BadAlloc;
}
map->size_types = nTotalTypes;
memset(&map->types[map->num_types], 0,
((map->size_types -
map->num_types) * sizeof(XkbKeyTypeRec)));
}
}
if (which & XkbKeySymsMask) {
int nKeys = XkbNumKeys(xkb);
if (map->syms == NULL) {
map->size_syms = (nKeys * 15) / 10;
map->syms = calloc(map->size_syms, sizeof(KeySym));
if (!map->syms) {
map->size_syms = 0;
return BadAlloc;
}
map->num_syms = 1;
map->syms[0] = NoSymbol;
}
if (map->key_sym_map == NULL) {
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
map->key_sym_map = calloc(MAP_LENGTH, sizeof(XkbSymMapRec));
if (map->key_sym_map == NULL)
return BadAlloc;
}
}
if (which & XkbModifierMapMask) {
if ((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))
return BadMatch;
if (map->modmap == NULL) {
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
map->modmap = calloc(MAP_LENGTH, sizeof(unsigned char));
if (map->modmap == NULL)
return BadAlloc;
}
2003-11-14 15:54:54 +00:00
}
return Success;
}
Status
XkbAllocServerMap(XkbDescPtr xkb, unsigned which, unsigned nNewActions)
2003-11-14 15:54:54 +00:00
{
register int i;
XkbServerMapPtr map;
if (xkb == NULL)
return BadMatch;
if (xkb->server == NULL) {
map = calloc(1, sizeof(XkbServerMapRec));
if (map == NULL)
return BadAlloc;
for (i = 0; i < XkbNumVirtualMods; i++) {
map->vmods[i] = XkbNoModifierMask;
}
xkb->server = map;
}
else
map = xkb->server;
if (which & XkbExplicitComponentsMask) {
if ((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))
return BadMatch;
if (map->explicit == NULL) {
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
map->explicit = calloc(MAP_LENGTH, sizeof(unsigned char));
if (map->explicit == NULL)
return BadAlloc;
}
}
if (which & XkbKeyActionsMask) {
if ((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))
return BadMatch;
if (nNewActions < 1)
nNewActions = 1;
if (map->acts == NULL) {
map->acts = calloc((nNewActions + 1), sizeof(XkbAction));
if (map->acts == NULL)
return BadAlloc;
map->num_acts = 1;
map->size_acts = nNewActions + 1;
}
else if ((map->size_acts - map->num_acts) < nNewActions) {
unsigned need;
XkbAction *prev_acts = map->acts;
need = map->num_acts + nNewActions;
map->acts = reallocarray(map->acts, need, sizeof(XkbAction));
if (map->acts == NULL) {
free(prev_acts);
map->num_acts = map->size_acts = 0;
return BadAlloc;
}
map->size_acts = need;
memset(&map->acts[map->num_acts], 0,
((map->size_acts - map->num_acts) * sizeof(XkbAction)));
}
if (map->key_acts == NULL) {
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
map->key_acts = calloc(MAP_LENGTH, sizeof(unsigned short));
if (map->key_acts == NULL)
return BadAlloc;
}
}
if (which & XkbKeyBehaviorsMask) {
if ((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))
return BadMatch;
if (map->behaviors == NULL) {
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
map->behaviors = calloc(MAP_LENGTH, sizeof(XkbBehavior));
if (map->behaviors == NULL)
return BadAlloc;
}
}
if (which & XkbVirtualModMapMask) {
if ((!XkbIsLegalKeycode(xkb->min_key_code)) ||
(!XkbIsLegalKeycode(xkb->max_key_code)) ||
(xkb->max_key_code < xkb->min_key_code))
return BadMatch;
if (map->vmodmap == NULL) {
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
map->vmodmap = calloc(MAP_LENGTH, sizeof(unsigned short));
if (map->vmodmap == NULL)
return BadAlloc;
}
2003-11-14 15:54:54 +00:00
}
return Success;
}
/***====================================================================***/
static Status
XkbCopyKeyType(XkbKeyTypePtr from, XkbKeyTypePtr into)
2003-11-14 15:54:54 +00:00
{
if ((!from) || (!into))
return BadMatch;
free(into->map);
into->map = NULL;
free(into->preserve);
into->preserve = NULL;
free(into->level_names);
into->level_names = NULL;
*into = *from;
if ((from->map) && (into->map_count > 0)) {
into->map = calloc(into->map_count, sizeof(XkbKTMapEntryRec));
if (!into->map)
return BadAlloc;
memcpy(into->map, from->map,
into->map_count * sizeof(XkbKTMapEntryRec));
}
if ((from->preserve) && (into->map_count > 0)) {
into->preserve = calloc(into->map_count, sizeof(XkbModsRec));
if (!into->preserve)
return BadAlloc;
memcpy(into->preserve, from->preserve,
into->map_count * sizeof(XkbModsRec));
}
if ((from->level_names) && (into->num_levels > 0)) {
into->level_names = calloc(into->num_levels, sizeof(Atom));
if (!into->level_names)
return BadAlloc;
memcpy(into->level_names, from->level_names,
into->num_levels * sizeof(Atom));
2003-11-14 15:54:54 +00:00
}
return Success;
}
Status
XkbCopyKeyTypes(XkbKeyTypePtr from, XkbKeyTypePtr into, int num_types)
2003-11-14 15:54:54 +00:00
{
register int i, rtrn;
2003-11-14 15:54:54 +00:00
if ((!from) || (!into) || (num_types < 0))
return BadMatch;
for (i = 0; i < num_types; i++) {
if ((rtrn = XkbCopyKeyType(from++, into++)) != Success)
return rtrn;
2003-11-14 15:54:54 +00:00
}
return Success;
}
Status
XkbResizeKeyType(XkbDescPtr xkb,
int type_ndx,
int map_count, Bool want_preserve, int new_num_lvls)
2003-11-14 15:54:54 +00:00
{
XkbKeyTypePtr type;
KeyCode matchingKeys[XkbMaxKeyCount], nMatchingKeys;
2003-11-14 15:54:54 +00:00
if ((type_ndx < 0) || (type_ndx >= xkb->map->num_types) || (map_count < 0)
|| (new_num_lvls < 1))
return BadValue;
2003-11-14 15:54:54 +00:00
switch (type_ndx) {
case XkbOneLevelIndex:
if (new_num_lvls != 1)
return BadMatch;
break;
case XkbTwoLevelIndex:
case XkbAlphabeticIndex:
case XkbKeypadIndex:
if (new_num_lvls != 2)
return BadMatch;
break;
}
type = &xkb->map->types[type_ndx];
if (map_count == 0) {
free(type->map);
type->map = NULL;
free(type->preserve);
type->preserve = NULL;
type->map_count = 0;
2003-11-14 15:54:54 +00:00
}
else {
XkbKTMapEntryRec *prev_map = type->map;
if ((map_count > type->map_count) || (type->map == NULL))
type->map =
reallocarray(type->map, map_count, sizeof(XkbKTMapEntryRec));
if (!type->map) {
free(prev_map);
return BadAlloc;
}
if (want_preserve) {
XkbModsRec *prev_preserve = type->preserve;
if ((map_count > type->map_count) || (type->preserve == NULL)) {
type->preserve = reallocarray(type->preserve,
map_count, sizeof(XkbModsRec));
}
if (!type->preserve) {
free(prev_preserve);
return BadAlloc;
}
}
else {
free(type->preserve);
type->preserve = NULL;
}
type->map_count = map_count;
}
if ((new_num_lvls > type->num_levels) || (type->level_names == NULL)) {
Atom *prev_level_names = type->level_names;
type->level_names = reallocarray(type->level_names,
new_num_lvls, sizeof(Atom));
if (!type->level_names) {
free(prev_level_names);
return BadAlloc;
}
2003-11-14 15:54:54 +00:00
}
/*
* Here's the theory:
* If the width of the type changed, we might have to resize the symbol
* maps for any keys that use the type for one or more groups. This is
* expensive, so we'll try to cull out any keys that are obviously okay:
* In any case:
* - keys that have a group width <= the old width are okay (because
* they could not possibly have been associated with the old type)
* If the key type increased in size:
* - keys that already have a group width >= to the new width are okay
* + keys that have a group width >= the old width but < the new width
* might have to be enlarged.
* If the key type decreased in size:
* - keys that have a group width > the old width don't have to be
* resized (because they must have some other wider type associated
2003-11-14 15:54:54 +00:00
* with some group).
* + keys that have a group width == the old width might have to be
* shrunk.
* The possibilities marked with '+' require us to examine the key types
* associated with each group for the key.
*/
memset(matchingKeys, 0, XkbMaxKeyCount * sizeof(KeyCode));
nMatchingKeys = 0;
if (new_num_lvls > type->num_levels) {
int nTotal;
KeySym *newSyms;
int width, match, nResize;
register int i, g, nSyms;
nResize = 0;
for (nTotal = 1, i = xkb->min_key_code; i <= xkb->max_key_code; i++) {
width = XkbKeyGroupsWidth(xkb, i);
if (width < type->num_levels || width >= new_num_lvls) {
nTotal += XkbKeyNumSyms(xkb,i);
continue;
}
for (match = 0, g = XkbKeyNumGroups(xkb, i) - 1;
(g >= 0) && (!match); g--) {
if (XkbKeyKeyTypeIndex(xkb, i, g) == type_ndx) {
matchingKeys[nMatchingKeys++] = i;
match = 1;
}
}
if (!match)
nTotal += XkbKeyNumSyms(xkb, i);
else {
nTotal += XkbKeyNumGroups(xkb, i) * new_num_lvls;
nResize++;
}
}
if (nResize > 0) {
int nextMatch;
xkb->map->size_syms = (nTotal * 15) / 10;
newSyms = calloc(xkb->map->size_syms, sizeof(KeySym));
if (newSyms == NULL)
return BadAlloc;
nextMatch = 0;
nSyms = 1;
for (i = xkb->min_key_code; i <= xkb->max_key_code; i++) {
if (matchingKeys[nextMatch] == i) {
KeySym *pOld;
nextMatch++;
width = XkbKeyGroupsWidth(xkb, i);
pOld = XkbKeySymsPtr(xkb, i);
for (g = XkbKeyNumGroups(xkb, i) - 1; g >= 0; g--) {
memcpy(&newSyms[nSyms + (new_num_lvls * g)],
&pOld[width * g], width * sizeof(KeySym));
}
xkb->map->key_sym_map[i].offset = nSyms;
nSyms += XkbKeyNumGroups(xkb, i) * new_num_lvls;
}
else {
memcpy(&newSyms[nSyms], XkbKeySymsPtr(xkb, i),
XkbKeyNumSyms(xkb, i) * sizeof(KeySym));
xkb->map->key_sym_map[i].offset = nSyms;
nSyms += XkbKeyNumSyms(xkb, i);
}
}
type->num_levels = new_num_lvls;
free(xkb->map->syms);
xkb->map->syms = newSyms;
xkb->map->num_syms = nSyms;
return Success;
}
}
else if (new_num_lvls < type->num_levels) {
int width, match;
register int g, i;
for (i = xkb->min_key_code; i <= xkb->max_key_code; i++) {
width = XkbKeyGroupsWidth(xkb, i);
if (width < type->num_levels)
continue;
for (match = 0, g = XkbKeyNumGroups(xkb, i) - 1;
(g >= 0) && (!match); g--) {
if (XkbKeyKeyTypeIndex(xkb, i, g) == type_ndx) {
matchingKeys[nMatchingKeys++] = i;
match = 1;
}
}
}
}
if (nMatchingKeys > 0) {
int key, firstClear;
register int i, g;
if (new_num_lvls > type->num_levels)
firstClear = type->num_levels;
else
firstClear = new_num_lvls;
for (i = 0; i < nMatchingKeys; i++) {
KeySym *pSyms;
int width, nClear;
key = matchingKeys[i];
width = XkbKeyGroupsWidth(xkb, key);
nClear = width - firstClear;
pSyms = XkbKeySymsPtr(xkb, key);
for (g = XkbKeyNumGroups(xkb, key) - 1; g >= 0; g--) {
if (XkbKeyKeyTypeIndex(xkb, key, g) == type_ndx) {
if (nClear > 0)
memset(&pSyms[g * width + firstClear], 0,
nClear * sizeof(KeySym));
}
}
}
}
type->num_levels = new_num_lvls;
2003-11-14 15:54:54 +00:00
return Success;
}
KeySym *
XkbResizeKeySyms(XkbDescPtr xkb, int key, int needed)
2003-11-14 15:54:54 +00:00
{
register int i, nSyms, nKeySyms;
unsigned nOldSyms;
KeySym *newSyms;
if (needed == 0) {
xkb->map->key_sym_map[key].offset = 0;
return xkb->map->syms;
}
nOldSyms = XkbKeyNumSyms(xkb, key);
if (nOldSyms >= (unsigned) needed) {
return XkbKeySymsPtr(xkb, key);
}
if (xkb->map->size_syms - xkb->map->num_syms >= (unsigned) needed) {
if (nOldSyms > 0) {
memcpy(&xkb->map->syms[xkb->map->num_syms], XkbKeySymsPtr(xkb, key),
nOldSyms * sizeof(KeySym));
}
if ((needed - nOldSyms) > 0) {
memset(&xkb->map->
syms[xkb->map->num_syms + XkbKeyNumSyms(xkb, key)], 0,
(needed - nOldSyms) * sizeof(KeySym));
}
xkb->map->key_sym_map[key].offset = xkb->map->num_syms;
xkb->map->num_syms += needed;
return &xkb->map->syms[xkb->map->key_sym_map[key].offset];
}
xkb->map->size_syms += (needed > 32 ? needed : 32);
newSyms = calloc(xkb->map->size_syms, sizeof(KeySym));
if (newSyms == NULL)
return NULL;
newSyms[0] = NoSymbol;
2003-11-14 15:54:54 +00:00
nSyms = 1;
for (i = xkb->min_key_code; i <= (int) xkb->max_key_code; i++) {
int nCopy;
nCopy = nKeySyms = XkbKeyNumSyms(xkb, i);
if ((nKeySyms == 0) && (i != key))
continue;
if (i == key)
nKeySyms = needed;
if (nCopy != 0)
memcpy(&newSyms[nSyms], XkbKeySymsPtr(xkb, i),
nCopy * sizeof(KeySym));
if (nKeySyms > nCopy)
memset(&newSyms[nSyms + nCopy], 0,
(nKeySyms - nCopy) * sizeof(KeySym));
xkb->map->key_sym_map[i].offset = nSyms;
nSyms += nKeySyms;
2003-11-14 15:54:54 +00:00
}
free(xkb->map->syms);
2003-11-14 15:54:54 +00:00
xkb->map->syms = newSyms;
xkb->map->num_syms = nSyms;
return &xkb->map->syms[xkb->map->key_sym_map[key].offset];
}
static unsigned
_ExtendRange(unsigned int old_flags,
unsigned int flag,
KeyCode newKC, KeyCode *old_min, unsigned char *old_num)
2003-11-14 15:54:54 +00:00
{
if ((old_flags & flag) == 0) {
old_flags |= flag;
*old_min = newKC;
*old_num = 1;
2003-11-14 15:54:54 +00:00
}
else {
int last = (*old_min) + (*old_num) - 1;
if (newKC < *old_min) {
*old_min = newKC;
*old_num = (last - newKC) + 1;
}
else if (newKC > last) {
*old_num = (newKC - (*old_min)) + 1;
}
2003-11-14 15:54:54 +00:00
}
return old_flags;
}
Status
XkbChangeKeycodeRange(XkbDescPtr xkb,
int minKC, int maxKC, XkbChangesPtr changes)
2003-11-14 15:54:54 +00:00
{
int tmp;
if ((!xkb) || (minKC < XkbMinLegalKeyCode) || (maxKC > XkbMaxLegalKeyCode))
return BadValue;
if (minKC > maxKC)
return BadMatch;
if (minKC < xkb->min_key_code) {
if (changes)
changes->map.min_key_code = minKC;
tmp = xkb->min_key_code - minKC;
if (xkb->map) {
if (xkb->map->key_sym_map) {
memset((char *) &xkb->map->key_sym_map[minKC], 0,
tmp * sizeof(XkbSymMapRec));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbKeySymsMask, minKC,
&changes->map.
first_key_sym,
&changes->map.
num_key_syms);
}
}
if (xkb->map->modmap) {
memset((char *) &xkb->map->modmap[minKC], 0, tmp);
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbModifierMapMask,
minKC,
&changes->map.
first_modmap_key,
&changes->map.
num_modmap_keys);
}
}
}
if (xkb->server) {
if (xkb->server->behaviors) {
memset((char *) &xkb->server->behaviors[minKC], 0,
tmp * sizeof(XkbBehavior));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbKeyBehaviorsMask,
minKC,
&changes->map.
first_key_behavior,
&changes->map.
num_key_behaviors);
}
}
if (xkb->server->key_acts) {
memset((char *) &xkb->server->key_acts[minKC], 0,
tmp * sizeof(unsigned short));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbKeyActionsMask,
minKC,
&changes->map.
first_key_act,
&changes->map.
num_key_acts);
}
}
if (xkb->server->vmodmap) {
memset((char *) &xkb->server->vmodmap[minKC], 0,
tmp * sizeof(unsigned short));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbVirtualModMapMask,
minKC,
&changes->map.
first_modmap_key,
&changes->map.
num_vmodmap_keys);
}
}
}
if ((xkb->names) && (xkb->names->keys)) {
memset((char *) &xkb->names->keys[minKC], 0,
tmp * sizeof(XkbKeyNameRec));
if (changes) {
changes->names.changed = _ExtendRange(changes->names.changed,
XkbKeyNamesMask, minKC,
&changes->names.first_key,
&changes->names.num_keys);
}
}
xkb->min_key_code = minKC;
}
if (maxKC > xkb->max_key_code) {
if (changes)
changes->map.max_key_code = maxKC;
xkb: Always use MAP_LENGTH keymap size Generating the modifier modmap, the helper function generate_modkeymap() would check the entire range up to the MAP_LENGTH. However, the given keymap might have less keycodes than MAP_LENGTH, in which case we would go beyond the size of the modmap, as reported by ASAN: ==ERROR: AddressSanitizer: heap-buffer-overflow READ of size 1 at 0x5110001c225b thread T0 #0 0x5e7369393873 in generate_modkeymap ../dix/inpututils.c:309 #1 0x5e736930dcce in ProcGetModifierMapping ../dix/devices.c:1794 #2 0x5e7369336489 in Dispatch ../dix/dispatch.c:550 #3 0x5e736934407d in dix_main ../dix/main.c:275 #5 0x7e46d47b2ecb in __libc_start_main #6 0x5e73691be324 in _start (xserver/build/hw/xwayland/Xwayland) Address is located 0 bytes after 219-byte region allocated by thread T0 here: #0 0x7e46d4cfc542 in realloc #1 0x5e73695aa90e in _XkbCopyClientMap ../xkb/xkbUtils.c:1142 #2 0x5e73695aa90e in XkbCopyKeymap ../xkb/xkbUtils.c:1966 #3 0x5e73695b1b2f in XkbDeviceApplyKeymap ../xkb/xkbUtils.c:2023 #4 0x5e73691c6c18 in keyboard_handle_keymap ../hw/xwayland/xwayland-input.c:1194 As MAP_LENGTH is used in various code paths where the max keycode might not be easily available, best is to always use MAP_LENGTH to allocate the keymaps so that the code never run past the buffer size. If the max key code is smaller than the MAP_LENGTH limit, fill-in the gap with zeros. That also simplifies the code slightly as we do not constantly need to reallocate the keymap to adjust to the max key code size. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1780 Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> (cherry picked from commit 92bcebfd7e248f695503c0a6e7bee80be4c96834) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1767>
2025-01-10 15:02:54 +01:00
tmp = MAP_LENGTH - xkb->max_key_code;
if (xkb->map) {
if (xkb->map->key_sym_map) {
memset((char *) &xkb->map->key_sym_map[xkb->max_key_code], 0,
tmp * sizeof(XkbSymMapRec));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbKeySymsMask, maxKC,
&changes->map.
first_key_sym,
&changes->map.
num_key_syms);
}
}
if (xkb->map->modmap) {
memset((char *) &xkb->map->modmap[xkb->max_key_code], 0, tmp);
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbModifierMapMask,
maxKC,
&changes->map.
first_modmap_key,
&changes->map.
num_modmap_keys);
}
}
}
if (xkb->server) {
if (xkb->server->behaviors) {
memset((char *) &xkb->server->behaviors[xkb->max_key_code], 0,
tmp * sizeof(XkbBehavior));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbKeyBehaviorsMask,
maxKC,
&changes->map.
first_key_behavior,
&changes->map.
num_key_behaviors);
}
}
if (xkb->server->key_acts) {
memset((char *) &xkb->server->key_acts[xkb->max_key_code], 0,
tmp * sizeof(unsigned short));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbKeyActionsMask,
maxKC,
&changes->map.
first_key_act,
&changes->map.
num_key_acts);
}
}
if (xkb->server->vmodmap) {
memset((char *) &xkb->server->vmodmap[xkb->max_key_code], 0,
tmp * sizeof(unsigned short));
if (changes) {
changes->map.changed = _ExtendRange(changes->map.changed,
XkbVirtualModMapMask,
maxKC,
&changes->map.
first_modmap_key,
&changes->map.
num_vmodmap_keys);
}
}
}
if ((xkb->names) && (xkb->names->keys)) {
memset((char *) &xkb->names->keys[xkb->max_key_code], 0,
tmp * sizeof(XkbKeyNameRec));
if (changes) {
changes->names.changed = _ExtendRange(changes->names.changed,
XkbKeyNamesMask, maxKC,
&changes->names.first_key,
&changes->names.num_keys);
}
}
xkb->max_key_code = maxKC;
2003-11-14 15:54:54 +00:00
}
return Success;
}
XkbAction *
XkbResizeKeyActions(XkbDescPtr xkb, int key, int needed)
2003-11-14 15:54:54 +00:00
{
register int i, nActs;
XkbAction *newActs;
xkb: Check that needed is > 0 in XkbResizeKeyActions Passing a negative value in `needed` to the `XkbResizeKeyActions()` function can create a `newActs` array of an unespected size. Check the value and return if it is invalid. This error has been found by a static analysis tool. This is the report: Error: OVERRUN (CWE-119): libX11-1.8.7/src/xkb/XKBMAlloc.c:811: cond_const: Checking "xkb->server->size_acts == 0" implies that "xkb->server->size_acts" is 0 on the true branch. libX11-1.8.7/src/xkb/XKBMAlloc.c:811: buffer_alloc: "calloc" allocates 8 bytes dictated by parameters "(size_t)((xkb->server->size_acts == 0) ? 1 : xkb->server->size_acts)" and "8UL". libX11-1.8.7/src/xkb/XKBMAlloc.c:811: var_assign: Assigning: "newActs" = "calloc((size_t)((xkb->server->size_acts == 0) ? 1 : xkb->server->size_acts), 8UL)". libX11-1.8.7/src/xkb/XKBMAlloc.c:815: assignment: Assigning: "nActs" = "1". libX11-1.8.7/src/xkb/XKBMAlloc.c:829: cond_at_least: Checking "nCopy > 0" implies that "nCopy" is at least 1 on the true branch. libX11-1.8.7/src/xkb/XKBMAlloc.c:830: overrun-buffer-arg: Overrunning buffer pointed to by "&newActs[nActs]" of 8 bytes by passing it to a function which accesses it at byte offset 15 using argument "nCopy * 8UL" (which evaluates to 8). # 828| # 829| if (nCopy > 0) # 830|-> memcpy(&newActs[nActs], XkbKeyActionsPtr(xkb, i), # 831| nCopy * sizeof(XkbAction)); # 832| if (nCopy < nKeyActs) (cherry picked from xorg/lib/libx11@af1312d2873d2ce49b18708a5029895aed477392) Signed-off-by: José Expósito <jexposit@redhat.com> Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com> (cherry picked from commit 6d3383418672dd87e6cd73931268fcbacdaae9c8) Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1894>
2024-04-30 17:37:39 +02:00
if (needed <= 0) {
xkb->server->key_acts[key] = 0;
return NULL;
}
if (XkbKeyHasActions(xkb, key) &&
(XkbKeyNumSyms(xkb, key) >= (unsigned) needed))
return XkbKeyActionsPtr(xkb, key);
if (xkb->server->size_acts - xkb->server->num_acts >= (unsigned) needed) {
xkb->server->key_acts[key] = xkb->server->num_acts;
xkb->server->num_acts += needed;
return &xkb->server->acts[xkb->server->key_acts[key]];
}
xkb->server->size_acts = xkb->server->num_acts + needed + 8;
newActs = calloc(xkb->server->size_acts, sizeof(XkbAction));
if (newActs == NULL)
return NULL;
2003-11-14 15:54:54 +00:00
newActs[0].type = XkbSA_NoAction;
nActs = 1;
for (i = xkb->min_key_code; i <= (int) xkb->max_key_code; i++) {
int nKeyActs, nCopy;
if ((xkb->server->key_acts[i] == 0) && (i != key))
continue;
nCopy = nKeyActs = XkbKeyNumActions(xkb, i);
if (i == key) {
nKeyActs = needed;
if (needed < nCopy)
nCopy = needed;
}
if (nCopy > 0)
memcpy(&newActs[nActs], XkbKeyActionsPtr(xkb, i),
nCopy * sizeof(XkbAction));
if (nCopy < nKeyActs)
memset(&newActs[nActs + nCopy], 0,
(nKeyActs - nCopy) * sizeof(XkbAction));
xkb->server->key_acts[i] = nActs;
nActs += nKeyActs;
2003-11-14 15:54:54 +00:00
}
free(xkb->server->acts);
2003-11-14 15:54:54 +00:00
xkb->server->acts = newActs;
xkb->server->num_acts = nActs;
2003-11-14 15:54:54 +00:00
return &xkb->server->acts[xkb->server->key_acts[key]];
}
void
XkbFreeClientMap(XkbDescPtr xkb, unsigned what, Bool freeMap)
2003-11-14 15:54:54 +00:00
{
XkbClientMapPtr map;
2003-11-14 15:54:54 +00:00
if ((xkb == NULL) || (xkb->map == NULL))
return;
2003-11-14 15:54:54 +00:00
if (freeMap)
what = XkbAllClientInfoMask;
map = xkb->map;
if (what & XkbKeyTypesMask) {
if (map->types != NULL) {
if (map->num_types > 0) {
register int i;
XkbKeyTypePtr type;
for (i = 0, type = map->types; i < map->num_types; i++, type++) {
free(type->map);
type->map = NULL;
free(type->preserve);
type->preserve = NULL;
type->map_count = 0;
free(type->level_names);
type->level_names = NULL;
}
}
free(map->types);
map->num_types = map->size_types = 0;
map->types = NULL;
}
}
if (what & XkbKeySymsMask) {
free(map->key_sym_map);
map->key_sym_map = NULL;
if (map->syms != NULL) {
free(map->syms);
map->size_syms = map->num_syms = 0;
map->syms = NULL;
}
}
if ((what & XkbModifierMapMask) && (map->modmap != NULL)) {
free(map->modmap);
map->modmap = NULL;
2003-11-14 15:54:54 +00:00
}
if (freeMap) {
free(xkb->map);
xkb->map = NULL;
2003-11-14 15:54:54 +00:00
}
return;
}
void
XkbFreeServerMap(XkbDescPtr xkb, unsigned what, Bool freeMap)
2003-11-14 15:54:54 +00:00
{
XkbServerMapPtr map;
2003-11-14 15:54:54 +00:00
if ((xkb == NULL) || (xkb->server == NULL))
return;
2003-11-14 15:54:54 +00:00
if (freeMap)
what = XkbAllServerInfoMask;
map = xkb->server;
if ((what & XkbExplicitComponentsMask) && (map->explicit != NULL)) {
free(map->explicit);
map->explicit = NULL;
}
if (what & XkbKeyActionsMask) {
free(map->key_acts);
map->key_acts = NULL;
if (map->acts != NULL) {
free(map->acts);
map->num_acts = map->size_acts = 0;
map->acts = NULL;
}
}
if ((what & XkbKeyBehaviorsMask) && (map->behaviors != NULL)) {
free(map->behaviors);
map->behaviors = NULL;
}
if ((what & XkbVirtualModMapMask) && (map->vmodmap != NULL)) {
free(map->vmodmap);
map->vmodmap = NULL;
2003-11-14 15:54:54 +00:00
}
if (freeMap) {
free(xkb->server);
xkb->server = NULL;
2003-11-14 15:54:54 +00:00
}
return;
}