def execute(self, fun): if fun: log('%s', fun) if self.verify_pyargs: check_pyargs(fun) if self.only_on_python_code: # Only run the refcount checker on code that # includes <Python.h>: if not get_PyObject(): return # The refcount code is too buggy for now to be on by default: if self.verify_refcounting: if 0: # Profiled version: import cProfile prof_filename = '%s.%s.refcount-profile' % (gcc.get_dump_base_name(), fun.decl.name) cProfile.runctx('self._check_refcounts(fun)', globals(), locals(), filename=prof_filename) import pstats prof = pstats.Stats(prof_filename) prof.sort_stats('cumulative').print_stats(20) else: # Normal mode (without profiler): self._check_refcounts(fun)
def describe_trace(trace, report, annotator): """ Buffer up more details about the path through the function that leads to the error, using report.add_inform() """ awaiting_target = None for t in trace.transitions: log('transition: %s', t) srcloc = t.src.get_gcc_loc_or_none() if t.desc: if srcloc: report.add_inform(t.src.get_gcc_loc(report.fun), ('%s at: %s' % (t.desc, get_src_for_loc(srcloc)))) else: report.add_inform(t.src.get_gcc_loc(report.fun), '%s' % t.desc) if t.src.loc.bb != t.dest.loc.bb: # Tell the user where conditionals reach: destloc = t.dest.get_gcc_loc_or_none() if destloc: report.add_inform(destloc, 'reaching: %s' % get_src_for_loc(destloc)) if annotator: notes = annotator.get_notes(t) for note in notes: if note.loc and note.loc == srcloc: report.add_inform(note.loc, note.msg)
def describe_trace(trace, report, annotator): """ Buffer up more details about the path through the function that leads to the error, using report.add_inform() """ awaiting_target = None for t in trace.transitions: log('transition: %s', t) srcloc = t.src.get_gcc_loc_or_none() if t.desc: if srcloc: report.add_inform(t.src.get_gcc_loc(report.fun), ('%s at: %s' % (t.desc, get_src_for_loc(srcloc)))) else: report.add_inform(t.src.get_gcc_loc(report.fun), '%s' % t.desc) if t.src.stmtnode.bb != t.dest.stmtnode.bb: # Tell the user where conditionals reach: destloc = t.dest.get_gcc_loc_or_none() if destloc: report.add_inform( destloc, 'reaching: %s' % get_src_for_loc(destloc)) if annotator: notes = annotator.get_notes(t) for note in notes: if note.loc and note.loc == srcloc: report.add_inform(note.loc, note.msg)
def is_compatible(self, actual_type, actual_arg): # We should be encountering a function pointer of type: # int (fn)(PyObject *, T*) # The result type (next argument) should be a T* if not isinstance(actual_type, gcc.PointerType): return False signature = actual_type.dereference if not isinstance(signature, gcc.FunctionType): return False # Check return type: if signature.type != gcc.Type.int(): return False # Check argument types: if len(signature.argument_types) != 2: return False if not compatible_type(signature.argument_types[0], get_PyObject().pointer): return False if not isinstance(signature.argument_types[1], gcc.PointerType): return False # Write back to the ConverterResultType with the second arg: log('2nd argument of converter should be of type %s', signature.argument_types[1]) self.conv.result.type = signature.argument_types[1] self.actual_type = actual_type return True
def get_all_PyMethodDef_initializers(): """ Locate all initializers for PyMethodDef, returning a list of StructInitializer instances """ log('get_all_PyMethodDef_initializers') result = [] vars = gcc.get_variables() for var in vars: if isinstance(var.decl, gcc.VarDecl): if isinstance(var.decl.type, gcc.ArrayType): if str(var.decl.type.type) == 'struct PyMethodDef': if var.decl.initial: table = [] for idx, ctor in var.decl.initial.elements: #print idx, ctor si = PyMethodDefInitializer(ctor) table.append(si) # Warn about missing sentinel entry with # ml->ml_name == NULL ml_name = table[-1].char_ptr_field('ml_name') if 0: print('final ml_name: %r' % ml_name) if ml_name is not None: gcc.warning(table[-1].get_location(), 'missing NULL sentinel value at end of PyMethodDef table') result += table return result
def execute(self, fun): if fun: log('%s', fun) if self.verify_pyargs: check_pyargs(fun) if self.only_on_python_code: # Only run the refcount checker on code that # includes <Python.h>: if not get_PyObject(): return # The refcount code is too buggy for now to be on by default: if self.verify_refcounting: if 0: # Profiled version: import cProfile prof_filename = '%s.%s.refcount-profile' % ( gcc.get_dump_base_name(), fun.decl.name) cProfile.runctx('self._check_refcounts(fun)', globals(), locals(), filename=prof_filename) import pstats prof = pstats.Stats(prof_filename) prof.sort_stats('cumulative').print_stats(20) else: # Normal mode (without profiler): self._check_refcounts(fun)
def get_all_PyMethodDef_initializers(): """ Locate all initializers for PyMethodDef, returning a list of StructInitializer instances """ log('get_all_PyMethodDef_initializers') result = [] vars = gcc.get_variables() for var in vars: if isinstance(var.decl, gcc.VarDecl): if isinstance(var.decl.type, gcc.ArrayType): if str(var.decl.type.type) == 'struct PyMethodDef': if var.decl.initial: table = [] for idx, ctor in var.decl.initial.elements: #print idx, ctor si = PyMethodDefInitializer(ctor) table.append(si) # Warn about missing sentinel entry with # ml->ml_name == NULL ml_name = table[-1].char_ptr_field('ml_name') if 0: print('final ml_name: %r' % ml_name) if ml_name is not None: gcc.warning( table[-1].get_location(), 'missing NULL sentinel value at end of PyMethodDef table' ) result += table return result
def check_callsite(stmt, parser, funcname, format_idx, varargs_idx, with_size_t): log('got call at %s', stmt.loc) log(get_src_for_loc(stmt.loc)) # log('stmt: %r %s', (stmt, stmt)) # log('args: %r', stmt.args) # for arg in stmt.args: # # log(' arg: %s %r', (arg, arg)) # We expect the following args: # args[0]: PyObject *input_tuple # args[1]: char * format # args[2...]: output pointers if len(stmt.args) >= format_idx: fmt_string = get_format_string(stmt, format_idx) if fmt_string: log('fmt_string: %r', fmt_string) loc = stmt.loc # Figure out expected types, based on the format string... try: fmt = parser.from_string(fmt_string, with_size_t) except FormatStringWarning: err = sys.exc_info()[1] err.emit_as_warning(stmt.loc) return log('fmt: %r', fmt.args) exp_types = list(fmt.iter_exp_types()) log('exp_types: %r', exp_types) # ...then compare them against the actual types: varargs = stmt.args[varargs_idx:] # log('varargs: %r', varargs) if len(varargs) < len(exp_types): NotEnoughVars(funcname, fmt, varargs).emit_as_warning(loc) return if len(varargs) > len(exp_types): TooManyVars(funcname, fmt, varargs).emit_as_warning(loc) return for index, ((exp_arg, exp_type), vararg) in enumerate(zip(exp_types, varargs)): if not compatible_type( exp_type, vararg.type, actualarg=vararg): err = MismatchingType(funcname, fmt, index + varargs_idx + 1, exp_arg.code, exp_type, vararg) if hasattr(vararg, 'location'): loc = vararg.location else: loc = stmt.loc err.emit_as_warning(loc)
def check_callsite(stmt, parser, funcname, format_idx, varargs_idx, with_size_t): log('got call at %s', stmt.loc) log(get_src_for_loc(stmt.loc)) # log('stmt: %r %s', (stmt, stmt)) # log('args: %r', stmt.args) # for arg in stmt.args: # # log(' arg: %s %r', (arg, arg)) # We expect the following args: # args[0]: PyObject *input_tuple # args[1]: char * format # args[2...]: output pointers if len(stmt.args) >= format_idx: fmt_string = get_format_string(stmt, format_idx) if fmt_string: log('fmt_string: %r', fmt_string) loc = stmt.loc # Figure out expected types, based on the format string... try: fmt = parser.from_string(fmt_string, with_size_t) except FormatStringWarning: err = sys.exc_info()[1] err.emit_as_warning(stmt.loc) return log('fmt: %r', fmt.args) exp_types = list(fmt.iter_exp_types()) log('exp_types: %r', exp_types) # ...then compare them against the actual types: varargs = stmt.args[varargs_idx:] # log('varargs: %r', varargs) if len(varargs) < len(exp_types): NotEnoughVars(funcname, fmt, varargs).emit_as_warning(loc) return if len(varargs) > len(exp_types): TooManyVars(funcname, fmt, varargs).emit_as_warning(loc) return for index, ((exp_arg, exp_type), vararg) in enumerate(zip(exp_types, varargs)): if not compatible_type(exp_type, vararg.type, actualarg=vararg): err = MismatchingType(funcname, fmt, index + varargs_idx + 1, exp_arg.code, exp_type, vararg) if hasattr(vararg, 'location'): loc = vararg.location else: loc = stmt.loc err.emit_as_warning(loc)
def get_all_PyTypeObject_initializers(): """ Locate all initializers for PyTypeObject, returning a list of PyTypeObjectInitializer instances """ log('get_all_PyTypeObject_initializers') result = [] vars = gcc.get_variables() for var in vars: if isinstance(var.decl, gcc.VarDecl): if str(var.decl.type) == 'struct PyTypeObject': ctor = var.decl.initial if ctor: si = PyTypeObjectInitializer(ctor) result.append(si) return result
def compatible_type(exp_type, actual_type, actualarg=None): log('comparing exp_type: %s (%r) with actual_type: %s (%r)', exp_type, exp_type, actual_type, actual_type) log('type(exp_type): %r %s', type(exp_type), type(exp_type)) log('actualarg: %s (%r)', actualarg, actualarg) # Support exp_type being actually a tuple of expected types (we need this # for "S" and "U"): if isinstance(exp_type, tuple): for exp in exp_type: if compatible_type(exp, actual_type, actualarg): return True # Didn't match any of them: return False # Support the "O!" and "O&" converter codes: if isinstance(exp_type, AwkwardType): return exp_type.is_compatible(actual_type, actualarg) # Support the codes that can accept NULL: if isinstance(exp_type, NullPointer): if isinstance(actual_type, gcc.PointerType): if isinstance(actual_type.dereference, gcc.VoidType): # We have a (void*), double-check that it's actually NULL: if actualarg: if isinstance(actualarg, gcc.IntegerCst): if actualarg.constant == 0: # We have NULL: return True return False assert isinstance(exp_type, gcc.Type) or isinstance(exp_type, gcc.TypeDecl) assert isinstance(actual_type, gcc.Type) or isinstance(actual_type, gcc.TypeDecl) # Try direct comparison: if actual_type == exp_type: return True # Sometimes we get the typedef rather than the type, for both exp and # actual. Compare using the actual types, but report using the typedefs # so that we can report that e.g. # PyObject * * # was expected, rather than: # struct PyObject * * if isinstance(exp_type, gcc.TypeDecl): if compatible_type(exp_type.type, actual_type): return True if isinstance(actual_type, gcc.TypeDecl): if compatible_type(exp_type, actual_type.type): return True # Dereference for pointers (and ptrs to ptrs etc): if isinstance(actual_type, gcc.PointerType) and isinstance(exp_type, gcc.PointerType): if compatible_type(exp_type.dereference, actual_type.dereference): return True # Support (const char*) vs (char*) # Somewhat counter-intuitively, the APIs that expect a char* are those that # read the string data (Py_BuildValue); those that expect a const char* are # those that write back a const char* value (PyArg_ParseTuple) # # Hence it's OK to accept a (const char*) when a (char*) was expected: if str(exp_type) == 'char *': if str(actual_type) == 'const char *': return True # Don't be too fussy about typedefs to integer types # For instance: # typedef unsigned PY_LONG_LONG gdb_py_ulongest; # gives a different IntegerType instance to that of # gcc.Type.long_long().unsigned_equivalent # As long as the size, signedness etc are the same, let it go if isinstance(actual_type, gcc.IntegerType) and isinstance(exp_type, gcc.IntegerType): def compare_int_types(): for attr in ('precision', 'unsigned', 'const', 'volatile', 'restrict'): if getattr(actual_type, attr) != getattr(exp_type, attr): return False return True if compare_int_types(): return True # Support character arrays vs char*: if str(exp_type) == 'char *': if isinstance(actual_type, gcc.PointerType): if isinstance(actual_type.dereference, gcc.ArrayType): if actual_type.dereference.dereference == gcc.Type.char(): return True return False
def compatible_type(exp_type, actual_type, actualarg=None): log('comparing exp_type: %s (%r) with actual_type: %s (%r)', exp_type, exp_type, actual_type, actual_type) log('type(exp_type): %r %s', type(exp_type), type(exp_type)) log('actualarg: %s (%r)', actualarg, actualarg) # Support exp_type being actually a tuple of expected types (we need this # for "S" and "U"): if isinstance(exp_type, tuple): for exp in exp_type: if compatible_type(exp, actual_type, actualarg): return True # Didn't match any of them: return False # Support the "O!" and "O&" converter codes: if isinstance(exp_type, AwkwardType): return exp_type.is_compatible(actual_type, actualarg) # Support the codes that can accept NULL: if isinstance(exp_type, NullPointer): if isinstance(actual_type, gcc.PointerType): if isinstance(actual_type.dereference, gcc.VoidType): # We have a (void*), double-check that it's actually NULL: if actualarg: if isinstance(actualarg, gcc.IntegerCst): if actualarg.constant == 0: # We have NULL: return True return False assert isinstance(exp_type, gcc.Type) or isinstance(exp_type, gcc.TypeDecl) assert isinstance(actual_type, gcc.Type) or isinstance( actual_type, gcc.TypeDecl) # Try direct comparison: if actual_type == exp_type: return True # Sometimes we get the typedef rather than the type, for both exp and # actual. Compare using the actual types, but report using the typedefs # so that we can report that e.g. # PyObject * * # was expected, rather than: # struct PyObject * * if isinstance(exp_type, gcc.TypeDecl): if compatible_type(exp_type.type, actual_type): return True if isinstance(actual_type, gcc.TypeDecl): if compatible_type(exp_type, actual_type.type): return True # Dereference for pointers (and ptrs to ptrs etc): if isinstance(actual_type, gcc.PointerType) and isinstance( exp_type, gcc.PointerType): if compatible_type(exp_type.dereference, actual_type.dereference): return True # Support (const char*) vs (char*) # Somewhat counter-intuitively, the APIs that expect a char* are those that # read the string data (Py_BuildValue); those that expect a const char* are # those that write back a const char* value (PyArg_ParseTuple) # # Hence it's OK to accept a (const char*) when a (char*) was expected: if str(exp_type) == 'char *': if str(actual_type) == 'const char *': return True # Don't be too fussy about typedefs to integer types # For instance: # typedef unsigned PY_LONG_LONG gdb_py_ulongest; # gives a different IntegerType instance to that of # gcc.Type.long_long().unsigned_equivalent # As long as the size, signedness etc are the same, let it go if isinstance(actual_type, gcc.IntegerType) and isinstance( exp_type, gcc.IntegerType): def compare_int_types(): for attr in ('precision', 'unsigned', 'const', 'volatile', 'restrict'): if getattr(actual_type, attr) != getattr(exp_type, attr): return False return True if compare_int_types(): return True # Support character arrays vs char*: if str(exp_type) == 'char *': if isinstance(actual_type, gcc.PointerType): if isinstance(actual_type.dereference, gcc.ArrayType): if actual_type.dereference.dereference == gcc.Type.char(): return True return False