fix: harden small core containers

This commit is contained in:
2026-08-12 15:54:16 +09:00
parent cec62cc40f
commit 103f2ec448
11 changed files with 136 additions and 18 deletions

11
dict.c
View File

@@ -64,13 +64,15 @@ dictopen(char *path)
Biobuf *b;
Str key;
char *line, *tab;
int len;
int len, lineno;
b = Bopen(path, OREAD);
if(b == nil)
die("can't open: %s", path);
h = hmapalloc(4096);
lineno = 0;
while((line = Brdstr(b, '\n', 1)) != nil){
lineno++;
len = strlen(line);
if(len > 0 && line[len-1] == '\r')
line[--len] = '\0';
@@ -84,6 +86,8 @@ dictopen(char *path)
continue;
}
*tab = '\0';
if(utflen(line) > Maxrunes)
die("dictionary key too long: %s:%d", path, lineno);
sinit(&key, line, tab - line);
hmapset(&h, &key, tab+1, len - (tab - line) - 1);
free(line);
@@ -101,7 +105,10 @@ dictinit(char *dir)
for(i = 0; i < nlang; i++){
if(langs[i].dictname == nil)
continue;
snprint(path, sizeof(path), "%s/%s.dict", dir, langs[i].dictname);
if(snprint(path, sizeof(path), "%s/%s.dict", dir,
langs[i].dictname) >= (int)sizeof path)
die("dictionary path too long: %s/%s.dict",
dir, langs[i].dictname);
langs[i].dict = dictopen(path);
}
}

14
hash.c
View File

@@ -25,7 +25,7 @@ hmapalloc(int nbuckets)
int nsz;
if(nbuckets < 1)
die("hmapalloc: no buckets");
return nil;
nsz = Tagsize;
store = emalloc(sizeof(*h) + nbuckets * nsz);
h = store;
@@ -54,6 +54,8 @@ hmapget(Hmap *h, Str *key)
Hnode *n;
uchar *v;
if(h == nil || key == nil || key->n < 0 || key->n > Maxrunes)
return nil;
v = h->nodes + (hash(key) % h->nbs) * h->nsz;
for(;;){
n = (Hnode*)v;
@@ -67,7 +69,7 @@ hmapget(Hmap *h, Str *key)
}
static char*
sdup(Str *s)
sdup(Str *s, int *len)
{
char buf[Maxutf];
char *p;
@@ -77,6 +79,7 @@ sdup(Str *s)
p = emalloc(n + 1);
memmove(p, buf, n);
p[n] = '\0';
*len = n;
return p;
}
@@ -121,6 +124,10 @@ hmapset(Hmap **store, Str *key, const char *val, int vlen)
int next;
vlong diff;
if(store == nil || *store == nil || key == nil ||
key->n < 0 || key->n > Maxrunes || vlen < 0 ||
(vlen > 0 && val == nil))
return;
newval = memdup(val, vlen);
h = *store;
v = h->nodes + (hash(key) % h->nbs) * h->nsz;
@@ -151,8 +158,7 @@ hmapset(Hmap **store, Str *key, const char *val, int vlen)
n = (Hnode*)v;
replace:
if(n->filled == 0){
n->key = sdup(key);
n->klen = strlen(n->key);
n->key = sdup(key, &n->klen);
n->filled = 1;
}
n->next = next;

18
main.c
View File

@@ -67,12 +67,18 @@ threadmain(int argc, char **argv)
dictresc = chancreate(sizeof(Dictres), 0);
mapinit(argv[1]);
dictinit(argv[1]);
proccreate(drawthread, nil, 16384);
proccreate(srvthread, nil, 16384);
proccreate(ibusthread, nil, 32768);
proccreate(waylandthread, nil, 32768);
threadcreate(dictthread, nil, 16384);
threadcreate(imthread, nil, 16384);
if(proccreate(drawthread, nil, 16384) < 0)
die("can't create draw worker");
if(proccreate(srvthread, nil, 16384) < 0)
die("can't create server worker");
if(proccreate(ibusthread, nil, 32768) < 0)
die("can't create IBus worker");
if(proccreate(waylandthread, nil, 32768) < 0)
die("can't create Wayland worker");
if(threadcreate(dictthread, nil, 16384) < 0)
die("can't create dictionary worker");
if(threadcreate(imthread, nil, 16384) < 0)
die("can't create input worker");
threadexits(nil);
}

5
str.c
View File

@@ -7,6 +7,8 @@ sinit(Str *s, char *src, int n)
int len;
s->n = 0;
if(n < 0 || (n > 0 && src == nil))
return;
while(n > 0 && s->n < Maxrunes){
if(!fullrune(src, n))
break;
@@ -28,8 +30,9 @@ sclear(Str *s)
void
sputr(Str *s, Rune r)
{
/* Str is a capped value; appends at capacity leave it unchanged. */
if(s->n >= Maxrunes)
die("sputr overflow");
return;
s->r[s->n++] = r;
}

View File

@@ -724,6 +724,12 @@ keystroke(u32int ks, u32int mod, Str *com)
}
if(im.l->trans == nil)
return 0;
if(im.pre.n >= Maxrunes ||
(isjp(&im) && im.pre.n + im.raw.n >= Maxrunes)){
commit(com);
reset();
return 0;
}
if(im.l->lang == LangKO)
ks = kokey(ks, mod);
n = dotrans(ks, com);
@@ -858,7 +864,9 @@ mapinit(char *dir)
for(i = 0; i < nelem(langs); i++){
if(langs[i].mapname == nil)
continue;
snprint(path, sizeof(path), "%s/%s.map", dir, langs[i].mapname);
if(snprint(path, sizeof(path), "%s/%s.map", dir,
langs[i].mapname) >= (int)sizeof path)
die("map path too long: %s/%s.map", dir, langs[i].mapname);
langs[i].map = trieopen(path);
}
}

View File

@@ -76,3 +76,37 @@ hmap_long_utf8_keys(struct ct *t)
cleanup:
hmapfree(h);
}
void
hmap_binary_keys_and_invalid_lengths(struct ct *t)
{
Hmap *h;
Hnode *n;
Str key, other;
h = hmapalloc(1);
CT_CHECK(t, h != nil);
key.n = 3;
key.r[0] = 'a';
key.r[1] = 0;
key.r[2] = 'b';
other = key;
other.r[2] = 'c';
hmapset(&h, &key, "one", 3);
hmapset(&h, &other, "two", 3);
n = hmapget(h, &key);
if(CT_CHECK(t, n != nil)){
CT_EQ_INT(t, 3, n->klen);
CT_EQ_MEM(t, "one", n->val, n->vlen);
}
n = hmapget(h, &other);
if(CT_CHECK(t, n != nil))
CT_EQ_MEM(t, "two", n->val, n->vlen);
hmapset(&h, &key, "bad", -1);
hmapset(&h, &key, nil, 1);
n = hmapget(h, &key);
if(CT_CHECK(t, n != nil))
CT_EQ_MEM(t, "one", n->val, n->vlen);
CT_EQ_PTR(t, nil, hmapalloc(0));
hmapfree(h);
}

View File

@@ -82,3 +82,21 @@ str_utf8_capacity(struct ct *t)
CT_EQ_STR(t, "😀", buf);
CT_EQ_INT(t, 'Z', buf[5]);
}
void
str_invalid_and_full_appends(struct ct *t)
{
Str s;
int i;
sinit(&s, nil, 1);
CT_EQ_INT(t, 0, s.n);
sinit(&s, "x", -1);
CT_EQ_INT(t, 0, s.n);
for(i = 0; i < Maxrunes; i++)
s.r[i] = 'a';
s.n = Maxrunes;
sputr(&s, 'z');
CT_EQ_INT(t, Maxrunes, s.n);
CT_EQ_INT(t, 'a', s.r[Maxrunes-1]);
}

View File

@@ -17,9 +17,12 @@ Str shownpre(Im*);
void str_init_utf8(struct ct*);
void str_edit_and_alias(struct ct*);
void str_utf8_capacity(struct ct*);
void str_invalid_and_full_appends(struct ct*);
void hmap_set_replace_and_grow(struct ct*);
void hmap_long_utf8_keys(struct ct*);
void hmap_binary_keys_and_invalid_lengths(struct ct*);
void trie_exact_prefix_and_duplicate(struct ct*);
void trie_optional_outputs_and_invalid_lengths(struct ct*);
void production_maps_load(struct ct*);
void transmap_states(struct ct*);
void korean_sequences(struct ct*);

View File

@@ -34,6 +34,29 @@ cleanup:
trieclose(trie);
}
void
trie_optional_outputs_and_invalid_lengths(struct ct *t)
{
char *v;
Trie *trie;
int n;
trie = trieopen("data/trie.map");
CT_CHECK(t, trieget(trie, "a", 1, nil) != nil);
v = nil;
CT_CHECK(t, trielookup(trie, "a", 1, &v, nil));
CT_CHECK(t, v != nil);
n = -1;
CT_CHECK(t, trielookup(trie, "a", 1, nil, &n));
CT_EQ_INT(t, 5, n);
CT_CHECK(t, trielookup(trie, "dupli", 5, nil, nil));
CT_EQ_PTR(t, nil, trieget(trie, nil, 1, &n));
CT_EQ_PTR(t, nil, trieget(trie, "a", -1, &n));
CT_CHECK(t, !trielookup(trie, nil, 1, &v, &n));
CT_CHECK(t, !trielookup(trie, "a", -1, &v, &n));
trieclose(trie);
}
void
production_maps_load(struct ct *t)
{

View File

@@ -66,9 +66,12 @@ static const struct ct_test tests[] = {
{ "str/init-utf8", str_init_utf8 },
{ "str/edit-and-alias", str_edit_and_alias },
{ "str/utf8-capacity", str_utf8_capacity },
{ "str/invalid-full-appends", str_invalid_and_full_appends },
{ "hmap/set-replace-grow", hmap_set_replace_and_grow },
{ "hmap/long-utf8-keys", hmap_long_utf8_keys },
{ "hmap/binary-invalid-lengths", hmap_binary_keys_and_invalid_lengths },
{ "trie/exact-prefix-duplicate", trie_exact_prefix_and_duplicate },
{ "trie/optional-invalid-lengths", trie_optional_outputs_and_invalid_lengths },
{ "map/production-lifecycle", production_maps_load },
{ "transmap/states", transmap_states },
{ "hangul/sequences", korean_sequences },

15
trie.c
View File

@@ -118,6 +118,8 @@ trieget(Trie *t, char *key, int klen, int *vlen)
int ni;
int i;
if(t == nil || klen < 0 || (klen > 0 && key == nil))
return nil;
ni = t->root;
for(i = 0; i < klen; i++){
ni = find(t, ni, key[i]);
@@ -126,7 +128,8 @@ trieget(Trie *t, char *key, int klen, int *vlen)
}
if(t->nodes[ni].val == nil)
return nil;
*vlen = t->nodes[ni].vlen;
if(vlen != nil)
*vlen = t->nodes[ni].vlen;
return t->nodes[ni].val;
}
@@ -136,15 +139,19 @@ trielookup(Trie *t, char *key, int klen, char **val, int *vlen)
int ni;
int i;
if(t == nil || klen < 0 || (klen > 0 && key == nil))
return 0;
ni = t->root;
for(i = 0; i < klen; i++){
ni = find(t, ni, key[i]);
if(ni < 0)
return 0;
}
if(val != nil && t->nodes[ni].val != nil){
*val = t->nodes[ni].val;
*vlen = t->nodes[ni].vlen;
if(t->nodes[ni].val != nil){
if(val != nil)
*val = t->nodes[ni].val;
if(vlen != nil)
*vlen = t->nodes[ni].vlen;
}
return 1;
}