vt: prevent potential endless loop when finding a slot for a composed character

Composed characters are stored in a tree structure, using a key as
identifier. The key is calculated from the individual characters that
make up the composed character sequence.

Since the address space for keys is limited, collisions may occur. In
this case, we simply increment the key and try again.

It is theoretically possible to saturate the key space, in which case
we’ll get stuck in an endless loop.

Even if the key space isn’t fully saturated, we fairly easy reach a
point where there are so many collisions for each insertion, that
performance drops significantly.

Since key space is limited (it’s not like a hash table that we can
grow), our only option is to limit the number of collisions. If we
can’t find a slot within a hard code amount of collisions, the
character is simply dropped.
This commit is contained in:
Daniel Eklöf 2022-06-13 12:14:15 +02:00
parent dbe2c0a068
commit edd68732ad
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F

14
vt.c
View file

@ -738,8 +738,20 @@ action_utf8_print(struct terminal *term, char32_t wc)
xassert(wanted_count <= 255);
size_t collision_count = 0;
/* Look for existing combining chain */
while (true) {
if (collision_count > 128) {
static bool have_warned = false;
if (!have_warned) {
have_warned = true;
LOG_WARN("ignoring composed character: "
"too many collisions in hash table");
}
return;
}
const struct composed *cc = composed_lookup(term->composed, key);
if (cc == NULL)
break;
@ -756,6 +768,7 @@ action_utf8_print(struct terminal *term, char32_t wc)
cc->chars[wanted_count - 1] != wc)
{
key++;
collision_count++;
continue;
}
@ -766,6 +779,7 @@ action_utf8_print(struct terminal *term, char32_t wc)
if (!match) {
key++;
collision_count++;
continue;
}