• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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