1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Kernel module for testing utf-8 support. 4 * 5 * Copyright 2017 Collabora Ltd. 6 */ 7 8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9 10 #include <linux/module.h> 11 #include <linux/printk.h> 12 #include <linux/unicode.h> 13 #include <linux/dcache.h> 14 15 #include "utf8n.h" 16 17 static unsigned int failed_tests; 18 static unsigned int total_tests; 19 20 #define _test(cond, func, line, fmt, ...) do { \ 21 total_tests++; \ 22 if (!cond) { \ 23 failed_tests++; \ 24 pr_err("test %s:%d Failed: %s%s", \ 25 func, line, #cond, (fmt?":":".")); \ 26 if (fmt) \ 27 pr_err(fmt, ##__VA_ARGS__); \ 28 } \ 29 } while (0) 30 #define test_f(cond, fmt, ...) _test(cond, __func__, __LINE__, fmt, ##__VA_ARGS__) 31 #define test(cond) _test(cond, __func__, __LINE__, "") 32 33 static const struct { 34 /* UTF-8 strings in this vector _must_ be NULL-terminated. */ 35 unsigned char str[10]; 36 unsigned char dec[10]; 37 } nfdi_test_data[] = { 38 /* Trivial sequence */ 39 { 40 /* "ABba" decomposes to itself */ 41 .str = "aBba", 42 .dec = "aBba", 43 }, 44 /* Simple equivalent sequences */ 45 { 46 /* 'VULGAR FRACTION ONE QUARTER' cannot decompose to 47 'NUMBER 1' + 'FRACTION SLASH' + 'NUMBER 4' on 48 canonical decomposition */ 49 .str = {0xc2, 0xbc, 0x00}, 50 .dec = {0xc2, 0xbc, 0x00}, 51 }, 52 { 53 /* 'LATIN SMALL LETTER A WITH DIAERESIS' decomposes to 54 'LETTER A' + 'COMBINING DIAERESIS' */ 55 .str = {0xc3, 0xa4, 0x00}, 56 .dec = {0x61, 0xcc, 0x88, 0x00}, 57 }, 58 { 59 /* 'LATIN SMALL LETTER LJ' can't decompose to 60 'LETTER L' + 'LETTER J' on canonical decomposition */ 61 .str = {0xC7, 0x89, 0x00}, 62 .dec = {0xC7, 0x89, 0x00}, 63 }, 64 { 65 /* GREEK ANO TELEIA decomposes to MIDDLE DOT */ 66 .str = {0xCE, 0x87, 0x00}, 67 .dec = {0xC2, 0xB7, 0x00} 68 }, 69 /* Canonical ordering */ 70 { 71 /* A + 'COMBINING ACUTE ACCENT' + 'COMBINING OGONEK' decomposes 72 to A + 'COMBINING OGONEK' + 'COMBINING ACUTE ACCENT' */ 73 .str = {0x41, 0xcc, 0x81, 0xcc, 0xa8, 0x0}, 74 .dec = {0x41, 0xcc, 0xa8, 0xcc, 0x81, 0x0}, 75 }, 76 { 77 /* 'LATIN SMALL LETTER A WITH DIAERESIS' + 'COMBINING OGONEK' 78 decomposes to 79 'LETTER A' + 'COMBINING OGONEK' + 'COMBINING DIAERESIS' */ 80 .str = {0xc3, 0xa4, 0xCC, 0xA8, 0x00}, 81 82 .dec = {0x61, 0xCC, 0xA8, 0xcc, 0x88, 0x00}, 83 }, 84 85 }; 86 87 static const struct { 88 /* UTF-8 strings in this vector _must_ be NULL-terminated. */ 89 unsigned char str[30]; 90 unsigned char ncf[30]; 91 } nfdicf_test_data[] = { 92 /* Trivial sequences */ 93 { 94 /* "ABba" folds to lowercase */ 95 .str = {0x41, 0x42, 0x62, 0x61, 0x00}, 96 .ncf = {0x61, 0x62, 0x62, 0x61, 0x00}, 97 }, 98 { 99 /* All ASCII folds to lower-case */ 100 .str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0.1", 101 .ncf = "abcdefghijklmnopqrstuvwxyz0.1", 102 }, 103 { 104 /* LATIN SMALL LETTER SHARP S folds to 105 LATIN SMALL LETTER S + LATIN SMALL LETTER S */ 106 .str = {0xc3, 0x9f, 0x00}, 107 .ncf = {0x73, 0x73, 0x00}, 108 }, 109 { 110 /* LATIN CAPITAL LETTER A WITH RING ABOVE folds to 111 LATIN SMALL LETTER A + COMBINING RING ABOVE */ 112 .str = {0xC3, 0x85, 0x00}, 113 .ncf = {0x61, 0xcc, 0x8a, 0x00}, 114 }, 115 /* Introduced by UTF-8.0.0. */ 116 /* Cherokee letters are interesting test-cases because they fold 117 to upper-case. Before 8.0.0, Cherokee lowercase were 118 undefined, thus, the folding from LC is not stable between 119 7.0.0 -> 8.0.0, but it is from UC. */ 120 { 121 /* CHEROKEE SMALL LETTER A folds to CHEROKEE LETTER A */ 122 .str = {0xea, 0xad, 0xb0, 0x00}, 123 .ncf = {0xe1, 0x8e, 0xa0, 0x00}, 124 }, 125 { 126 /* CHEROKEE SMALL LETTER YE folds to CHEROKEE LETTER YE */ 127 .str = {0xe1, 0x8f, 0xb8, 0x00}, 128 .ncf = {0xe1, 0x8f, 0xb0, 0x00}, 129 }, 130 { 131 /* OLD HUNGARIAN CAPITAL LETTER AMB folds to 132 OLD HUNGARIAN SMALL LETTER AMB */ 133 .str = {0xf0, 0x90, 0xb2, 0x83, 0x00}, 134 .ncf = {0xf0, 0x90, 0xb3, 0x83, 0x00}, 135 }, 136 /* Introduced by UTF-9.0.0. */ 137 { 138 /* OSAGE CAPITAL LETTER CHA folds to 139 OSAGE SMALL LETTER CHA */ 140 .str = {0xf0, 0x90, 0x92, 0xb5, 0x00}, 141 .ncf = {0xf0, 0x90, 0x93, 0x9d, 0x00}, 142 }, 143 { 144 /* LATIN CAPITAL LETTER SMALL CAPITAL I folds to 145 LATIN LETTER SMALL CAPITAL I */ 146 .str = {0xea, 0x9e, 0xae, 0x00}, 147 .ncf = {0xc9, 0xaa, 0x00}, 148 }, 149 /* Introduced by UTF-11.0.0. */ 150 { 151 /* GEORGIAN SMALL LETTER AN folds to GEORGIAN MTAVRULI 152 CAPITAL LETTER AN */ 153 .str = {0xe1, 0xb2, 0x90, 0x00}, 154 .ncf = {0xe1, 0x83, 0x90, 0x00}, 155 } 156 }; 157 158 static ssize_t utf8len(const struct unicode_map *um, enum utf8_normalization n, 159 const char *s) 160 { 161 return utf8nlen(um, n, s, (size_t)-1); 162 } 163 164 static int utf8cursor(struct utf8cursor *u8c, const struct unicode_map *um, 165 enum utf8_normalization n, const char *s) 166 { 167 return utf8ncursor(u8c, um, n, s, (unsigned int)-1); 168 } 169 170 static void check_utf8_nfdi(struct unicode_map *um) 171 { 172 int i; 173 struct utf8cursor u8c; 174 175 for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) { 176 int len = strlen(nfdi_test_data[i].str); 177 int nlen = strlen(nfdi_test_data[i].dec); 178 int j = 0; 179 unsigned char c; 180 181 test((utf8len(um, UTF8_NFDI, nfdi_test_data[i].str) == nlen)); 182 test((utf8nlen(um, UTF8_NFDI, nfdi_test_data[i].str, len) == 183 nlen)); 184 185 if (utf8cursor(&u8c, um, UTF8_NFDI, nfdi_test_data[i].str) < 0) 186 pr_err("can't create cursor\n"); 187 188 while ((c = utf8byte(&u8c)) > 0) { 189 test_f((c == nfdi_test_data[i].dec[j]), 190 "Unexpected byte 0x%x should be 0x%x\n", 191 c, nfdi_test_data[i].dec[j]); 192 j++; 193 } 194 195 test((j == nlen)); 196 } 197 } 198 199 static void check_utf8_nfdicf(struct unicode_map *um) 200 { 201 int i; 202 struct utf8cursor u8c; 203 204 for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) { 205 int len = strlen(nfdicf_test_data[i].str); 206 int nlen = strlen(nfdicf_test_data[i].ncf); 207 int j = 0; 208 unsigned char c; 209 210 test((utf8len(um, UTF8_NFDICF, nfdicf_test_data[i].str) == 211 nlen)); 212 test((utf8nlen(um, UTF8_NFDICF, nfdicf_test_data[i].str, len) == 213 nlen)); 214 215 if (utf8cursor(&u8c, um, UTF8_NFDICF, 216 nfdicf_test_data[i].str) < 0) 217 pr_err("can't create cursor\n"); 218 219 while ((c = utf8byte(&u8c)) > 0) { 220 test_f((c == nfdicf_test_data[i].ncf[j]), 221 "Unexpected byte 0x%x should be 0x%x\n", 222 c, nfdicf_test_data[i].ncf[j]); 223 j++; 224 } 225 226 test((j == nlen)); 227 } 228 } 229 230 static void check_utf8_comparisons(struct unicode_map *table) 231 { 232 int i; 233 234 for (i = 0; i < ARRAY_SIZE(nfdi_test_data); i++) { 235 const struct qstr s1 = {.name = nfdi_test_data[i].str, 236 .len = sizeof(nfdi_test_data[i].str)}; 237 const struct qstr s2 = {.name = nfdi_test_data[i].dec, 238 .len = sizeof(nfdi_test_data[i].dec)}; 239 240 test_f(!utf8_strncmp(table, &s1, &s2), 241 "%s %s comparison mismatch\n", s1.name, s2.name); 242 } 243 244 for (i = 0; i < ARRAY_SIZE(nfdicf_test_data); i++) { 245 const struct qstr s1 = {.name = nfdicf_test_data[i].str, 246 .len = sizeof(nfdicf_test_data[i].str)}; 247 const struct qstr s2 = {.name = nfdicf_test_data[i].ncf, 248 .len = sizeof(nfdicf_test_data[i].ncf)}; 249 250 test_f(!utf8_strncasecmp(table, &s1, &s2), 251 "%s %s comparison mismatch\n", s1.name, s2.name); 252 } 253 } 254 255 static void check_supported_versions(struct unicode_map *um) 256 { 257 /* Unicode 7.0.0 should be supported. */ 258 test(utf8version_is_supported(um, UNICODE_AGE(7, 0, 0))); 259 260 /* Unicode 9.0.0 should be supported. */ 261 test(utf8version_is_supported(um, UNICODE_AGE(9, 0, 0))); 262 263 /* Unicode 1x.0.0 (the latest version) should be supported. */ 264 test(utf8version_is_supported(um, UTF8_LATEST)); 265 266 /* Next versions don't exist. */ 267 test(!utf8version_is_supported(um, UNICODE_AGE(13, 0, 0))); 268 test(!utf8version_is_supported(um, UNICODE_AGE(0, 0, 0))); 269 test(!utf8version_is_supported(um, UNICODE_AGE(-1, -1, -1))); 270 } 271 272 static int __init init_test_ucd(void) 273 { 274 struct unicode_map *um; 275 276 failed_tests = 0; 277 total_tests = 0; 278 279 um = utf8_load(UTF8_LATEST); 280 if (IS_ERR(um)) { 281 pr_err("%s: Unable to load utf8 table.\n", __func__); 282 return PTR_ERR(um); 283 } 284 285 check_supported_versions(um); 286 check_utf8_nfdi(um); 287 check_utf8_nfdicf(um); 288 check_utf8_comparisons(um); 289 290 if (!failed_tests) 291 pr_info("All %u tests passed\n", total_tests); 292 else 293 pr_err("%u out of %u tests failed\n", failed_tests, 294 total_tests); 295 utf8_unload(um); 296 return 0; 297 } 298 299 static void __exit exit_test_ucd(void) 300 { 301 } 302 303 module_init(init_test_ucd); 304 module_exit(exit_test_ucd); 305 306 MODULE_AUTHOR("Gabriel Krisman Bertazi <krisman@collabora.co.uk>"); 307 MODULE_DESCRIPTION("Kernel module for testing utf-8 support"); 308 MODULE_LICENSE("GPL"); 309