AST API Tutorial: C++ Version

This tutorial example uses the CodeSonar C++ API to construct a check for expressions of const type being cast to non-const types using an AST Pattern.

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.cpp

File UnsafeCast_plugin.cpp is reproduced below.

  //
//      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.cpp
// 
// A small example plugin that reports casts from const to non-const
// types.


// Must precede #include "csonar_api.hpp".
#define CS_CPP_IMPL

// Include the entire CodeSonar C++ API.
#include "csonar_api.hpp"  

// for std::cout
#include <iostream>

// badcast is a new \warning class \ defined by this plug-in. It must be created in the
// top level scope or in cs_plug_main().
static cs::warningclass badcast = cs::analysis::create_warningclass(
       "Cast from const to non-const",
       "",
       1,
       cs::warningclass_flags::WARNING_POINT_IS_START_POINT,
       cs::warning_significance::SECURITY);



// 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.

class check_cast: public cs::visitor<cs::point>
{
    cs::ast_pattern *pattern;

public:

    // The \AST pattern-matching functionality \ in the C++ API uses ast_pattern 
    // objects, with the pattern specification passed as an
    // argument to the ast_pattern constructor. Note
    // that the pattern specification uses variables instead of
    // concrete values for the classes of the ASTs in the respective
    // :pointed-to fields: this check is not concerned with the exact
    // types involved in the cast, only in whether or not they are
    // consts.
    // The pattern can be broken down as follows:
    // (c:cast                 a normalized AST representing a cast...
    //   :2 (?e                               ...of something...
    //     :type (c:pointer                   ...that is a pointer...
    //       :pointed-to (?c :is-const #t)))  ...to some const type
    //   :type (c:pointer      and it's being cast to a pointer...
    //     :pointed-to (?k :is-const #f)))    ...to some non-const type
    //
    // Set up the pattern in the check_cast default constructor.
    check_cast(void): cs::visitor<cs::point>(){
      try {
        pattern = new 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)))"
        );
      }
      catch ( const cs::ast_pattern_compilation_error &e ) {
        std::cout << "*** problem compiling ast pattern" << std::endl;
        std::cout << "***  - pattern: " << e.get_pattern() << std::endl;
        std::cout << "***  - error: " << e.as_string() << std::endl;
        return;
      }
    }


    // Clean up the pattern in the check_cast destructor.
    ~check_cast(void){
        delete pattern;
    }


    // CodeSonar will invoke this function on every program point in
    // the program.
    void operator()(cs::point pt)
    {
        try{
          // Retrieve the procedure that contains pt. 
          cs::procedure 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()
            std::cout << "point has no normalized C/C++ AST" << std::endl;
            return;
          }
          cs::ast ptast = pt.get_ast(cs::ast_family::C_NORMALIZED);         // cs::point::get_ast()

          // An offending cast might be nested inside other
          // expressions, so we need to check every subtree of ptast
          // against our pattern.
          for ( cs::ast_iterator it=ptast.traverse(); !it.at_end(); ++it){  // cs::ast::traverse()
            if (this->pattern->match(*it)){                                 // cs::ast_pattern::match()
              badcast.report(pt, "unsafe cast");                            // cs::warningclass::report()
              return;
            }
          } 
        }
        catch( const cs::result &r ) {
            std::cout << "*** check_cast_visitor exception: " << r << std::endl;
        }
    }
};

// Create an instance of check_cast, and add it as a point visitor.
// Visitors must be added either in the top-level scope or in
// cs_plug_main().
static char dummy = (cs::analysis::add_point_visitor(new check_cast()), 0); 

   
// Every C++ plug-in for CodeSonar must define this function. 
static void cs_plug_main(void){}


// All CodeSonar C++ plug-ins must finish with a line of this form (at
// the top level). Note that the argument UnsafeCast_plugin to
// CS_DEFINE_PLUGIN corresponds to the name of the compiled plug-in
// (UnsafeCast_plugin.dll, UnsafeCast_plugin.so, or
// UnsafeCast_plugin.bundle, depending on your system).
CS_DEFINE_PLUGIN(UnsafeCast_plugin)

  

Build

Build the plug-in into a library.

See Writing C++ Plug-Ins For CodeSonar for further compilation details and troubleshooting hints.

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: