AST API Tutorial: Python Version

This tutorial example uses the CodeSonar Plug-in API (Python) to a check for expressions of const type being cast to non-const types.

CodeSonar SaaS Note: If you want to use your own custom plug-ins with CodeSonar SaaS, contact CodeSecure support for assistance. The installation instructions provided in this page are not sufficient to make plug-ins available to SaaS analyses.

UnsafeCast_plugin.py

File UnsafeCast_plugin.py is reproduced below. Select any API function name to see the documentation for that function.

#      Copyright (c) 2023, an unpublished work by CodeSecure, Inc.
#                      ALL RIGHTS RESERVED
#
#      Copyright (c) 2017-2023, an unpublished work by GrammaTech, Inc.
#                      ALL RIGHTS RESERVED
#
#      This software is furnished under a license and may be used and
#      copied only in accordance with the terms of such license and the       
#      inclusion of the above copyright notice.  This software or any
#      other copies thereof may not be provided or otherwise made
#      available to any other person.  Title to and ownership of the
#      software is retained by CodeSecure, Inc.
#

# UnsafeCast_plugin.py
# 
# A small example plug-in that reports casts from const to non-const
# types.

# All Python plug-ins for CodeSonar must import the cs module.
import cs


# badcast is a new \warning class \ defined by this plug-in. It must be 
# created in the top level scope.
#
# We do not associate any \CodeSonar mnemonics \
# or \CWE identifiers  \ with the new class, as none are appropriate. 

badcast = cs.analysis.create_warningclass(
    'Cast from const to non-const',
    '',
    1,
    cs.warningclass_flags.WARNING_POINT_IS_START_POINT,
    cs.warning_significance.SECURITY )


# Set up the pattern in global scope.
try:
    pat = cs.ast_pattern('''
       (c:cast
          :2 (?e
            :type (c:pointer
              :pointed-to (?c :is-const #t)))
          :type (c:pointer
            :pointed-to (?k :is-const #f)))''')
except cs.ast_pattern_compilation_error as e:
    print(str(e))
    print('   - pattern: ', e.get_pattern())                   # cs.ast_pattern_compilation_error.get_pattern()


# Visitors should be at the finest granularity that is appropriate
# for the check: in this case, we are checking the AST associated
# with a point.

@cs.point_visitor                                             # \visitor decorator \
def check_cast(pt):
    # Retrieve the procedure that contains pt.    
    proc = pt.get_procedure()                                 # cs.point.get_procedure()

    # Check if the procedure was authored by the user.  If not, don't
    # produce warnings about this code, since the user probably isn't
    # interested about warnings internal to library models or
    # synthesized procedures.
    if proc.get_kind() != cs.procedure_kind.USER_DEFINED:     # cs.procedure.get_kind()
        return

    # Get the \normalized C/C++ AST\ associated with pt. If there isn't
    # one (for whatever reason), there is no point in proceeding.
    if pt.has_ast(cs.ast_family.C_NORMALIZED):                # cs.point.has_ast()
        ptast = pt.get_ast(cs.ast_family.C_NORMALIZED)        # cs.point.get_ast()
    else:
        print('point has no normalized C/C++ AST')
        return
       
    # An offending cast might be nested inside other expressions, so
    # we need to check every subtree of ptast against our pattern.
    if next((subast
             for subast in ptast.traverse()                   # cs.ast.traverse()
             if pat.match(subast)),                           # cs.ast_pattern.match()
            False):
        badcast.report(pt, 'unsafe cast')                     # cs.warningclass.report()
        return

Install

Install the plug-in:

Exercise

We have provided an extremely short example program to exercise the plug-in. You can also try building other programs into CodeSonar projects to see whether they contain these unsafe casts.

Clean Up

If you don't want to perform this check in future:

More on Plug-Ins

Additional sample plugins can be found at $CSONAR/codesonar/plugins/*.py where $CSONAR is your CodeSonar installation directory.