diff options
-rw-r--r-- | common/glib-ectomy.c | 87 | ||||
-rw-r--r-- | common/glib-ectomy.h | 2 | ||||
-rw-r--r-- | hcid/dbus-adapter.c | 4 | ||||
-rw-r--r-- | hcid/security.c | 3 |
4 files changed, 94 insertions, 2 deletions
diff --git a/common/glib-ectomy.c b/common/glib-ectomy.c index 33d20533..56357648 100644 --- a/common/glib-ectomy.c +++ b/common/glib-ectomy.c @@ -515,3 +515,90 @@ gint g_timeout_remove(const guint id) return -1; } + +/* UTF-8 Validation: approximate copy/paste from glib2. */ + +#define UNICODE_VALID(c) \ + ((c) < 0x110000 && \ + (((c) & 0xFFFFF800) != 0xD800) && \ + ((c) < 0xFDD0 || (c) > 0xFDEF) && \ + ((c) & 0xFFFE) != 0xFFFE) + +#define CONTINUATION_CHAR(c, val) \ + do { \ + if (((c) & 0xc0) != 0x80) /* 10xxxxxx */ \ + goto failed; \ + (val) <<= 6; \ + (val) |= (c) & 0x3f; \ + } while (0) + +#define INCREMENT_AND_CHECK_MAX(i, max_len) \ + do { \ + (i)++; \ + if ((max_len) >= 0 && (i) >= (max_len)) \ + goto failed; \ + } while (0) + + +gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end) +{ + unsigned long val, min, i; + const unsigned char *p, *last; + + min = val = 0; + + for (p = (unsigned char *) str, i = 0; p[i]; i++) { + if (max_len >= 0 && i >= max_len) + break; + + if (p[i] < 128) + continue; + + last = &p[i]; + + if ((p[i] & 0xe0) == 0xc0) { /* 110xxxxx */ + if ((p[i] & 0x1e) == 0) + goto failed; + INCREMENT_AND_CHECK_MAX(i, max_len); + if ((p[i] & 0xc0) != 0x80) + goto failed; /* 10xxxxxx */ + } else { + if ((p[i] & 0xf0) == 0xe0) { + /* 1110xxxx */ + min = (1 << 11); + val = p[i] & 0x0f; + goto two_remaining; + } else if ((p[i] & 0xf8) == 0xf0) { + /* 11110xxx */ + min = (1 << 16); + val = p[i] & 0x07; + } else + goto failed; + + INCREMENT_AND_CHECK_MAX(i, max_len); + CONTINUATION_CHAR(p[i], val); +two_remaining: + INCREMENT_AND_CHECK_MAX(i, max_len); + CONTINUATION_CHAR(p[i], val); + + INCREMENT_AND_CHECK_MAX(i, max_len); + CONTINUATION_CHAR(p[i], val); + + if (val < min || !UNICODE_VALID(val)) + goto failed; + } + } + + if (end) + *end = (const gchar *) &p[i]; + + return TRUE; + +failed: + if (end) + *end = (const gchar *) last; + + return FALSE; +} + + diff --git a/common/glib-ectomy.h b/common/glib-ectomy.h index 2885f019..4d00870c 100644 --- a/common/glib-ectomy.h +++ b/common/glib-ectomy.h @@ -93,6 +93,8 @@ void g_main_loop_unref(GMainLoop *loop); guint g_timeout_add(guint interval, GSourceFunc function, gpointer data); gint g_timeout_remove(const guint id); +gboolean g_utf8_validate(const gchar *str, gssize max_len, const gchar **end); + #define g_main_new(is_running) g_main_loop_new(NULL, is_running); #define g_main_run(loop) g_main_loop_run(loop) #define g_main_quit(loop) g_main_loop_quit(loop) diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index 3fbd0c36..2989536c 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -1036,8 +1036,8 @@ static DBusHandlerResult handle_dev_set_name_req(DBusConnection *conn, DBusMessa dbus_error_free(&err); return error_invalid_arguments(conn, msg); } - - if (strlen(str_ptr) == 0) { + + if (strlen(str_ptr) == 0 || !g_utf8_validate(str_ptr, -1, NULL)) { error("Name change failed: Invalid parameter"); return error_invalid_arguments(conn, msg); } diff --git a/hcid/security.c b/hcid/security.c index d44bfea4..e1c29ea9 100644 --- a/hcid/security.c +++ b/hcid/security.c @@ -464,7 +464,10 @@ static inline void remote_name_information(int dev, bdaddr_t *sba, void *ptr) bacpy(&dba, &evt->bdaddr); if (!evt->status) { + char *end; memcpy(name, evt->name, 248); + if (!g_utf8_validate(name, -1, &end)) + *end = '\0'; write_device_name(sba, &dba, name); } |