diff --git a/dict.c b/dict.c index e4bb646..2e9f691 100644 --- a/dict.c +++ b/dict.c @@ -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); } } diff --git a/hash.c b/hash.c index 1b50926..9522075 100644 --- a/hash.c +++ b/hash.c @@ -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; diff --git a/main.c b/main.c index 62d4e91..351ad03 100644 --- a/main.c +++ b/main.c @@ -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); } diff --git a/str.c b/str.c index 75044c2..2d694b3 100644 --- a/str.c +++ b/str.c @@ -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; } diff --git a/strans.c b/strans.c index 032e7f3..26efc2c 100644 --- a/strans.c +++ b/strans.c @@ -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); } } diff --git a/tests/hash_test.c b/tests/hash_test.c index 631809d..c20478a 100644 --- a/tests/hash_test.c +++ b/tests/hash_test.c @@ -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); +} diff --git a/tests/str_test.c b/tests/str_test.c index 4f1ed81..0794365 100644 --- a/tests/str_test.c +++ b/tests/str_test.c @@ -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]); +} diff --git a/tests/test.h b/tests/test.h index 1af9a44..4698951 100644 --- a/tests/test.h +++ b/tests/test.h @@ -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*); diff --git a/tests/trie_test.c b/tests/trie_test.c index b7a3134..d6230b7 100644 --- a/tests/trie_test.c +++ b/tests/trie_test.c @@ -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) { diff --git a/tests/unit_test.c b/tests/unit_test.c index 28dd8d4..8eccf45 100644 --- a/tests/unit_test.c +++ b/tests/unit_test.c @@ -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 }, diff --git a/trie.c b/trie.c index 4ffdee8..af92b47 100644 --- a/trie.c +++ b/trie.c @@ -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; }