class step_state¶
Abstract base class for step visitors, as added with analysis.add_step_bottom_up_visitor(): subclass to define your own step visitors.
Step visitors are applied during CodeSonar’s traversal of the program CFGs (control flow graphs) in the bottom-up phase.
The step_state keeps track of program state as CodeSonar explores the program procedures. For a subclass to be used in a step visitor, it must declare any additional data members the step visitor requires to keep track of program state and define the various member methods to operate appropriately on this additional data. CodeSonar will then do bookkeeping for the visitor, applying the various methods as shown in the following table.
step_state … |
definition must… | applied by CodeSonar … |
|---|---|---|
step_state.copy() |
ensure your additional data members are copied correctly. | to initial instance I (see below) to set up initial state every time the step traversal enters a function, and to the current state every time the step traversal encounters a CFG branch. |
step_state.transition() |
perform any operations associated with a transition in the CFG, updating the additional data members as needed. | when step traversal transitions over a CFG edge. |
| destructor | clean up your additional data members. | when a step_state object is no longer needed. |
Once you have defined your step_state class, create an instance I of the class and add it to the analysis with a suitable analysis.add_step_bottom_up_visitor() invocation (step visitors can only be applied in the bottom-up phase). I must be appropriately initialized: CodeSonar will invoke I.copy() to create a fresh step_state (subclass) object every time the step traversal enters a function.
Shown below is the Python version of the sample plug-in from Part Two of the Plug-In API Tutorial.
# software is retained by CodeSecure, Inc.
#
# Assume you have a program that defines these procedures:
#
# void openParen(void){putchar( '(' );}
# void closeParen(void){putchar( ')' );}
# void openBracket(void){putchar( '[' );}
# void closeBracket(void){putchar( ']' );}
#
# This plugin will check that all strings output by this program
# (assuming these are the only calls to putchar) have properly
# balanced parenthesis and brackets.
#
# The check is intraprocedural. It is subject to false negatives in
# the presence of loops and conditions that cause resource limits to
# expire. It is subject to false positives in the presence of
# some interprocedural transfer of parenthesis ownership.
#
# Example:
# void foo(){ int i = rand();
# if( i ) openParen(); else openBracket();
# if( i ) closeParen(); else closeBracket();
# }
# This procedure can output the strings:
# '()' and '[]'
#
# These strings are well-formed:
# []
# ()
# []()([])
#
# These strings are not well-formed:
# [)
# (]
# [[]
# )
# [(])
import cs
missing_open_wc = cs.analysis.create_warningclass(
'Missing open',
'',
1.0,
cs.warningclass_flags.NONE,
cs.warning_significance.STYLE)
missing_close_wc = cs.analysis.create_warningclass(
'Missing close',
'',
2.0,
cs.warningclass_flags.NONE,
cs.warning_significance.STYLE)
mismatched_wc = cs.analysis.create_warningclass(
'Mismatched close',
'',
1.0,
cs.warningclass_flags.NONE,
cs.warning_significance.STYLE)
# A helper function. Given a point p and a string fname,
# return True if p is a call site where fname is called,
# False otherwise.
def point_calls_function(p, fname):
try:
pcallee = p.callee()
except cs.result as r:
if r != cs.result.ERROR_NOT_A_CALL_SITE:
raise
return False
if pcallee and pcallee.name()==fname:
return True
return False
class paren_state(cs.step_state):
# Define __init__ to set up the initial state
def __init__(self):
super(paren_state,self).__init__()
self.push_stack = []
# Make a copy of the stack when copying the state.
def copy(self):
rv = paren_state()
rv.push_stack = list(self.push_stack)
return rv
# The transition() method does most of the step visitor work.
def transition(self,
srcpt,
edgelabel,
destpt,
tosrc_xform,
edge_xform,
tosrc_path):
# First, check whether the source point at this CFG edge is a
# call-site to one of the functions we are interested in.
src_calls_open = [fname for fname in ['openParen', 'openBracket']
if point_calls_function(srcpt, fname)]
src_calls_close = [fname for fname in ['closeParen', 'closeBracket']
if point_calls_function(srcpt, fname)]
dest_calls_close = [fname for fname in ['closeParen', 'closeBracket']
if point_calls_function(destpt, fname)]
print('Testing 1, 2, 3... This will show up in the analysis log page.')
# If the source point is a call-site to openParen() or
# openBracket(), update the state by pushing the appropriate
# kind of parenthesis onto the stack.
if src_calls_open:
self.push_stack.append('(' if 'openParen' in src_calls_open else '[')
# If the source point is a call site to closeParen() or
# closeBracket(), update the state by popping the stack. No
# checking needs to be carried out here, as it was already
# carried out when traversing into srcpt (see below)
elif src_calls_close:
if len(self.push_stack)>0:
self.push_stack.pop()
# If the destination point is a call site to closeParen() or
# closeBracket(), check to make sure the corresponding '(' or
# ']' is on top of the stack and issue an appropriate warning
# if not.
if dest_calls_close:
if self.push_stack:
stacktop = self.push_stack[-1]
# closeParen without matching '(' on top of the stack
# -> issue a 'mismatched' warning.
if 'closeParen' in dest_calls_close and stacktop != '(':
mismatched_wc.report(tosrc_path)
# closeBracket without matching '[' on top of the stack
# -> issue a 'mismatched' warning.
elif 'closeBracket' in dest_calls_close and stacktop != '[':
mismatched_wc.report(tosrc_path)
# closeParen or closeBracket with empty stack (no top at all)
# -> issue a 'missing open' warning
else:
missing_open_wc.report(tosrc_path)
# Traversal is about to exit the function while there
# are outstanding unbalanced opening parentheses
# -> issue a 'missing close' warning
if (destpt.get_kind() == cs.point_kind.EXIT) and self.push_stack:
missing_close_wc.report(tosrc_path)
# We defined paren_state.__init__ to suitably initialize the state, so
# just pass a new instance to cs.analysis.add_step_bottom_up_visitor()
cs.analysis.add_step_bottom_up_visitor(paren_state())
step_state Details¶
-
class
cs.step_state¶ Abstract base class for step visitors, as added with
analysis.add_step_bottom_up_visitor(): subclass to define your own step visitors.-
__init__()¶ Constructor.
When you subclass
step_stateto create a step visitor class, provide a definition for this, making sure that it correctly handles any additional data members you have added.
-
__repr__()¶ Get a representation of a
step_stateobject that includes information useful for debugging.Return type: str Returns: The string representation.
-
__str__()¶ Get a simple string representation of a
step_stateobject.Return type: str Returns: The string representation.
-
copy()¶ Dynamically allocate and return a new copy of a
step_state.Return type: step_stateReturns: A new copy of the step state. CodeSonar will invoke this:
- On initial instance
Ito set up initial state every time the step traversal enters a function. - On the current state every time the step traversal encounters a CFG branch.
When you subclass
step_stateto create a step visitor class, provide a definition for this, making sure that it correctly handles any additional data members you have added.- On initial instance
-
transition(source, label, destination, since_entry, this_edge, path)¶ Perform any operations associated with a specified transition in the CFG, and update the
step_state.Parameters: - source (
point) – Thepointbeing transitioned from. - label (
edge_label) – The label on the edge betweensourceanddestination. - destination (
point) – Thepointbeing transitioned to. - since_entry (
step_xform) – The transformation the program has undergone between the function entry point anddestination. Do not attempt to use thisstep_xformobject afterstep_state.transition()has returned. - this_edge (
step_xform) – The transformation the program undergoes betweensourceanddestination. Do not attempt to use thisstep_xformobject afterstep_state.transition()has returned. - path (
step_path) – The full step path from the function entry point todestination.
Return type: NoneType
This will be invoked by CodeSonar every time the step traversal moves from
sourcetodestinationover the edge labeledlabel.When you subclass
step_stateto create a step visitor class, provide a definition for this, making sure that it correctly handles any additional data members you have added. (If you don’t, the step visitor won’t do anything!)- source (
-