summaryrefslogtreecommitdiffstats
path: root/dbus/dbus-marshal.c
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-01-30 04:20:44 +0000
committerHavoc Pennington <hp@redhat.com>2003-01-30 04:20:44 +0000
commit7ba714ad7fe8256edfaad7d9a0f09aeb9611ca44 (patch)
tree921d4ee8a780d5fe03f168405a5811287c10a926 /dbus/dbus-marshal.c
parent8fdd8915bd7424cdf90bf59a018838a1290ac0c4 (diff)
2003-01-30 Havoc Pennington <hp@pobox.com>
* dbus/dbus-message.c: use message->byte_order instead of DBUS_COMPILER_BYTE_ORDER throughout. (dbus_message_create_header): pad header to align the start of the body of the message to 8-byte boundary * dbus/dbus-marshal.h: make all the demarshalers take const DBusString arguments. * dbus/dbus-message.c (_dbus_message_loader_return_buffer): validate message args here, so we don't have to do slow validation later, and so we catch bad messages as they are incoming. Also add better checks on header_len and body_len. Also fill in message->byte_order * dbus/dbus-string.c (_dbus_string_validate_utf8): new (not implemented properly) (_dbus_string_validate_nul): new function to check all-nul * dbus/dbus-marshal.c (_dbus_marshal_get_field_end_pos): rename get_arg_end_pos and remove all validation (_dbus_marshal_validate_arg): actually do validation here.
Diffstat (limited to 'dbus/dbus-marshal.c')
-rw-r--r--dbus/dbus-marshal.c371
1 files changed, 314 insertions, 57 deletions
diff --git a/dbus/dbus-marshal.c b/dbus/dbus-marshal.c
index 9c17aabc..efdc7efb 100644
--- a/dbus/dbus-marshal.c
+++ b/dbus/dbus-marshal.c
@@ -73,7 +73,7 @@ _dbus_unpack_uint32 (int byte_order,
return DBUS_UINT32_FROM_LE (*(dbus_uint32_t*)data);
else
return DBUS_UINT32_FROM_BE (*(dbus_uint32_t*)data);
-}
+}
/**
* Unpacks a 32 bit signed integer from a data pointer
@@ -504,7 +504,7 @@ _dbus_marshal_string_array (DBusString *str,
* @returns the demarshaled double.
*/
double
-_dbus_demarshal_double (DBusString *str,
+_dbus_demarshal_double (const DBusString *str,
int byte_order,
int pos,
int *new_pos)
@@ -537,7 +537,7 @@ _dbus_demarshal_double (DBusString *str,
* @returns the demarshaled integer.
*/
dbus_int32_t
-_dbus_demarshal_int32 (DBusString *str,
+_dbus_demarshal_int32 (const DBusString *str,
int byte_order,
int pos,
int *new_pos)
@@ -564,7 +564,7 @@ _dbus_demarshal_int32 (DBusString *str,
* @returns the demarshaled integer.
*/
dbus_uint32_t
-_dbus_demarshal_uint32 (DBusString *str,
+_dbus_demarshal_uint32 (const DBusString *str,
int byte_order,
int pos,
int *new_pos)
@@ -598,7 +598,7 @@ _dbus_demarshal_uint32 (DBusString *str,
* @returns the demarshaled string.
*/
char *
-_dbus_demarshal_string (DBusString *str,
+_dbus_demarshal_string (const DBusString *str,
int byte_order,
int pos,
int *new_pos)
@@ -606,7 +606,7 @@ _dbus_demarshal_string (DBusString *str,
int len;
char *retval;
const char *data;
-
+
len = _dbus_demarshal_uint32 (str, byte_order, pos, &pos);
retval = dbus_malloc (len + 1);
@@ -641,7 +641,7 @@ _dbus_demarshal_string (DBusString *str,
* @returns the demarshaled data.
*/
unsigned char *
-_dbus_demarshal_byte_array (DBusString *str,
+_dbus_demarshal_byte_array (const DBusString *str,
int byte_order,
int pos,
int *new_pos,
@@ -685,7 +685,7 @@ _dbus_demarshal_byte_array (DBusString *str,
* @returns the demarshaled data.
*/
dbus_int32_t *
-_dbus_demarshal_int32_array (DBusString *str,
+_dbus_demarshal_int32_array (const DBusString *str,
int byte_order,
int pos,
int *new_pos,
@@ -724,7 +724,7 @@ _dbus_demarshal_int32_array (DBusString *str,
* @returns the demarshaled data.
*/
dbus_uint32_t *
-_dbus_demarshal_uint32_array (DBusString *str,
+_dbus_demarshal_uint32_array (const DBusString *str,
int byte_order,
int pos,
int *new_pos,
@@ -763,7 +763,7 @@ _dbus_demarshal_uint32_array (DBusString *str,
* @returns the demarshaled data.
*/
double *
-_dbus_demarshal_double_array (DBusString *str,
+_dbus_demarshal_double_array (const DBusString *str,
int byte_order,
int pos,
int *new_pos,
@@ -802,7 +802,7 @@ _dbus_demarshal_double_array (DBusString *str,
* @returns the demarshaled data.
*/
char **
-_dbus_demarshal_string_array (DBusString *str,
+_dbus_demarshal_string_array (const DBusString *str,
int byte_order,
int pos,
int *new_pos,
@@ -843,32 +843,24 @@ _dbus_demarshal_string_array (DBusString *str,
}
/**
- * Returns the position right after the end position
- * end position of a field. Validates the field
- * contents as required (e.g. ensures that
- * string fields have a valid length and
- * are nul-terminated).
+ * Returns the position right after the end of an argument. PERFORMS
+ * NO VALIDATION WHATSOEVER. The message must have been previously
+ * validated.
*
- * @todo security: audit the field validation code.
- *
- * @todo warns on invalid type in a message, but
- * probably the whole message needs to be dumped,
- * or we might even drop the connection due
- * to bad protocol. Needs better error handling.
- * Possible security issue.
+ * @todo handle DBUS_TYPE_NIL
*
* @param str a string
* @param byte_order the byte order to use
- * @param pos the pos where the field starts
+ * @param pos the pos where the arg starts
* @param end_pos pointer where the position right
* after the end position will follow
- * @returns TRUE if more data exists after the field
+ * @returns TRUE if more data exists after the arg
*/
dbus_bool_t
-_dbus_marshal_get_field_end_pos (DBusString *str,
- int byte_order,
- int pos,
- int *end_pos)
+_dbus_marshal_get_arg_end_pos (const DBusString *str,
+ int byte_order,
+ int pos,
+ int *end_pos)
{
const char *data;
@@ -906,21 +898,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
len = _dbus_demarshal_uint32 (str, byte_order, pos + 1, &pos);
*end_pos = pos + len + 1;
-
- if (*end_pos > _dbus_string_get_length (str))
- {
- _dbus_verbose ("string length outside length of the message\n");
- return FALSE;
- }
-
- if (_dbus_string_get_byte (str, pos+len) != '\0')
- {
- _dbus_verbose ("string field not nul-terminated\n");
- return FALSE;
- }
-
- break;
}
+ break;
case DBUS_TYPE_BYTE_ARRAY:
{
@@ -930,9 +909,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
len = _dbus_demarshal_uint32 (str, byte_order, pos + 1, &pos);
*end_pos = pos + len;
-
- break;
}
+ break;
case DBUS_TYPE_INT32_ARRAY:
{
@@ -943,9 +921,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
*end_pos = _DBUS_ALIGN_VALUE (new_pos, sizeof (dbus_int32_t))
+ (len * sizeof (dbus_int32_t));
-
- break;
}
+ break;
case DBUS_TYPE_UINT32_ARRAY:
{
@@ -956,9 +933,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
*end_pos = _DBUS_ALIGN_VALUE (new_pos, sizeof (dbus_uint32_t))
+ (len * sizeof (dbus_uint32_t));
-
- break;
}
+ break;
case DBUS_TYPE_DOUBLE_ARRAY:
{
@@ -969,9 +945,8 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
*end_pos = _DBUS_ALIGN_VALUE (new_pos, sizeof (double))
+ (len * sizeof (double));
-
- break;
}
+ break;
case DBUS_TYPE_STRING_ARRAY:
{
@@ -990,10 +965,262 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
}
*end_pos = pos;
- break;
- }
+ }
+ break;
+
+ default:
+ _dbus_warn ("Unknown message arg type %d\n", *data);
+ _dbus_assert_not_reached ("Unknown message argument type\n");
+ return FALSE;
+ }
+
+ if (*end_pos > _dbus_string_get_length (str))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Demarshals and validates a length; returns < 0 if the validation
+ * fails. The length is required to be small enough that
+ * len*sizeof(double) will not overflow, and small enough to fit in a
+ * signed integer.
+ *
+ * @param str the string
+ * @param byte_order the byte order
+ * @param pos the unaligned string position (snap to next aligned)
+ */
+static int
+demarshal_and_validate_len (const DBusString *str,
+ int byte_order,
+ int pos,
+ int *new_pos)
+{
+ int align_4 = _DBUS_ALIGN_VALUE (pos, 4);
+ unsigned int len;
+
+ if ((align_4 + 4) >= _dbus_string_get_length (str))
+ {
+ _dbus_verbose ("not enough room in message for array length\n");
+ return -1;
+ }
+
+ if (!_dbus_string_validate_nul (str, pos,
+ align_4 - pos))
+ {
+ _dbus_verbose ("array length alignment padding not initialized to nul\n");
+ return -1;
+ }
+
+ len = _dbus_demarshal_uint32 (str, byte_order, align_4, new_pos);
+
+ /* note that the len may be a number of doubles, so we need it to be
+ * at least SIZE_T_MAX / 8, but make it smaller just to keep things
+ * sane. We end up using ints for most sizes to avoid unsigned mess
+ * so limit to maximum 32-bit signed int divided by at least 8, more
+ * for a bit of paranoia margin. INT_MAX/32 is about 65 megabytes.
+ */
+#define MAX_ARRAY_LENGTH (((unsigned int)_DBUS_INT_MAX) / 32)
+ if (len > MAX_ARRAY_LENGTH)
+ {
+ _dbus_verbose ("array length %u exceeds maximum of %u\n",
+ len, MAX_ARRAY_LENGTH);
+ return -1;
+ }
+ else
+ return (int) len;
+}
+
+static dbus_bool_t
+validate_string (const DBusString *str,
+ int pos,
+ int len_without_nul,
+ int *end_pos)
+{
+ *end_pos = pos + len_without_nul + 1;
+
+ if (*end_pos > _dbus_string_get_length (str))
+ {
+ _dbus_verbose ("string length outside length of the message\n");
+ return FALSE;
+ }
+
+ if (_dbus_string_get_byte (str, pos + len_without_nul) != '\0')
+ {
+ _dbus_verbose ("string arg not nul-terminated\n");
+ return FALSE;
+ }
+
+ if (!_dbus_string_validate_utf8 (str, pos, len_without_nul))
+ {
+ _dbus_verbose ("string is not valid UTF-8\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Validates an argument, checking that it is well-formed, for example
+ * no ludicrous length fields, strings are nul-terminated, etc.
+ * Returns the end position of the argument in end_pos, and
+ * returns #TRUE if a valid arg begins at "pos"
+ *
+ * @todo security: need to audit this function.
+ *
+ * @todo handle DBUS_TYPE_NIL
+ *
+ * @param str a string
+ * @param byte_order the byte order to use
+ * @param pos the pos where the arg starts (offset of its typecode)
+ * @param end_pos pointer where the position right
+ * after the end position will follow
+ * @returns #TRUE if the arg is valid.
+ */
+dbus_bool_t
+_dbus_marshal_validate_arg (const DBusString *str,
+ int byte_order,
+ int pos,
+ int *end_pos)
+{
+ const char *data;
+
+ if (pos >= _dbus_string_get_length (str))
+ return FALSE;
+
+ _dbus_string_get_const_data_len (str, &data, pos, 1);
+
+ switch (*data)
+ {
+ case DBUS_TYPE_INVALID:
+ return FALSE;
+ break;
+
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ {
+ int align_4 = _DBUS_ALIGN_VALUE (pos + 1, 4);
+
+ if (!_dbus_string_validate_nul (str, pos + 1,
+ align_4 - pos - 1))
+ {
+ _dbus_verbose ("int32/uint32 alignment padding not initialized to nul\n");
+ return FALSE;
+ }
+
+ *end_pos = align_4 + 4;
+ }
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ {
+ int align_8 = _DBUS_ALIGN_VALUE (pos + 1, 8);
+
+ _dbus_verbose_bytes_of_string (str, pos, (align_8 + 8 - pos));
+
+ if (!_dbus_string_validate_nul (str, pos + 1,
+ align_8 - pos - 1))
+ {
+ _dbus_verbose ("double alignment padding not initialized to nul\n");
+ return FALSE;
+ }
+
+ *end_pos = align_8 + 8;
+ }
+ break;
+
+ case DBUS_TYPE_STRING:
+ {
+ int len;
+
+ /* Demarshal the length, which does NOT include
+ * nul termination
+ */
+ len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos);
+ if (len < 0)
+ return FALSE;
+
+ if (!validate_string (str, pos, len, end_pos))
+ return FALSE;
+ }
+ break;
+
+ case DBUS_TYPE_BYTE_ARRAY:
+ {
+ int len;
+
+ len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos);
+ if (len < 0)
+ return FALSE;
+
+ *end_pos = pos + len;
+ }
+ break;
+
+ case DBUS_TYPE_INT32_ARRAY:
+ case DBUS_TYPE_UINT32_ARRAY:
+ {
+ int len;
+
+ len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos);
+ if (len < 0)
+ return FALSE;
+
+ _dbus_assert (_DBUS_ALIGN_VALUE (pos, 4) == (unsigned int) pos);
+
+ *end_pos = pos + len * 4;
+ }
+ break;
+
+ case DBUS_TYPE_DOUBLE_ARRAY:
+ {
+ int len;
+ int align_8;
+
+ len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos);
+ if (len < 0)
+ return FALSE;
+
+ align_8 = _DBUS_ALIGN_VALUE (pos, 8);
+ if (!_dbus_string_validate_nul (str, pos,
+ align_8 - pos))
+ {
+ _dbus_verbose ("double array alignment padding not initialized to nul\n");
+ return FALSE;
+ }
+
+ *end_pos = align_8 + len * 8;
+ }
+ break;
+
+ case DBUS_TYPE_STRING_ARRAY:
+ {
+ int len;
+ int i;
+
+ len = demarshal_and_validate_len (str, byte_order, pos + 1, &pos);
+ if (len < 0)
+ return FALSE;
+
+ for (i = 0; i < len; i++)
+ {
+ int str_len;
+
+ str_len = demarshal_and_validate_len (str, byte_order,
+ pos, &pos);
+ if (str_len < 0)
+ return FALSE;
+
+ if (!validate_string (str, pos, str_len, &pos))
+ return FALSE;
+ }
+
+ *end_pos = pos;
+ }
+ break;
+
default:
- _dbus_warn ("Unknown message field type %d\n", *data);
+ _dbus_warn ("Unknown message arg type %d\n", *data);
return FALSE;
}
@@ -1003,6 +1230,7 @@ _dbus_marshal_get_field_end_pos (DBusString *str,
return TRUE;
}
+
/**
* If in verbose mode, print a block of binary data.
*
@@ -1018,6 +1246,8 @@ _dbus_verbose_bytes (const unsigned char *data,
int i;
const unsigned char *aligned;
+ _dbus_assert (len >= 0);
+
/* Print blanks on first row if appropriate */
aligned = _DBUS_ALIGN_ADDRESS (data, 4);
if (aligned > data)
@@ -1026,7 +1256,7 @@ _dbus_verbose_bytes (const unsigned char *data,
if (aligned != data)
{
- _dbus_verbose ("%5d\t%p: ", - (data - aligned), aligned);
+ _dbus_verbose ("%4d\t%p: ", - (data - aligned), aligned);
while (aligned != data)
{
_dbus_verbose (" ");
@@ -1040,7 +1270,7 @@ _dbus_verbose_bytes (const unsigned char *data,
{
if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
{
- _dbus_verbose ("%5d\t%p: ",
+ _dbus_verbose ("%4d\t%p: ",
i, &data[i]);
}
@@ -1056,9 +1286,16 @@ _dbus_verbose_bytes (const unsigned char *data,
if (_DBUS_ALIGN_ADDRESS (&data[i], 4) == &data[i])
{
if (i > 3)
- _dbus_verbose ("big: %d little: %d",
+ _dbus_verbose ("BE: %d LE: %d",
_dbus_unpack_uint32 (DBUS_BIG_ENDIAN, &data[i-4]),
_dbus_unpack_uint32 (DBUS_LITTLE_ENDIAN, &data[i-4]));
+
+ if (i > 7 &&
+ _DBUS_ALIGN_ADDRESS (&data[i], 8) == &data[i])
+ {
+ _dbus_verbose (" dbl: %g",
+ *(double*)&data[i-8]);
+ }
_dbus_verbose ("\n");
}
@@ -1080,7 +1317,27 @@ _dbus_verbose_bytes_of_string (const DBusString *str,
int len)
{
const char *d;
+ int real_len;
+
+ real_len = _dbus_string_get_length (str);
+ _dbus_assert (start >= 0);
+
+ if (start > real_len)
+ {
+ _dbus_verbose (" [%d,%d) is not inside string of length %d\n",
+ start, len, real_len);
+ return;
+ }
+
+ if ((start + len) > real_len)
+ {
+ _dbus_verbose (" [%d,%d) extends outside string of length %d\n",
+ start, len, real_len);
+ len = real_len - start;
+ }
+
+
_dbus_string_get_const_data_len (str, &d, start, len);
_dbus_verbose_bytes (d, len);