github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/syz-headerparser/headerlib/container.py (about)

     1  # Copyright 2017 syzkaller project authors. All rights reserved.
     2  # Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  '''
     5  This module contains container classes for holding struct, struct fields, and a global
     6  namespace for struct objects obtained from multiple header files.
     7  '''
     8  
     9  import logging
    10  
    11  from headerlib.struct_walker import StructWalker
    12  
    13  
    14  class StructRepr(object):
    15      '''
    16      This class is a container for a single struct type. `fr_list` is a list of all items
    17      inside the struct, along with type information.
    18      '''
    19  
    20      def __init__(self, struct_name, fr_list, loglvl=logging.INFO):
    21          self.struct_name = struct_name
    22          self.fr_list = fr_list
    23          self.global_hierarchy = {}
    24          self._setuplogging(loglvl)
    25  
    26      def __str__(self):
    27          return self._output_syzkaller_fmt()
    28  
    29      def _setuplogging(self, loglvl):
    30          self.logger = logging.getLogger(self.__class__.__name__)
    31          formatter = logging.Formatter('DEBUG:%(name)s:%(message)s')
    32          sh = logging.StreamHandler()
    33          sh.setFormatter(formatter)
    34          sh.setLevel(loglvl)
    35          self.logger.addHandler(sh)
    36          self.logger.setLevel(loglvl)
    37  
    38      def _output_syzkaller_fmt(self):
    39          header = '%s {' % (self.struct_name)
    40          body = self.get_syzkaller_field_body()[:-1]
    41          footer = '}'
    42          return '\n'.join([header, body, footer])
    43  
    44      def get_syzkaller_field_body(self):
    45          '''
    46          Returns the metadata description for a struct field in syzkaller format.
    47          eg: "len    intptr".
    48          In cases where more than one syzkaller type maps to a native type, return
    49          a string with possible syzkaller types seperated by '|'.
    50          '''
    51  
    52          def _get_syzkaller_type(native_type):
    53              syzkaller_types = {
    54                  'size_t'            : 'len|fileoff|intN',
    55                  'ssize_t'           : 'len|intN',
    56                  'unsigned int'      : 'len|fileoff|int32',
    57                  'int'               : 'len|fileoff|flags|int32',
    58                  'long'              : 'len|fileoff|flags|intN',
    59                  'unsigned long'     : 'len|fileoff|flags|intN',
    60                  'unsigned long long': 'len|fileoff|intN',
    61                  'char*'             : 'ptr[in|out, string]|ptr[in, filename]',
    62                  'char**'            : 'ptr[in, [ptr[in|out, string]]]',
    63                  'void*'             : 'ptr[in|out, string]|ptr[in|out, array]',
    64                  'void (*)()'        : 'vma',
    65                  'uint64_t'          : 'len|int64',
    66                  'int64_t'           : 'len|int64',
    67                  'uint32_t'          : 'len|int32',
    68                  'int32_t'           : 'len|int32',
    69                  'uint16_t'          : 'len|int16',
    70                  'int16_t'           : 'len|int16',
    71                  'uint8_t'           : 'len|int8',
    72                  'int8_t'            : 'len|int8',
    73                  }
    74              if '[' in native_type and ']' in native_type:
    75                  return 'array'
    76  
    77              # If we have a pointer to a struct object
    78              elif 'struct ' in native_type:
    79                  if '*' in native_type:
    80                      return 'ptr|buffer|array'
    81                  else:
    82                      return native_type.split(' ')[-1]
    83  
    84              elif 'enum ' in native_type:
    85                  return native_type.split(' ')[-1]
    86  
    87              # typedef types
    88              return syzkaller_types.get(native_type, native_type)
    89  
    90          body = ''
    91          rows = []
    92          for field in self.fr_list:
    93              rows.append((field.field_identifier, _get_syzkaller_type(field.field_type), field.field_type))
    94  
    95          maxcolwidth = lambda rows, x: max([len(row[x])+5 for row in rows])
    96          col1_width = maxcolwidth(rows, 0)
    97          col2_width = maxcolwidth(rows, 1)
    98          for row in rows:
    99              body += ' '*10 + '%s%s#(%s)\n' % (row[0].ljust(col1_width), row[1].ljust(col2_width), row[2])
   100  
   101          return body
   102  
   103      def get_fields(self):
   104          '''
   105          Get a list of all fields in this struct.
   106          '''
   107          return self.fr_list
   108  
   109      def set_global_hierarchy(self, global_hierarchy):
   110          '''
   111          Set a reference to the global hierarchy of structs. This is useful when unrolling
   112          structs.
   113          '''
   114          self.global_hierarchy = global_hierarchy
   115  
   116  
   117  class FieldRepr(object):
   118      '''
   119      This class is a container for a single item in a struct. field_type refers to the
   120      type of the item. field_identifier refers to the name/label of the item. field_extra
   121      is any item specific metadata. In cases where the field_type refers to another struct
   122      (whose items we are aware of), field_extra points to its StructRepr instance. This is
   123      used for struct unrolling in cases where an instance of "struct B" is an item inside
   124      "struct A".
   125      '''
   126  
   127      def __init__(self, field_type, field_identifier):
   128          self._field_type = field_type
   129          self._field_identifier = field_identifier
   130          self._field_extra = None
   131  
   132      @property
   133      def field_type(self):
   134          '''Retrieve the field type.'''
   135          return self._field_type
   136      @field_type.setter
   137      def field_type(self, field_type):
   138          self._field_type = field_type
   139  
   140      @property
   141      def field_identifier(self):
   142          '''Retrieve the field identifier.'''
   143          return self._field_identifier
   144      @field_identifier.setter
   145      def field_identifier(self, field_identifier):
   146          self._field_identifier = field_identifier
   147  
   148      @property
   149      def field_extra(self):
   150          '''Retrieve any field specific metadata object.'''
   151          return self._field_extra
   152      @field_extra.setter
   153      def field_extra(self, field_extra):
   154          self._field_extra = field_extra
   155  
   156  
   157  class GlobalHierarchy(dict):
   158      '''
   159      This class is a global container for structs and their items across a list
   160      of header files. Each struct is stored key'd by the struct name, and represented
   161      by an instance of `StructRepr`.
   162      '''
   163  
   164      def __init__(self, filenames, loglvl=logging.INFO,
   165                   include_lines='', output_fmt=''):
   166          super(GlobalHierarchy, self).__init__()
   167          self.filenames = filenames
   168          self.include_lines = include_lines
   169          self.loglvl = loglvl
   170          self._setuplogging()
   171          if self.filenames:
   172              self.load_header_files()
   173  
   174      def __str__(self):
   175          return self._output_syzkaller_fmt()
   176  
   177      def _setuplogging(self):
   178          self.logger = logging.getLogger(self.__class__.__name__)
   179          formatter = logging.Formatter('DEBUG:%(name)s:%(message)s')
   180          sh = logging.StreamHandler()
   181          sh.setFormatter(formatter)
   182          sh.setLevel(self.loglvl)
   183          self.logger.addHandler(sh)
   184          self.logger.setLevel(self.loglvl)
   185  
   186      @staticmethod
   187      def _get_struct_name(struct_type):
   188          return struct_type.split()[-1]
   189  
   190      def _output_syzkaller_fmt(self):
   191          return ''
   192  
   193      def add_header_file(self, filename):
   194          '''Add a header file to the list of headers we are about to parse.'''
   195          self.filenames.append(filename)
   196  
   197      def load_header_files(self):
   198          '''
   199          Parse the list of header files and generate StructRepr instances to represent each
   200          struct object. Maintain a global view of all structs.
   201          '''
   202          self.logger.debug('load_header_files : %s', str(self.filenames))
   203          struct_walker = StructWalker(filenames=self.filenames, include_lines=self.include_lines,
   204                                       loglvl=self.loglvl)
   205          local_hierarchy = struct_walker.generate_local_hierarchy()
   206  
   207          for struct_name in local_hierarchy:
   208              fr_list = [FieldRepr(i[0], i[1]) for i in local_hierarchy[struct_name]]
   209              sr = StructRepr(struct_name, fr_list, loglvl=self.loglvl)
   210              sr.set_global_hierarchy(self)
   211              self["struct %s" % (struct_name)] = sr
   212  
   213          for struct_name in list(self.keys()):
   214              sr = self[struct_name]
   215              for field in sr.get_fields():
   216                  # If the item is a struct object, we link it against an
   217                  # instance of its corresponding `sr`
   218                  if field.field_type in self:
   219                      field.field_extra = self[field.field_type]
   220  
   221      def get_metadata_structs(self):
   222          '''
   223          Generate metadata structs for all structs that this global namespace knows about.
   224          '''
   225          metadata_structs = ""
   226          for struct_name in sorted(self.keys()):
   227              sr = self[struct_name]
   228              metadata_structs += str(sr) + "\n"
   229          return metadata_structs.strip()