]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
uni_utf8_*(): Treat overlong UTF8 sequences as invalid.
authorTimo Sirainen <tss@iki.fi>
Fri, 4 Jan 2013 21:24:26 +0000 (23:24 +0200)
committerTimo Sirainen <tss@iki.fi>
Fri, 4 Jan 2013 21:24:26 +0000 (23:24 +0200)
src/lib/Makefile.am
src/lib/test-lib.c
src/lib/test-lib.h
src/lib/test-unichar.c [new file with mode: 0644]
src/lib/unichar.c

index 0bd56f02cae59f4a0e00979ddaaf1f628e9eb0c2..eeccfd86fd86fd4a71f7bf5260074960edda2ae0 100644 (file)
@@ -273,6 +273,7 @@ test_lib_SOURCES = \
        test-str-find.c \
        test-str-sanitize.c \
        test-time-util.c \
+       test-unichar.c \
        test-utc-mktime.c \
        test-var-expand.c
 
index 1af0760f6d6cae5e8047c24571f706e265e1ead9..ca6f223f3d699992859110def15202c66b7639a8 100644 (file)
@@ -30,6 +30,7 @@ int main(void)
                test_str_find,
                test_str_sanitize,
                test_time_util,
+               test_unichar,
                test_utc_mktime,
                test_var_expand,
                NULL
index 5993939f95162952c1c915d9a775e9fa284cd8f2..c7cc7b9e36afa04bfc5e9bc5c63c2ac264d63d8a 100644 (file)
@@ -29,6 +29,7 @@ void test_strfuncs(void);
 void test_str_find(void);
 void test_str_sanitize(void);
 void test_time_util(void);
+void test_unichar(void);
 void test_utc_mktime(void);
 void test_var_expand(void);
 
diff --git a/src/lib/test-unichar.c b/src/lib/test-unichar.c
new file mode 100644 (file)
index 0000000..61d2ec2
--- /dev/null
@@ -0,0 +1,24 @@
+/* Copyright (c) 2007-2012 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "str.h"
+#include "unichar.h"
+
+void test_unichar(void)
+{
+       static const char *overlong_utf8 = "\xf8\x80\x95\x81\xa1";
+       unichar_t chr, chr2;
+       string_t *str = t_str_new(16);
+
+       test_begin("unichars");
+       for (chr = 0; chr <= 0x10ffff; chr++) {
+               str_truncate(str, 0);
+               uni_ucs4_to_utf8_c(chr, str);
+               test_assert(uni_utf8_str_is_valid(str_c(str)));
+               test_assert(uni_utf8_get_char(str_c(str), &chr2) > 0);
+               test_assert(chr2 == chr);
+       }
+       test_assert(!uni_utf8_str_is_valid(overlong_utf8));
+       test_assert(uni_utf8_get_char(overlong_utf8, &chr2) < 0);
+       test_end();
+}
index ba35626387585e37cd2c26473060dc573ab13b89..8dd78c2981f155342107a7f211cdf41947e7c463 100644 (file)
@@ -37,8 +37,10 @@ int uni_utf8_get_char(const char *input, unichar_t *chr_r)
 
 int uni_utf8_get_char_n(const void *_input, size_t max_len, unichar_t *chr_r)
 {
+       static unichar_t lowest_valid_chr_table[] =
+               { 0, 0, 0x80, 0x800, 0x10000, 0x20000, 0x40000 };
        const unsigned char *input = _input;
-       unichar_t chr;
+       unichar_t chr, lowest_valid_chr;
        unsigned int i, len;
        int ret;
 
@@ -75,10 +77,12 @@ int uni_utf8_get_char_n(const void *_input, size_t max_len, unichar_t *chr_r)
                return -1;
        }
 
-       if (len <= max_len)
+       if (len <= max_len) {
+               lowest_valid_chr = lowest_valid_chr_table[len];
                ret = 1;
-       else {
+       else {
                /* check first if the input is invalid before returning 0 */
+               lowest_valid_chr = 0;
                ret = 0;
                len = max_len;
        }
@@ -91,6 +95,10 @@ int uni_utf8_get_char_n(const void *_input, size_t max_len, unichar_t *chr_r)
                chr <<= 6;
                chr |= input[i] & 0x3f;
        }
+       if (chr < lowest_valid_chr) {
+               /* overlong encoding */
+               return -1;
+       }
 
        *chr_r = chr;
        return ret;
@@ -340,19 +348,11 @@ int uni_utf8_to_decomposed_titlecase(const void *_input, size_t max_len,
 static inline unsigned int
 is_valid_utf8_seq(const unsigned char *input, unsigned int size)
 {
-       unsigned int i, len;
+       unichar_t chr;
 
-       len = uni_utf8_char_bytes(input[0]);
-       if (unlikely(len > size || len == 1))
+       if (uni_utf8_get_char_n(input, size, &chr) <= 0)
                return 0;
-
-       /* the rest of the chars should be in 0x80..0xbf range.
-          anything else is start of a sequence or invalid */
-       for (i = 1; i < len; i++) {
-               if (unlikely(input[i] < 0x80 || input[i] > 0xbf))
-                       return 0;
-       }
-       return len;
+       return uni_utf8_char_bytes(input[0]);
 }
 
 static int uni_utf8_find_invalid_pos(const unsigned char *input, size_t size,