From ebfa320956169e3419234b72fee51bd596867661 Mon Sep 17 00:00:00 2001 From: "Reece H. Dunn" Date: Wed, 26 Jul 2017 20:44:27 +0100 Subject: [PATCH] Fix storing the line length in the hash chain. The length is stored as the first byte in the output from compile_line. As the data pointer is char, if char is signed then length could be negative resulting in undefined behaviour. This commit fixes the issue by reading and writing that byte as a uint8_t. This bug was caused by 2a00ca79f635d8e7979ad09846901d59a4ea4ee0. Previously, the entries could only be a maximum of 128 bytes, and would not be negative on platforms with signed chars. That commit was made to support long emoji entries, especially for non-Latin languages where the utf-8 representations could be longer than 128 bytes. This change also adds some documentation to make it clearer what is going on. NOTE: The code should really be using actual struct objects instead of writing to opaque char buffers. Reported by Reef Turner --- src/libespeak-ng/compiledict.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libespeak-ng/compiledict.c b/src/libespeak-ng/compiledict.c index 12e80910..509b8fa7 100644 --- a/src/libespeak-ng/compiledict.c +++ b/src/libespeak-ng/compiledict.c @@ -52,7 +52,15 @@ static int text_mode = 0; static int debug_flag = 0; static int error_need_dictionary = 0; +// A hash chain is a linked-list of hash chain entry objects: +// struct hash_chain_entry { +// hash_chain_entry *next_entry; +// // dict_line output from compile_line: +// uint8_t length; +// char contents[length]; +// }; static char *hash_chains[N_HASH_DICT]; + static char letterGroupsDefined[N_LETTER_GROUPS]; MNEM_TAB mnem_rules[] = { @@ -648,7 +656,7 @@ static int compile_line(char *linebuf, char *dict_line, int n_dict_line, int *ha length += ix; } } - dict_line[0] = length; + *((uint8_t *)dict_line) = (uint8_t)length; return length; } @@ -682,7 +690,7 @@ static void compile_dictlist_end(FILE *f_out) p = hash_chains[hash]; while (p != NULL) { - length = *(p+sizeof(char *)); + length = *(uint8_t *)(p+sizeof(char *)); fwrite(p+sizeof(char *), length, 1, f_out); memcpy(&p, p, sizeof(char *)); } @@ -699,7 +707,7 @@ static int compile_dictlist_file(const char *path, const char *filename) FILE *f_in; char buf[200]; char fname[sizeof(path_home)+45]; - char dict_line[200]; + char dict_line[256]; // length is uint8_t, so an entry can't take up more than 256 bytes text_mode = 0; @@ -733,6 +741,7 @@ static int compile_dictlist_file(const char *path, const char *filename) memcpy(p, &hash_chains[hash], sizeof(char *)); hash_chains[hash] = p; + // NOTE: dict_line[0] is the entry length (0-255) memcpy(p+sizeof(char *), dict_line, length); count++; }