/** * Revalidates the fields cache * * @param header the header */ static void _dbus_header_cache_revalidate (DBusHeader *header) { DBusTypeReader array; DBusTypeReader reader; int i; i = 0; while (i <= DBUS_HEADER_FIELD_LAST) { header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; ++i; } _dbus_type_reader_init (&reader, header->byte_order, &_dbus_header_signature_str, FIELDS_ARRAY_SIGNATURE_OFFSET, &header->data, FIELDS_ARRAY_LENGTH_OFFSET); _dbus_type_reader_recurse (&reader, &array); while (_dbus_type_reader_get_current_type (&array) != DBUS_TYPE_INVALID) { DBusTypeReader sub; DBusTypeReader variant; unsigned char field_code; _dbus_type_reader_recurse (&array, &sub); _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); _dbus_type_reader_read_basic (&sub, &field_code); /* Unknown fields should be ignored */ if (field_code > DBUS_HEADER_FIELD_LAST) goto next_field; _dbus_type_reader_next (&sub); _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_VARIANT); _dbus_type_reader_recurse (&sub, &variant); _dbus_header_cache_one (header, field_code, &variant); next_field: _dbus_type_reader_next (&array); } }
static dbus_bool_t write_args_for_direction (DBusString *xml, const char *signature, dbus_bool_t in) { DBusTypeReader typereader; DBusString sigstr; int current_type; _dbus_string_init_const (&sigstr, signature); _dbus_type_reader_init_types_only (&typereader, &sigstr, 0); while ((current_type = _dbus_type_reader_get_current_type (&typereader)) != DBUS_TYPE_INVALID) { const DBusString *subsig; int start, len; _dbus_type_reader_get_signature (&typereader, &subsig, &start, &len); if (!_dbus_string_append_printf (xml, " <arg direction=\"%s\" type=\"", in ? "in" : "out")) goto oom; if (!_dbus_string_append_len (xml, _dbus_string_get_const_data (subsig) + start, len)) goto oom; if (!_dbus_string_append (xml, "\"/>\n")) goto oom; _dbus_type_reader_next (&typereader); } return TRUE; oom: return FALSE; }
/** * Sets a struct of { byte, variant } with the given basic type. * * @param reader the reader (should be iterating over the array pointing at the field to set) * @param type the type of the value * @param value the value as for _dbus_marshal_set_basic() * @param realign_root where to realign from * @returns #FALSE if no memory */ static dbus_bool_t set_basic_field (DBusTypeReader *reader, int field, int type, const void *value, const DBusTypeReader *realign_root) { DBusTypeReader sub; DBusTypeReader variant; _dbus_type_reader_recurse (reader, &sub); _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); #ifndef DBUS_DISABLE_ASSERT { unsigned char v_BYTE; _dbus_type_reader_read_basic (&sub, &v_BYTE); _dbus_assert (((int) v_BYTE) == field); } #endif if (!_dbus_type_reader_next (&sub)) _dbus_assert_not_reached ("no variant field?"); _dbus_type_reader_recurse (&sub, &variant); _dbus_assert (_dbus_type_reader_get_current_type (&variant) == type); if (!_dbus_type_reader_set_basic (&variant, value, realign_root)) return FALSE; return TRUE; }
static dbus_bool_t find_field_for_modification (DBusHeader *header, int field, DBusTypeReader *reader, DBusTypeReader *realign_root) { dbus_bool_t retval; retval = FALSE; _dbus_type_reader_init (realign_root, header->byte_order, &_dbus_header_signature_str, FIELDS_ARRAY_SIGNATURE_OFFSET, &header->data, FIELDS_ARRAY_LENGTH_OFFSET); _dbus_type_reader_recurse (realign_root, reader); while (_dbus_type_reader_get_current_type (reader) != DBUS_TYPE_INVALID) { DBusTypeReader sub; unsigned char field_code; _dbus_type_reader_recurse (reader, &sub); _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_BYTE); _dbus_type_reader_read_basic (&sub, &field_code); if (field_code == (unsigned) field) { _dbus_assert (_dbus_type_reader_get_current_type (reader) == DBUS_TYPE_STRUCT); retval = TRUE; goto done; } _dbus_type_reader_next (reader); } done: return retval; }
static DBusValidity validate_body_helper (DBusTypeReader *reader, int byte_order, dbus_bool_t walk_reader_to_end, const unsigned char *p, const unsigned char *end, const unsigned char **new_p) { int current_type; while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) { const unsigned char *a; int alignment; #if 0 _dbus_verbose (" validating value of type %s type reader %p type_pos %d p %p end %p %d remain\n", _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, (int) (end - p)); #endif /* Guarantee that p has one byte to look at */ if (p == end) return DBUS_INVALID_NOT_ENOUGH_DATA; switch (current_type) { case DBUS_TYPE_BYTE: ++p; break; case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: alignment = _dbus_type_get_alignment (current_type); a = _DBUS_ALIGN_ADDRESS (p, alignment); if (a >= end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } if (current_type == DBUS_TYPE_BOOLEAN) { dbus_uint32_t v = _dbus_unpack_uint32 (byte_order, p); if (!(v == 0 || v == 1)) return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE; } p += alignment; break; case DBUS_TYPE_ARRAY: case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: { dbus_uint32_t claimed_len; a = _DBUS_ALIGN_ADDRESS (p, 4); if (a + 4 > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } claimed_len = _dbus_unpack_uint32 (byte_order, p); p += 4; /* p may now be == end */ _dbus_assert (p <= end); if (current_type == DBUS_TYPE_ARRAY) { int array_elem_type = _dbus_type_reader_get_element_type (reader); alignment = _dbus_type_get_alignment (array_elem_type); p = _DBUS_ALIGN_ADDRESS (p, alignment); } if (claimed_len > (unsigned long) (end - p)) return DBUS_INVALID_LENGTH_OUT_OF_BOUNDS; if (current_type == DBUS_TYPE_OBJECT_PATH) { DBusString str; _dbus_string_init_const_len (&str, p, claimed_len); if (!_dbus_validate_path (&str, 0, _dbus_string_get_length (&str))) return DBUS_INVALID_BAD_PATH; p += claimed_len; } else if (current_type == DBUS_TYPE_STRING) { DBusString str; _dbus_string_init_const_len (&str, p, claimed_len); if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str))) return DBUS_INVALID_BAD_UTF8_IN_STRING; p += claimed_len; } else if (current_type == DBUS_TYPE_ARRAY && claimed_len > 0) { DBusTypeReader sub; DBusValidity validity; const unsigned char *array_end; if (claimed_len > DBUS_MAXIMUM_ARRAY_LENGTH) return DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM; /* Remember that the reader is types only, so we can't * use it to iterate over elements. It stays the same * for all elements. */ _dbus_type_reader_recurse (reader, &sub); array_end = p + claimed_len; while (p < array_end) { /* FIXME we are calling a function per array element! very bad * need if (dbus_type_is_fixed(elem_type)) here to just skip * big blocks of ints/bytes/etc. */ validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p); if (validity != DBUS_VALID) return validity; } if (p != array_end) return DBUS_INVALID_ARRAY_LENGTH_INCORRECT; } /* check nul termination */ if (current_type != DBUS_TYPE_ARRAY) { if (p == end) return DBUS_INVALID_NOT_ENOUGH_DATA; if (*p != '\0') return DBUS_INVALID_STRING_MISSING_NUL; ++p; } } break; case DBUS_TYPE_SIGNATURE: { dbus_uint32_t claimed_len; DBusString str; DBusValidity validity; claimed_len = *p; ++p; /* 1 is for nul termination */ if (claimed_len + 1 > (unsigned long) (end - p)) return DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS; _dbus_string_init_const_len (&str, p, claimed_len); validity = _dbus_validate_signature_with_reason (&str, 0, _dbus_string_get_length (&str)); if (validity != DBUS_VALID) return validity; p += claimed_len; _dbus_assert (p < end); if (*p != DBUS_TYPE_INVALID) return DBUS_INVALID_SIGNATURE_MISSING_NUL; ++p; _dbus_verbose ("p = %p end = %p claimed_len %u\n", p, end, claimed_len); } break; case DBUS_TYPE_VARIANT: { /* 1 byte sig len, sig typecodes, align to * contained-type-boundary, values. */ /* In addition to normal signature validation, we need to be sure * the signature contains only a single (possibly container) type. */ dbus_uint32_t claimed_len; DBusString sig; DBusTypeReader sub; DBusValidity validity; int contained_alignment; int contained_type; DBusValidity reason; claimed_len = *p; ++p; /* + 1 for nul */ if (claimed_len + 1 > (unsigned long) (end - p)) return DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS; _dbus_string_init_const_len (&sig, p, claimed_len); reason = _dbus_validate_signature_with_reason (&sig, 0, _dbus_string_get_length (&sig)); if (!(reason == DBUS_VALID)) { if (reason == DBUS_VALIDITY_UNKNOWN_OOM_ERROR) return reason; else return DBUS_INVALID_VARIANT_SIGNATURE_BAD; } p += claimed_len; if (*p != DBUS_TYPE_INVALID) return DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL; ++p; contained_type = _dbus_first_type_in_signature (&sig, 0); if (contained_type == DBUS_TYPE_INVALID) return DBUS_INVALID_VARIANT_SIGNATURE_EMPTY; contained_alignment = _dbus_type_get_alignment (contained_type); a = _DBUS_ALIGN_ADDRESS (p, contained_alignment); if (a > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } _dbus_type_reader_init_types_only (&sub, &sig, 0); _dbus_assert (_dbus_type_reader_get_current_type (&sub) != DBUS_TYPE_INVALID); validity = validate_body_helper (&sub, byte_order, FALSE, p, end, &p); if (validity != DBUS_VALID) return validity; if (_dbus_type_reader_next (&sub)) return DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES; _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID); } break; case DBUS_TYPE_DICT_ENTRY: case DBUS_TYPE_STRUCT: { DBusTypeReader sub; DBusValidity validity; a = _DBUS_ALIGN_ADDRESS (p, 8); if (a > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } _dbus_type_reader_recurse (reader, &sub); validity = validate_body_helper (&sub, byte_order, TRUE, p, end, &p); if (validity != DBUS_VALID) return validity; } break; default: _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); break; } #if 0 _dbus_verbose (" validated value of type %s type reader %p type_pos %d p %p end %p %d remain\n", _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, (int) (end - p)); #endif if (p > end) { _dbus_verbose ("not enough data!!! p = %p end = %p end-p = %d\n", p, end, (int) (end - p)); return DBUS_INVALID_NOT_ENOUGH_DATA; } if (walk_reader_to_end) _dbus_type_reader_next (reader); else break; } if (new_p) *new_p = p; return DBUS_VALID; }
/** * Creates a message header from potentially-untrusted data. The * return value is #TRUE if there was enough memory and the data was * valid. If it returns #TRUE, the header will be created. If it * returns #FALSE and *validity == #DBUS_VALIDITY_UNKNOWN_OOM_ERROR, * then there wasn't enough memory. If it returns #FALSE * and *validity != #DBUS_VALIDITY_UNKNOWN_OOM_ERROR then the data was * invalid. * * The byte_order, fields_array_len, and body_len args should be from * _dbus_header_have_message_untrusted(). Validation performed in * _dbus_header_have_message_untrusted() is assumed to have been * already done. * * @param header the header (must be initialized) * @param mode whether to do validation * @param validity return location for invalidity reason * @param byte_order byte order from header * @param fields_array_len claimed length of fields array * @param body_len claimed length of body * @param header_len claimed length of header * @param str a string * @param start start of header, 8-aligned * @param len length of string to look at * @returns #FALSE if no memory or data was invalid, #TRUE otherwise */ dbus_bool_t _dbus_header_load (DBusHeader *header, DBusValidationMode mode, DBusValidity *validity, int byte_order, int fields_array_len, int header_len, int body_len, const DBusString *str, int start, int len) { int leftover; DBusValidity v; DBusTypeReader reader; DBusTypeReader array_reader; unsigned char v_byte; dbus_uint32_t v_uint32; dbus_uint32_t serial; int padding_start; int padding_len; int i; _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8)); _dbus_assert (header_len <= len); _dbus_assert (_dbus_string_get_length (&header->data) == 0); if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0)) { _dbus_verbose ("Failed to copy buffer into new header\n"); *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR; return FALSE; } if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) { leftover = len - header_len - body_len - start; } else { v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0, byte_order, &leftover, str, start, len); if (v != DBUS_VALID) { *validity = v; goto invalid; } } _dbus_assert (leftover < len); padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len); padding_start = start + FIRST_FIELD_OFFSET + fields_array_len; _dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8)); _dbus_assert (start + header_len == padding_start + padding_len); if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) { if (!_dbus_string_validate_nul (str, padding_start, padding_len)) { *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; goto invalid; } } header->padding = padding_len; if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY) { *validity = DBUS_VALID; return TRUE; } /* We now know the data is well-formed, but we have to check that * it's valid. */ _dbus_type_reader_init (&reader, byte_order, &_dbus_header_signature_str, 0, str, start); /* BYTE ORDER */ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET); _dbus_type_reader_read_basic (&reader, &v_byte); _dbus_type_reader_next (&reader); _dbus_assert (v_byte == byte_order); header->byte_order = byte_order; /* MESSAGE TYPE */ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET); _dbus_type_reader_read_basic (&reader, &v_byte); _dbus_type_reader_next (&reader); /* unknown message types are supposed to be ignored, so only validation here is * that it isn't invalid */ if (v_byte == DBUS_MESSAGE_TYPE_INVALID) { *validity = DBUS_INVALID_BAD_MESSAGE_TYPE; goto invalid; } /* FLAGS */ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET); _dbus_type_reader_read_basic (&reader, &v_byte); _dbus_type_reader_next (&reader); /* unknown flags should be ignored */ /* PROTOCOL VERSION */ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET); _dbus_type_reader_read_basic (&reader, &v_byte); _dbus_type_reader_next (&reader); if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION) { *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION; goto invalid; } /* BODY LENGTH */ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET); _dbus_type_reader_read_basic (&reader, &v_uint32); _dbus_type_reader_next (&reader); _dbus_assert (body_len == (signed) v_uint32); /* SERIAL */ _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET); _dbus_type_reader_read_basic (&reader, &serial); _dbus_type_reader_next (&reader); if (serial == 0) { *validity = DBUS_INVALID_BAD_SERIAL; goto invalid; } _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY); _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET); _dbus_type_reader_recurse (&reader, &array_reader); while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID) { DBusTypeReader struct_reader; DBusTypeReader variant_reader; unsigned char field_code; _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT); _dbus_type_reader_recurse (&array_reader, &struct_reader); _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE); _dbus_type_reader_read_basic (&struct_reader, &field_code); _dbus_type_reader_next (&struct_reader); if (field_code == DBUS_HEADER_FIELD_INVALID) { _dbus_verbose ("invalid header field code\n"); *validity = DBUS_INVALID_HEADER_FIELD_CODE; goto invalid; } if (field_code > DBUS_HEADER_FIELD_LAST) { _dbus_verbose ("unknown header field code %d, skipping\n", field_code); goto next_field; } _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT); _dbus_type_reader_recurse (&struct_reader, &variant_reader); v = load_and_validate_field (header, field_code, &variant_reader); if (v != DBUS_VALID) { _dbus_verbose ("Field %d was invalid\n", field_code); *validity = v; goto invalid; } next_field: _dbus_type_reader_next (&array_reader); } /* Anything we didn't fill in is now known not to exist */ i = 0; while (i <= DBUS_HEADER_FIELD_LAST) { if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN) header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT; ++i; } v = check_mandatory_fields (header); if (v != DBUS_VALID) { _dbus_verbose ("Mandatory fields were missing, code %d\n", v); *validity = v; goto invalid; } *validity = DBUS_VALID; return TRUE; invalid: _dbus_string_set_length (&header->data, 0); return FALSE; }
static void byteswap_body_helper (DBusTypeReader *reader, dbus_bool_t walk_reader_to_end, int old_byte_order, int new_byte_order, unsigned char *p, unsigned char **new_p) { int current_type; while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) { switch (current_type) { case DBUS_TYPE_BYTE: ++p; break; case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: { p = _DBUS_ALIGN_ADDRESS (p, 2); *((dbus_uint16_t*)p) = DBUS_UINT16_SWAP_LE_BE (*((dbus_uint16_t*)p)); p += 2; } break; case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: { p = _DBUS_ALIGN_ADDRESS (p, 4); *((dbus_uint32_t*)p) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)p)); p += 4; } break; case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: { p = _DBUS_ALIGN_ADDRESS (p, 8); #ifdef DBUS_HAVE_INT64 *((dbus_uint64_t*)p) = DBUS_UINT64_SWAP_LE_BE (*((dbus_uint64_t*)p)); #else _dbus_swap_array (p, 1, 8); #endif p += 8; } break; case DBUS_TYPE_ARRAY: case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: { dbus_uint32_t array_len; p = _DBUS_ALIGN_ADDRESS (p, 4); array_len = _dbus_unpack_uint32 (old_byte_order, p); *((dbus_uint32_t*)p) = DBUS_UINT32_SWAP_LE_BE (*((dbus_uint32_t*)p)); p += 4; if (current_type == DBUS_TYPE_ARRAY) { int elem_type; int alignment; elem_type = _dbus_type_reader_get_element_type (reader); alignment = _dbus_type_get_alignment (elem_type); _dbus_assert ((array_len / alignment) < DBUS_MAXIMUM_ARRAY_LENGTH); p = _DBUS_ALIGN_ADDRESS (p, alignment); if (dbus_type_is_fixed (elem_type)) { if (alignment > 1) _dbus_swap_array (p, array_len / alignment, alignment); p += array_len; } else { DBusTypeReader sub; const unsigned char *array_end; array_end = p + array_len; _dbus_type_reader_recurse (reader, &sub); while (p < array_end) { byteswap_body_helper (&sub, FALSE, old_byte_order, new_byte_order, p, &p); } } } else { _dbus_assert (current_type == DBUS_TYPE_STRING || current_type == DBUS_TYPE_OBJECT_PATH); p += (array_len + 1); /* + 1 for nul */ } } break; case DBUS_TYPE_SIGNATURE: { dbus_uint32_t sig_len; sig_len = *p; p += (sig_len + 2); /* +2 for len and nul */ } break; case DBUS_TYPE_VARIANT: { /* 1 byte sig len, sig typecodes, align to * contained-type-boundary, values. */ dbus_uint32_t sig_len; DBusString sig; DBusTypeReader sub; int contained_alignment; sig_len = *p; ++p; _dbus_string_init_const_len (&sig, p, sig_len); p += (sig_len + 1); /* 1 for nul */ contained_alignment = _dbus_type_get_alignment (_dbus_first_type_in_signature (&sig, 0)); p = _DBUS_ALIGN_ADDRESS (p, contained_alignment); _dbus_type_reader_init_types_only (&sub, &sig, 0); byteswap_body_helper (&sub, FALSE, old_byte_order, new_byte_order, p, &p); } break; case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: { DBusTypeReader sub; p = _DBUS_ALIGN_ADDRESS (p, 8); _dbus_type_reader_recurse (reader, &sub); byteswap_body_helper (&sub, TRUE, old_byte_order, new_byte_order, p, &p); } break; case DBUS_TYPE_UNIX_FD: /* fds can only be passed on a local machine, so byte order must always match */ _dbus_assert_not_reached("attempted to byteswap unix fds which makes no sense"); break; default: _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); break; } if (walk_reader_to_end) _dbus_type_reader_next (reader); else break; } if (new_p) *new_p = p; }
/* note: this function is also used to validate the header's values, * since the header is a valid body with a particular signature. */ static DBusValidity validate_body_helper (DBusTypeReader *reader, int byte_order, dbus_bool_t walk_reader_to_end, int total_depth, const unsigned char *p, const unsigned char *end, const unsigned char **new_p) { int current_type; /* The spec allows arrays and structs to each nest 32, for total * nesting of 2*32. We want to impose the same limit on "dynamic" * value nesting (not visible in the signature) which is introduced * by DBUS_TYPE_VARIANT. */ if (total_depth > (DBUS_MAXIMUM_TYPE_RECURSION_DEPTH * 2)) { return DBUS_INVALID_NESTED_TOO_DEEPLY; } while ((current_type = _dbus_type_reader_get_current_type (reader)) != DBUS_TYPE_INVALID) { const unsigned char *a; int alignment; #if 0 _dbus_verbose (" validating value of type %s type reader %p type_pos %d p %p end %p %d remain\n", _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, (int) (end - p)); #endif /* Guarantee that p has one byte to look at */ if (p == end) return DBUS_INVALID_NOT_ENOUGH_DATA; switch (current_type) { case DBUS_TYPE_BYTE: ++p; break; case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: case DBUS_TYPE_UNIX_FD: case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: case DBUS_TYPE_DOUBLE: alignment = _dbus_type_get_alignment (current_type); a = _DBUS_ALIGN_ADDRESS (p, alignment); if (a >= end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } if (current_type == DBUS_TYPE_BOOLEAN) { dbus_uint32_t v = _dbus_unpack_uint32 (byte_order, p); if (!(v == 0 || v == 1)) return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE; } p += alignment; break; case DBUS_TYPE_ARRAY: case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: { dbus_uint32_t claimed_len; a = _DBUS_ALIGN_ADDRESS (p, 4); if (a + 4 > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } claimed_len = _dbus_unpack_uint32 (byte_order, p); p += 4; /* p may now be == end */ _dbus_assert (p <= end); if (current_type == DBUS_TYPE_ARRAY) { int array_elem_type = _dbus_type_reader_get_element_type (reader); if (!dbus_type_is_valid (array_elem_type)) { return DBUS_INVALID_UNKNOWN_TYPECODE; } alignment = _dbus_type_get_alignment (array_elem_type); a = _DBUS_ALIGN_ADDRESS (p, alignment); /* a may now be == end */ if (a > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } } if (claimed_len > (unsigned long) (end - p)) return DBUS_INVALID_LENGTH_OUT_OF_BOUNDS; if (current_type == DBUS_TYPE_OBJECT_PATH) { DBusString str; _dbus_string_init_const_len (&str, (const char *) p, claimed_len); if (!_dbus_validate_path (&str, 0, _dbus_string_get_length (&str))) return DBUS_INVALID_BAD_PATH; p += claimed_len; } else if (current_type == DBUS_TYPE_STRING) { DBusString str; _dbus_string_init_const_len (&str, (const char *) p, claimed_len); if (!_dbus_string_validate_utf8 (&str, 0, _dbus_string_get_length (&str))) return DBUS_INVALID_BAD_UTF8_IN_STRING; p += claimed_len; } else if (current_type == DBUS_TYPE_ARRAY && claimed_len > 0) { DBusTypeReader sub; DBusValidity validity; const unsigned char *array_end; int array_elem_type; if (claimed_len > DBUS_MAXIMUM_ARRAY_LENGTH) return DBUS_INVALID_ARRAY_LENGTH_EXCEEDS_MAXIMUM; /* Remember that the reader is types only, so we can't * use it to iterate over elements. It stays the same * for all elements. */ _dbus_type_reader_recurse (reader, &sub); array_end = p + claimed_len; array_elem_type = _dbus_type_reader_get_element_type (reader); /* avoid recursive call to validate_body_helper if this is an array * of fixed-size elements */ if (dbus_type_is_fixed (array_elem_type)) { /* bools need to be handled differently, because they can * have an invalid value */ if (array_elem_type == DBUS_TYPE_BOOLEAN) { dbus_uint32_t v; alignment = _dbus_type_get_alignment (array_elem_type); while (p < array_end) { v = _dbus_unpack_uint32 (byte_order, p); if (!(v == 0 || v == 1)) return DBUS_INVALID_BOOLEAN_NOT_ZERO_OR_ONE; p += alignment; } } else { p = array_end; } } else { while (p < array_end) { validity = validate_body_helper (&sub, byte_order, FALSE, total_depth + 1, p, end, &p); if (validity != DBUS_VALID) return validity; } } if (p != array_end) return DBUS_INVALID_ARRAY_LENGTH_INCORRECT; } /* check nul termination */ if (current_type != DBUS_TYPE_ARRAY) { if (p == end) return DBUS_INVALID_NOT_ENOUGH_DATA; if (*p != '\0') return DBUS_INVALID_STRING_MISSING_NUL; ++p; } } break; case DBUS_TYPE_SIGNATURE: { dbus_uint32_t claimed_len; DBusString str; DBusValidity validity; claimed_len = *p; ++p; /* 1 is for nul termination */ if (claimed_len + 1 > (unsigned long) (end - p)) return DBUS_INVALID_SIGNATURE_LENGTH_OUT_OF_BOUNDS; _dbus_string_init_const_len (&str, (const char *) p, claimed_len); validity = _dbus_validate_signature_with_reason (&str, 0, _dbus_string_get_length (&str)); if (validity != DBUS_VALID) return validity; p += claimed_len; _dbus_assert (p < end); if (*p != DBUS_TYPE_INVALID) return DBUS_INVALID_SIGNATURE_MISSING_NUL; ++p; _dbus_verbose ("p = %p end = %p claimed_len %u\n", p, end, claimed_len); } break; case DBUS_TYPE_VARIANT: { /* 1 byte sig len, sig typecodes, align to * contained-type-boundary, values. */ /* In addition to normal signature validation, we need to be sure * the signature contains only a single (possibly container) type. */ dbus_uint32_t claimed_len; DBusString sig; DBusTypeReader sub; DBusValidity validity; int contained_alignment; int contained_type; DBusValidity reason; claimed_len = *p; ++p; /* + 1 for nul */ if (claimed_len + 1 > (unsigned long) (end - p)) return DBUS_INVALID_VARIANT_SIGNATURE_LENGTH_OUT_OF_BOUNDS; _dbus_string_init_const_len (&sig, (const char *) p, claimed_len); reason = _dbus_validate_signature_with_reason (&sig, 0, _dbus_string_get_length (&sig)); if (!(reason == DBUS_VALID)) { if (reason == DBUS_VALIDITY_UNKNOWN_OOM_ERROR) return reason; else return DBUS_INVALID_VARIANT_SIGNATURE_BAD; } p += claimed_len; if (*p != DBUS_TYPE_INVALID) return DBUS_INVALID_VARIANT_SIGNATURE_MISSING_NUL; ++p; contained_type = _dbus_first_type_in_signature (&sig, 0); if (contained_type == DBUS_TYPE_INVALID) return DBUS_INVALID_VARIANT_SIGNATURE_EMPTY; contained_alignment = _dbus_type_get_alignment (contained_type); a = _DBUS_ALIGN_ADDRESS (p, contained_alignment); if (a > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } _dbus_type_reader_init_types_only (&sub, &sig, 0); _dbus_assert (_dbus_type_reader_get_current_type (&sub) != DBUS_TYPE_INVALID); validity = validate_body_helper (&sub, byte_order, FALSE, total_depth + 1, p, end, &p); if (validity != DBUS_VALID) return validity; if (_dbus_type_reader_next (&sub)) return DBUS_INVALID_VARIANT_SIGNATURE_SPECIFIES_MULTIPLE_VALUES; _dbus_assert (_dbus_type_reader_get_current_type (&sub) == DBUS_TYPE_INVALID); } break; case DBUS_TYPE_DICT_ENTRY: case DBUS_TYPE_STRUCT: { DBusTypeReader sub; DBusValidity validity; a = _DBUS_ALIGN_ADDRESS (p, 8); if (a > end) return DBUS_INVALID_NOT_ENOUGH_DATA; while (p != a) { if (*p != '\0') return DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL; ++p; } _dbus_type_reader_recurse (reader, &sub); validity = validate_body_helper (&sub, byte_order, TRUE, total_depth + 1, p, end, &p); if (validity != DBUS_VALID) return validity; } break; default: _dbus_assert_not_reached ("invalid typecode in supposedly-validated signature"); break; } #if 0 _dbus_verbose (" validated value of type %s type reader %p type_pos %d p %p end %p %d remain\n", _dbus_type_to_string (current_type), reader, reader->type_pos, p, end, (int) (end - p)); #endif if (p > end) { _dbus_verbose ("not enough data!!! p = %p end = %p end-p = %d\n", p, end, (int) (end - p)); return DBUS_INVALID_NOT_ENOUGH_DATA; } if (walk_reader_to_end) _dbus_type_reader_next (reader); else break; } if (new_p) *new_p = p; return DBUS_VALID; }