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''' 5This module contains container classes for holding struct, struct fields, and a global 6namespace for struct objects obtained from multiple header files. 7''' 8 9import logging 10 11from headerlib.struct_walker import StructWalker 12 13 14class 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 heirarchy of structs. This is useful when unrolling 112 structs. 113 ''' 114 self.global_hierarchy = global_hierarchy 115 116 117class 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 157class 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 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() 230