| # Copyright 2017 syzkaller project authors. All rights reserved. |
| # Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| ''' |
| This module contains container classes for holding struct, struct fields, and a global |
| namespace for struct objects obtained from multiple header files. |
| ''' |
| |
| import logging |
| |
| from headerlib.struct_walker import StructWalker |
| |
| |
| class StructRepr(object): |
| ''' |
| This class is a container for a single struct type. `fr_list` is a list of all items |
| inside the struct, along with type information. |
| ''' |
| |
| def __init__(self, struct_name, fr_list, loglvl=logging.INFO): |
| self.struct_name = struct_name |
| self.fr_list = fr_list |
| self.global_hierarchy = {} |
| self._setuplogging(loglvl) |
| |
| def __str__(self): |
| return self._output_syzkaller_fmt() |
| |
| def _setuplogging(self, loglvl): |
| self.logger = logging.getLogger(self.__class__.__name__) |
| formatter = logging.Formatter('DEBUG:%(name)s:%(message)s') |
| sh = logging.StreamHandler() |
| sh.setFormatter(formatter) |
| sh.setLevel(loglvl) |
| self.logger.addHandler(sh) |
| self.logger.setLevel(loglvl) |
| |
| def _output_syzkaller_fmt(self): |
| header = '%s {' % (self.struct_name) |
| body = self.get_syzkaller_field_body()[:-1] |
| footer = '}' |
| return '\n'.join([header, body, footer]) |
| |
| def get_syzkaller_field_body(self): |
| ''' |
| Returns the metadata description for a struct field in syzkaller format. |
| eg: "len intptr". |
| In cases where more than one syzkaller type maps to a native type, return |
| a string with possible syzkaller types seperated by '|'. |
| ''' |
| |
| def _get_syzkaller_type(native_type): |
| syzkaller_types = { |
| 'size_t' : 'len|fileoff|intN', |
| 'ssize_t' : 'len|intN', |
| 'unsigned int' : 'len|fileoff|int32', |
| 'int' : 'len|fileoff|flags|int32', |
| 'long' : 'len|fileoff|flags|intN', |
| 'unsigned long' : 'len|fileoff|flags|intN', |
| 'unsigned long long': 'len|fileoff|intN', |
| 'char*' : 'ptr[in|out, string]|ptr[in, filename]', |
| 'char**' : 'ptr[in, [ptr[in|out, string]]]', |
| 'void*' : 'ptr[in|out, string]|ptr[in|out, array]', |
| 'void (*)()' : 'vma', |
| 'uint64_t' : 'len|int64', |
| 'int64_t' : 'len|int64', |
| 'uint32_t' : 'len|int32', |
| 'int32_t' : 'len|int32', |
| 'uint16_t' : 'len|int16', |
| 'int16_t' : 'len|int16', |
| 'uint8_t' : 'len|int8', |
| 'int8_t' : 'len|int8', |
| } |
| if '[' in native_type and ']' in native_type: |
| return 'array' |
| |
| # If we have a pointer to a struct object |
| elif 'struct ' in native_type: |
| if '*' in native_type: |
| return 'ptr|buffer|array' |
| else: |
| return native_type.split(' ')[-1] |
| |
| elif 'enum ' in native_type: |
| return native_type.split(' ')[-1] |
| |
| # typedef types |
| return syzkaller_types.get(native_type, native_type) |
| |
| body = '' |
| rows = [] |
| for field in self.fr_list: |
| rows.append((field.field_identifier, _get_syzkaller_type(field.field_type), field.field_type)) |
| |
| maxcolwidth = lambda rows, x: max([len(row[x])+5 for row in rows]) |
| col1_width = maxcolwidth(rows, 0) |
| col2_width = maxcolwidth(rows, 1) |
| for row in rows: |
| body += ' '*10 + '%s%s#(%s)\n' % (row[0].ljust(col1_width), row[1].ljust(col2_width), row[2]) |
| |
| return body |
| |
| def get_fields(self): |
| ''' |
| Get a list of all fields in this struct. |
| ''' |
| return self.fr_list |
| |
| def set_global_hierarchy(self, global_hierarchy): |
| ''' |
| Set a reference to the global heirarchy of structs. This is useful when unrolling |
| structs. |
| ''' |
| self.global_hierarchy = global_hierarchy |
| |
| |
| class FieldRepr(object): |
| ''' |
| This class is a container for a single item in a struct. field_type refers to the |
| type of the item. field_identifier refers to the name/label of the item. field_extra |
| is any item specific metadata. In cases where the field_type refers to another struct |
| (whose items we are aware of), field_extra points to its StructRepr instance. This is |
| used for struct unrolling in cases where an instance of "struct B" is an item inside |
| "struct A". |
| ''' |
| |
| def __init__(self, field_type, field_identifier): |
| self._field_type = field_type |
| self._field_identifier = field_identifier |
| self._field_extra = None |
| |
| @property |
| def field_type(self): |
| '''Retrieve the field type.''' |
| return self._field_type |
| @field_type.setter |
| def field_type(self, field_type): |
| self._field_type = field_type |
| |
| @property |
| def field_identifier(self): |
| '''Retrieve the field identifier.''' |
| return self._field_identifier |
| @field_identifier.setter |
| def field_identifier(self, field_identifier): |
| self._field_identifier = field_identifier |
| |
| @property |
| def field_extra(self): |
| '''Retrieve any field specific metadata object.''' |
| return self._field_extra |
| @field_extra.setter |
| def field_extra(self, field_extra): |
| self._field_extra = field_extra |
| |
| |
| class GlobalHierarchy(dict): |
| ''' |
| This class is a global container for structs and their items across a list |
| of header files. Each struct is stored key'd by the struct name, and represented |
| by an instance of `StructRepr`. |
| ''' |
| |
| def __init__(self, filenames, loglvl=logging.INFO, |
| include_lines='', output_fmt=''): |
| super(GlobalHierarchy, self).__init__() |
| self.filenames = filenames |
| self.include_lines = include_lines |
| self.loglvl = loglvl |
| self._setuplogging() |
| if self.filenames: |
| self.load_header_files() |
| |
| def __str__(self): |
| return self._output_syzkaller_fmt() |
| |
| def _setuplogging(self): |
| self.logger = logging.getLogger(self.__class__.__name__) |
| formatter = logging.Formatter('DEBUG:%(name)s:%(message)s') |
| sh = logging.StreamHandler() |
| sh.setFormatter(formatter) |
| sh.setLevel(self.loglvl) |
| self.logger.addHandler(sh) |
| self.logger.setLevel(self.loglvl) |
| |
| @staticmethod |
| def _get_struct_name(struct_type): |
| return struct_type.split()[-1] |
| |
| def _output_syzkaller_fmt(self): |
| return '' |
| |
| def add_header_file(self, filename): |
| '''Add a header file to the list of headers we are about to parse.''' |
| self.filenames.append(filename) |
| |
| def load_header_files(self): |
| ''' |
| Parse the list of header files and generate StructRepr instances to represent each |
| struct object. Maintain a global view of all structs. |
| ''' |
| self.logger.debug('load_header_files : %s', str(self.filenames)) |
| struct_walker = StructWalker(filenames=self.filenames, include_lines=self.include_lines, |
| loglvl=self.loglvl) |
| local_hierarchy = struct_walker.generate_local_hierarchy() |
| |
| for struct_name in local_hierarchy: |
| fr_list = [FieldRepr(i[0], i[1]) for i in local_hierarchy[struct_name]] |
| sr = StructRepr(struct_name, fr_list, loglvl=self.loglvl) |
| sr.set_global_hierarchy(self) |
| self["struct %s" % (struct_name)] = sr |
| |
| for struct_name in self.keys(): |
| sr = self[struct_name] |
| for field in sr.get_fields(): |
| # If the item is a struct object, we link it against an |
| # instance of its corresponding `sr` |
| if field.field_type in self: |
| field.field_extra = self[field.field_type] |
| |
| def get_metadata_structs(self): |
| ''' |
| Generate metadata structs for all structs that this global namespace knows about. |
| ''' |
| metadata_structs = "" |
| for struct_name in sorted(self.keys()): |
| sr = self[struct_name] |
| metadata_structs += str(sr) + "\n" |
| return metadata_structs.strip() |