diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-10-30 15:08:02 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-10-30 20:42:59 +0100 |
commit | 4c98ff41cca3c83498c8f8b6f502fa56e4e31653 (patch) | |
tree | 7935c2d8132809eeb74c7cd655d9f87b560f8db3 /build-aux | |
parent | 7cce18820ef2142781c334d20e162d814695a409 (diff) | |
download | external_libqmi-4c98ff41cca3c83498c8f8b6f502fa56e4e31653.zip external_libqmi-4c98ff41cca3c83498c8f8b6f502fa56e4e31653.tar.gz external_libqmi-4c98ff41cca3c83498c8f8b6f502fa56e4e31653.tar.bz2 |
qmi-codegen: validate TLV before really reading it
Try to handle buggy firmware, or just make the library more robust, by
validating the read TLV before really reading it. If a TLV is not considered
valid, we just skip it for now.
E.g.: the "Detailed Service Status" TLV (0x21) in the "NAS Get Serving System"
message is supposed to be a sequence of 5 bytes, but some models (e.g. ZTE MF683)
end up sending only the first 4 bytes.
Diffstat (limited to 'build-aux')
-rw-r--r-- | build-aux/qmi-codegen/Field.py | 45 | ||||
-rw-r--r-- | build-aux/qmi-codegen/FieldResult.py | 26 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Message.py | 11 | ||||
-rw-r--r-- | build-aux/qmi-codegen/TypeFactory.py | 16 | ||||
-rw-r--r-- | build-aux/qmi-codegen/Variable.py | 7 | ||||
-rw-r--r-- | build-aux/qmi-codegen/VariableArray.py | 57 | ||||
-rw-r--r-- | build-aux/qmi-codegen/VariableInteger.py | 26 | ||||
-rw-r--r-- | build-aux/qmi-codegen/VariableSequence.py | 8 | ||||
-rw-r--r-- | build-aux/qmi-codegen/VariableString.py | 39 | ||||
-rw-r--r-- | build-aux/qmi-codegen/VariableStruct.py | 8 |
10 files changed, 220 insertions, 23 deletions
diff --git a/build-aux/qmi-codegen/Field.py b/build-aux/qmi-codegen/Field.py index 31005b3..5bf42bc 100644 --- a/build-aux/qmi-codegen/Field.py +++ b/build-aux/qmi-codegen/Field.py @@ -271,6 +271,7 @@ class Field: def emit_output_tlv_get(self, f, line_prefix): translations = { 'name' : self.name, 'container_underscore' : utils.build_underscore_name (self.prefix), + 'underscore' : utils.build_underscore_name (self.fullname), 'tlv_id' : self.id_enum_name, 'variable_name' : self.variable_name, 'lp' : line_prefix, @@ -283,7 +284,7 @@ class Field: '${lp}buffer = qmi_message_get_raw_tlv (message,\n' '${lp} ${tlv_id},\n' '${lp} &buffer_len);\n' - '${lp} if (buffer) {\n' + '${lp}if (buffer && ${underscore}_validate (buffer, buffer_len)) {\n' '${lp} self->${variable_name}_set = TRUE;\n' '\n') f.write(string.Template(template).substitute(translations)) @@ -317,15 +318,49 @@ class Field: """ Emit the method responsible for creating a printable representation of the TLV """ - def emit_tlv_get_printable(self, f): - if TypeFactory.is_get_printable_emitted(self.fullname): + def emit_tlv_helpers(self, f): + if TypeFactory.helpers_emitted(self.fullname): return - TypeFactory.set_get_printable_emitted(self.fullname) + TypeFactory.set_helpers_emitted(self.fullname) translations = { 'name' : self.name, 'tlv_id' : self.id_enum_name, 'underscore' : utils.build_underscore_name (self.fullname) } + + template = ( + '\n' + 'static gboolean\n' + '${underscore}_validate (\n' + ' const guint8 *buffer,\n' + ' guint16 buffer_len)\n' + '{\n' + ' guint expected_len = 0;\n' + '\n') + f.write(string.Template(template).substitute(translations)) + + # Now, read the size of the expected TLV + self.variable.emit_size_read(f, ' ', 'expected_len', 'buffer', 'buffer_len') + + template = ( + '\n' + ' if (buffer_len < expected_len) {\n' + ' g_warning ("Cannot read the \'${name}\' TLV: expected \'%u\' bytes, but only got \'%u\' bytes",\n' + ' expected_len, buffer_len);\n' + ' return FALSE;\n' + ' }\n' + '\n' + ' if (buffer_len > expected_len) {\n' + ' g_debug ("Reading the \'${name}\' TLV: expected \'%u\' bytes, but got \'%u\' bytes",\n' + ' expected_len, buffer_len);\n' + ' return TRUE;\n' + ' }\n' + '\n' + ' return TRUE;\n' + '}\n' + '\n') + f.write(string.Template(template).substitute(translations)) + template = ( '\n' 'static gchar *\n' @@ -339,7 +374,7 @@ class Field: ' buffer = qmi_message_get_raw_tlv (message,\n' ' ${tlv_id},\n' ' &buffer_len);\n' - ' if (buffer) {\n' + ' if (buffer && ${underscore}_validate (buffer, buffer_len)) {\n' ' GString *printable;\n' '\n' ' printable = g_string_new ("");\n') diff --git a/build-aux/qmi-codegen/FieldResult.py b/build-aux/qmi-codegen/FieldResult.py index cc298ee..6b7b91e 100644 --- a/build-aux/qmi-codegen/FieldResult.py +++ b/build-aux/qmi-codegen/FieldResult.py @@ -107,15 +107,35 @@ class FieldResult(Field): Emit the method responsible for getting a printable representation of this TLV field. """ - def emit_tlv_get_printable(self, f): - if TypeFactory.is_get_printable_emitted(self.fullname): + def emit_tlv_helpers(self, f): + if TypeFactory.helpers_emitted(self.fullname): return - TypeFactory.set_get_printable_emitted(self.fullname) + TypeFactory.set_helpers_emitted(self.fullname) translations = { 'name' : self.name, 'tlv_id' : self.id_enum_name, 'underscore' : utils.build_underscore_name (self.fullname) } + + template = ( + '\n' + 'static gboolean\n' + '${underscore}_validate (\n' + ' const guint8 *buffer,\n' + ' guint16 buffer_len)\n' + '{\n' + ' static const guint expected_len = 4;\n' + '\n' + ' if (buffer_len < expected_len) {\n' + ' g_warning ("Cannot read the \'${name}\' TLV: expected \'%u\' bytes, but only got \'%u\' bytes",\n' + ' expected_len, buffer_len);\n' + ' return FALSE;\n' + ' }\n' + '\n' + ' return TRUE;\n' + '}\n') + f.write(string.Template(template).substitute(translations)) + template = ( '\n' 'static gchar *\n' diff --git a/build-aux/qmi-codegen/Message.py b/build-aux/qmi-codegen/Message.py index 5b35cf7..04d775e 100644 --- a/build-aux/qmi-codegen/Message.py +++ b/build-aux/qmi-codegen/Message.py @@ -224,17 +224,17 @@ class Message: Emit method responsible for getting a printable representation of the whole request/response """ - def __emit_get_printable(self, hfile, cfile): + def __emit_helpers(self, hfile, cfile): need_tlv_printable = False if self.input is not None and self.input.fields is not None: need_tlv_printable = True for field in self.input.fields: - field.emit_tlv_get_printable(cfile) + field.emit_tlv_helpers(cfile) if self.output is not None and self.output.fields is not None: need_tlv_printable = True for field in self.output.fields: - field.emit_tlv_get_printable(cfile) + field.emit_tlv_helpers(cfile) translations = { 'name' : self.name, 'service' : self.service, @@ -394,12 +394,9 @@ class Message: hfile.write('\n/* --- Output -- */\n'); cfile.write('\n/* --- Output -- */\n'); self.output.emit(hfile, cfile) + self.__emit_helpers(hfile, cfile) self.__emit_response_or_indication_parser(hfile, cfile) - hfile.write('\n/* --- Printable -- */\n'); - cfile.write('\n/* --- Printable -- */\n'); - self.__emit_get_printable(hfile, cfile) - """ Emit the sections """ diff --git a/build-aux/qmi-codegen/TypeFactory.py b/build-aux/qmi-codegen/TypeFactory.py index da1a185..0e1e0f8 100644 --- a/build-aux/qmi-codegen/TypeFactory.py +++ b/build-aux/qmi-codegen/TypeFactory.py @@ -47,16 +47,16 @@ def set_type_emitted(type_name): """ -List to keep track of type-specific get_printable() methods already emitted to +List to keep track of type-specific helper methods already emitted to the source/header files. """ -emitted_get_printable = [] +emitted_helpers = [] """ -Checks whether a given type-specific get_printable() has already been emitted. +Checks whether a given type-specific helpers have already been emitted. """ -def is_get_printable_emitted(type_name): - for i in emitted_get_printable: +def helpers_emitted(type_name): + for i in emitted_helpers: if i == type_name: return True else: @@ -65,11 +65,11 @@ def is_get_printable_emitted(type_name): """ Sets the given type-specific get_printable() as already emitted. """ -def set_get_printable_emitted(type_name): - if is_get_printable_emitted(type_name): +def set_helpers_emitted(type_name): + if helpers_emitted(type_name): return False else: - emitted_get_printable.append(type_name) + emitted_helpers.append(type_name) return True diff --git a/build-aux/qmi-codegen/Variable.py b/build-aux/qmi-codegen/Variable.py index 3f3fcec..8e4bfc6 100644 --- a/build-aux/qmi-codegen/Variable.py +++ b/build-aux/qmi-codegen/Variable.py @@ -86,6 +86,13 @@ class Variable: """ + Emits the code involved in computing the size of the variable. + """ + def emit_size_read(self, f, line_prefix, variable_name, buffer_name, buffer_len): + pass + + + """ Emits the code to get the contents of the given variable as a printable string. """ def emit_get_printable(self, f, line_prefix, printable, buffer_name, buffer_len): diff --git a/build-aux/qmi-codegen/VariableArray.py b/build-aux/qmi-codegen/VariableArray.py index d582a6a..f2b122c 100644 --- a/build-aux/qmi-codegen/VariableArray.py +++ b/build-aux/qmi-codegen/VariableArray.py @@ -190,6 +190,63 @@ class VariableArray(Variable): """ + Emits the code involved in computing the size of the variable. + """ + def emit_size_read(self, f, line_prefix, variable_name, buffer_name, buffer_len): + common_var_prefix = utils.build_underscore_name(self.name) + translations = { 'lp' : line_prefix, + 'variable_name' : variable_name, + 'buffer_name' : buffer_name, + 'buffer_len' : buffer_len, + 'common_var_prefix' : common_var_prefix } + + template = ( + '${lp}{\n' + '${lp} guint ${common_var_prefix}_i;\n') + f.write(string.Template(template).substitute(translations)) + + if self.fixed_size: + translations['fixed_size'] = self.fixed_size + + template = ( + '${lp} guint16 ${common_var_prefix}_n_items = ${fixed_size};\n' + '\n') + f.write(string.Template(template).substitute(translations)) + else: + translations['array_size_element_format'] = self.array_size_element.public_format + if self.array_size_element.public_format == 'guint8': + translations['array_size_element_size'] = '1' + elif self.array_size_element.public_format == 'guint16': + translations['array_size_element_size'] = '2' + elif self.array_size_element.public_format == 'guint32': + translations['array_size_element_size'] = '4' + else: + translations['array_size_element_size'] = '0' + + template = ( + '${lp} ${array_size_element_format} ${common_var_prefix}_n_items;\n' + '${lp} const guint8 *${common_var_prefix}_aux_buffer = &${buffer_name}[${variable_name}];\n' + '${lp} guint16 ${common_var_prefix}_aux_buffer_len = ${buffer_len} - ${variable_name};\n' + '\n' + '${lp} ${variable_name} += ${array_size_element_size};\n' + '\n') + f.write(string.Template(template).substitute(translations)) + + self.array_size_element.emit_buffer_read(f, line_prefix + ' ', common_var_prefix + '_n_items', common_var_prefix + '_aux_buffer', common_var_prefix + '_aux_buffer_len') + + template = ( + '${lp} for (${common_var_prefix}_i = 0; ${common_var_prefix}_i < ${common_var_prefix}_n_items; ${common_var_prefix}_i++) {\n' + '\n') + f.write(string.Template(template).substitute(translations)) + + self.array_element.emit_size_read(f, line_prefix + ' ', variable_name, buffer_name, buffer_len) + + template = ( + '${lp} }\n' + '${lp}}\n') + f.write(string.Template(template).substitute(translations)) + + """ Writing an array to the raw byte buffer is just about providing a loop to write every array element one by one. """ diff --git a/build-aux/qmi-codegen/VariableInteger.py b/build-aux/qmi-codegen/VariableInteger.py index b891283..63d1d02 100644 --- a/build-aux/qmi-codegen/VariableInteger.py +++ b/build-aux/qmi-codegen/VariableInteger.py @@ -101,6 +101,32 @@ class VariableInteger(Variable): """ + Emits the code involved in computing the size of the variable. + """ + def emit_size_read(self, f, line_prefix, variable_name, buffer_name, buffer_len): + translations = { 'lp' : line_prefix, + 'len' : self.guint_sized_size, + 'variable_name' : variable_name } + template = '' + if self.format == 'guint-sized': + template += ( + '${lp}${variable_name} += ${len};\n') + elif self.private_format == 'guint8' or self.private_format == 'gint8': + template += ( + '${lp}${variable_name} += 1;\n') + elif self.private_format == 'guint16' or self.private_format == 'gint16': + template += ( + '${lp}${variable_name} += 2;\n') + elif self.private_format == 'guint32' or self.private_format == 'gint32': + template += ( + '${lp}${variable_name} += 4;\n') + elif self.private_format == 'guint64' or self.private_format == 'gint64': + template += ( + '${lp}${variable_name} += 8;\n') + f.write(string.Template(template).substitute(translations)) + + + """ Write a single integer to the raw byte buffer """ def emit_buffer_write(self, f, line_prefix, variable_name, buffer_name, buffer_len): diff --git a/build-aux/qmi-codegen/VariableSequence.py b/build-aux/qmi-codegen/VariableSequence.py index 07b3ba4..a20c6b7 100644 --- a/build-aux/qmi-codegen/VariableSequence.py +++ b/build-aux/qmi-codegen/VariableSequence.py @@ -81,6 +81,14 @@ class VariableSequence(Variable): """ + Emits the code involved in computing the size of the variable. + """ + def emit_size_read(self, f, line_prefix, variable_name, buffer_name, buffer_len): + for member in self.members: + member['object'].emit_size_read(f, line_prefix, variable_name, buffer_name, buffer_len) + + + """ Writing the contents of a sequence is just about writing each of the sequence fields one by one. """ diff --git a/build-aux/qmi-codegen/VariableString.py b/build-aux/qmi-codegen/VariableString.py index c6acbe0..db4a915 100644 --- a/build-aux/qmi-codegen/VariableString.py +++ b/build-aux/qmi-codegen/VariableString.py @@ -102,6 +102,45 @@ class VariableString(Variable): """ + Emits the code involved in computing the size of the variable. + """ + def emit_size_read(self, f, line_prefix, variable_name, buffer_name, buffer_len): + translations = { 'lp' : line_prefix, + 'variable_name' : variable_name, + 'buffer_name' : buffer_name, + 'buffer_len' : buffer_len } + + if self.is_fixed_size: + translations['fixed_size'] = self.fixed_size + template = ( + '${lp}${variable_name} += ${fixed_size};\n') + elif self.length_prefix_size == 0: + template = ( + '${lp}${variable_name} += ${buffer_len};\n') + elif self.length_prefix_size == 8: + template = ( + '${lp}{\n' + '${lp} guint8 size8;\n' + '${lp} const guint8 *aux_buffer = &${buffer_name}[${variable_name}];\n' + '${lp} guint16 aux_buffer_len = ${buffer_len} - ${variable_name};\n' + '\n' + '${lp} qmi_utils_read_guint8_from_buffer (&aux_buffer, &aux_buffer_len, &size8);\n' + '${lp} ${variable_name} = 1 + size8;\n' + '${lp}}\n') + elif self.length_prefix_size == 16: + template = ( + '${lp}{\n' + '${lp} guint16 size16;\n' + '${lp} const guint8 *aux_buffer = &${buffer_name}[${variable_name}];\n' + '${lp} guint16 aux_buffer_len = ${buffer_len} - ${variable_name};\n' + '\n' + '${lp} qmi_utils_read_guint16_from_buffer (&aux_buffer, &aux_buffer_len, QMI_ENDIAN_LITTLE, &size16);\n' + '${lp} ${variable_name} = 2 + size16;\n' + '${lp}}\n') + f.write(string.Template(template).substitute(translations)) + + + """ Write a string to the raw byte buffer. """ def emit_buffer_write(self, f, line_prefix, variable_name, buffer_name, buffer_len): diff --git a/build-aux/qmi-codegen/VariableStruct.py b/build-aux/qmi-codegen/VariableStruct.py index c22d9c2..522551e 100644 --- a/build-aux/qmi-codegen/VariableStruct.py +++ b/build-aux/qmi-codegen/VariableStruct.py @@ -110,6 +110,14 @@ class VariableStruct(Variable): """ + Emits the code involved in computing the size of the variable. + """ + def emit_size_read(self, f, line_prefix, variable_name, buffer_name, buffer_len): + for member in self.members: + member['object'].emit_size_read(f, line_prefix, variable_name, buffer_name, buffer_len) + + + """ Writing the contents of a struct is just about writing each of the struct fields one by one. """ |