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);  	}  | 
