Plug-In Tutorial: C Version

This tutorial illustrates the use of the CodeSonar Plug-in API (C) to construct custom plug-ins for CodeSonar.

Note: Depending on the hub configuration, you may be prompted for hub user account credentials to authenticate and authorize codesonar build and codesonar analyze commands. See Hub Authentication: Authenticated codesonar Subcommands for more information.

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.



Preliminaries

It is important to build your plug-in with the same pointer width as that of your CodeSonar installation. Otherwise, the plug-in build will fail and report errors in cs_basic_types.h.

Part One

The plug-in for Part One implements a check for variable names containing upper case characters. There are three important elements in the plug-in:

UCvar_plugin.c

Parts of file UCvar_plugin.c are reproduced below.

  
/*
 *      Copyright (c) 2023, an unpublished work by CodeSecure, Inc.
 *                      ALL RIGHTS RESERVED
 *
 *      Copyright (c) 2007-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.
 */

/* UCvar_plugin.c
 *
 * a small example plugin that reports declarations for variables
 * whose names contain upper case characters
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>


/* All C plug-ins for CodeSonar must include this file. */

#include "csonar_plugin.h"

/* This plug-in accesses various parts of the internal representation
 * (IR), so the relevant API headers must be included. */

#include "cs_pdg_vertex.h"
#include "cs_abs_loc.h"
#include "cs_pdg.h"
#include "cs_utility.h"
#include "cs_source_files.h"

/* upvar is a new \warning class \ defined by this plug-in. It must be created in
 * a setup visitor. */

static cs_warningclass_t upvar;


/* helper function for check_var_case
 *
 * returns 1 if instr contains an upper case character
 * (or a bad-valued character, although this should never happen)
 * 0 otherwise
 */
static int has_upper_char(cs_string instr, cs_size_t sz)
{
    cs_size_t i;
    for (i=0; i<sz; i++)
    {
        if (isupper((unsigned char) instr[i])) return 1;
    }
    return 0;
}


/* Visitors should be at the finest granularity that is appropriate
 * for the check. This plug-in is checking a property of variable
 * names, so we define a visitor that operates on variable ABS_LOCs. */

static void check_var_case(cs_abs_loc sym, void *ctx)
{
    cs_string varname;
    cs_size_t namesize,ns;
    char * buf;
    cs_abs_loc_kind varkind;

    /* We are only interested in user-defined variables. */
    if ( cs_abs_loc_get_kind(sym, &varkind) != CS_SUCCESS ) return;
    if ( varkind != cs_abs_loc_kind_user) return;
    
    /* Retrieving the variable name is a three-step process.
     *
     * 1. Call cs_abs_loc_name() with NULL and 0 as the second and
     * third parameters, respectively, in order to set namesize to the
     * length of the name (+1). This call should return CS_TRUNCATED,
     * since a variable name should not be able to fit into a 0-byte
     * space.
     *
     * 2. Allocate space for the varname according to the value of
     * namesize.
     *
     * 3. Call cs_abs_loc_name() again to set varname to the variable
     * name.
     */
    if ( cs_abs_loc_name(sym,NULL,0,&namesize) == CS_TRUNCATED)
    {
        varname = malloc(namesize * sizeof(char));

        /* Check that malloc() didn't fail. */
        if( !varname )         
            abort();
        if ( cs_abs_loc_name(sym,varname,namesize,&ns) 
             == CS_SUCCESS)
        {
            /* If an upper case character is found, and the variable
             * is declared in user code, issue a warning and return.*/
            if ( has_upper_char(varname, namesize) )
            {
                cs_sfid sfid;
                cs_line line;
                cs_result r;

                r = cs_abs_loc_file_line( sym, &sfid, &line );

                /* ... and if the variable is declared at some
                 * location in the source code... */
                if ( r == CS_SUCCESS )
                {
                    char filename[1024];
                    cs_size_t filename_sz;

                    /* ... and that location is not in a library...*/  
                    r = cs_file_get_include_name( sfid, 
                                                  filename, 
                                                  sizeof(filename), 
                                                  &filename_sz );
                    if( r != CS_TRUNCATED && r != CS_SUCCESS )
                        abort();

                    if( strstr( filename, "/libmodels/" )
                        || strstr( filename, "\\\\libmodels\\\\" )
                        || strstr( filename, "/smel/" )
                        || strstr( filename, "\\\\smel\\\\" ) )
                    {
                        free(varname);
                        return;
                    }
                    /* ... then construct and issue a warning.*/
                    buf = malloc(namesize + strlen("variable name %s has upper case character"));
                    if( !buf )
                        abort();
                    sprintf( buf,    
                             "variable name %s has upper case character",
                             varname );
                    csonar_report_location_warning( sfid, 
                                                    line, 
                                                    upvar, 
                                                    buf,
                                                    csrf_none,
                                                    NULL,
                                                    NULL);
                    free(buf);
                } 
                free(varname);
                return;
            } 
        } 
        free(varname);
    }
}


/* The plug-in defines a new warning class to go with its new
 * check. We do not associate any \CodeSonar mnemonics \ or
 * \CWE identifiers  \ with the new class, as none are appropriate.
 * The new warning class must be created with
 * csonar_create_warningclass(), which must be called from a setup
 * visitor.*/

static void setup(void *ctx)
{
    upvar = csonar_create_warningclass(
            "Variable name has upper case characters",
            "",
            1.0,
            csonar_bcf_padding,
            csws_style );
}


/* Every C plug-in for CodeSonar must define this function,
 * which must contain all the calls to csonar_add_*_visitor() and
 * csonar_create_warningclass() required for the plug-in. */

static void cs_plug_main(void)
{
    csonar_add_setup_visitor(NULL, setup, NULL);

    /* check_var_case() must be added as an ABS_LOC visitor inside
     * cs_plug_main() in order for CodeSonar to add it to the
     * visitor list. */
    csonar_add_abs_loc_visitor(NULL, check_var_case, NULL);
}

/* All CodeSonar C plug-ins require a line of this form at the
 * top level. Note that the argument to CS_DEFINE_PLUGIN corresponds
 * to the name of the compiled plug-in (which will be either
 * UCvar_plugin.dll or UCvar_plugin.so, depending on your system).*/

CS_DEFINE_PLUGIN(UCvar_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 how many variables they contain whose names include upper case letters.

Clean Up

If you don't want to be warned in future about variable names containing upper case letters:

Part Two

The plug-in for Part Two implements a check for mismatched parentheses in the output of a program. This involves setting up a step visitor to ensure that the control-flow paths in the program involve properly balanced calls to the procedures that write opening and closing parentheses.

callseq_plugin.c

callseq_plugin.c contains comments describing the various elements of the plug-in. See these comments for details.

Build

Build the plug-in into a library.

Install

Install the plug-in:

Exercise

We have provided an extremely short example program to exercise the plug-in.

Clean Up

If you don't want to use this plug-in in future:

More on Plug-Ins

General Information:

Visitors Plug-ins are based on visitors, which specify actions to be carried out on elements of the CodeSonar internal representation (IR) at various stages of the analysis.
Writing Plug-Ins General information about creating plug-ins to attach custom functionality to the CodeSonar analysis.
Plug-In Tutorial Two annotated example plug-ins (each provided in all API languages), with building and installation instructions.
AST API Tutorial The AST API tutorial (provided in all API languages) also uses plug-ins.

Specific API Language:

  Plug-In Guidelines Key API References
C++ Writing C++ Plug-Ins classes analysis, visitor, warningclass, project_metricclass, compunit_metricclass, sfile_metricclass, procedure_metricclass.
Python Writing Python Plug-Ins Visitor decorators, Metric decorators; classes analysis, warningclass, project_metricclass, compunit_metricclass, sfile_metricclass, procedure_metricclass.
C Writing C Plug-Ins CodeSonar Plug-In API: C Functions and Types for Visitors, Warnings, and Metrics