
/*
 *      Copyright (c) 2023, an unpublished work by CodeSecure, Inc.
 *                      ALL RIGHTS RESERVED
 *
 *      Copyright (c) 2008-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.c
 * 
 * a small example plugin that reports casts from const to non-const
 * types
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>


/* All C plug-ins for CodeSonar must include csonar_plugin.h. Two
 * header files are needed for the AST API: cs_ast.h declares the
 * general AST functions and types, and cs_ast_c.h declares the AST
 * types specific to C/C++ programs. The plug-in operates on
 * PDG_VERTEX objects, so we also need cs_pdg_vertex.h. */

#include "csonar_plugin.h"
#include "cs_pdg_vertex.h"
#include "cs_pdg.h"
#include "cs_ast.h"
#include "cs_ast_c.h"
#include "cs_utility.h"


/* badcast is a new \link WarningClasses/WarningClasses.html warning
 * class \endlink defined by this plug-in. It must be 
 * created in a setup visitor. We declare pattern as a file static
 * variable so that we only have to compile the pattern once (instead
 * of every time we visit a PDG_VERTEX).*/

static cs_warningclass_t badcast;
static cs_ast_pattern *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 PDG_VERTEX.*/

static void check_cast(cs_pdg_vertex pdgv, void *ctx){
     
    cs_ast ast, trav_ast;
    cs_ast_traverse_iter it;
    cs_result res;
    cs_size_t bsize;
    cs_pdg proc_pdg;

    /* Retrieve the procedure containing the program point pdgv. */
    CSRC( cs_pdg_vertex_pdg( pdgv, &proc_pdg ) );
    
    /* 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( cs_pdg_get_kind( proc_pdg ) != cs_pdg_kind_user_defined )
        return;

    /* can we get an associated normalized ast? */
    res = cs_pdg_vertex_ast(pdgv,csaf_c_normalized, &ast);

    /* If the plug-in can't recover the \link
     * C_Module/API/AST/csaf_c_normalized.html normalized AST\endlink 
     * associated with pdgv (for whatever reason), there is
     * no point in proceeding. Note that there is at most one
     * normalized AST associated with each PDG_VERTEX.*/
    if (res !=  CS_SUCCESS){
        printf("Problem with cs_pdg_vertex_ast(): %s\n", 
               cs_resolve_error(res));
        return;
    }
        
    /* An offending cast might be nested inside other expressions, so
     * we need to check every subtree of ast against our pattern. The
     * AST API includes iterator functionality to support this.*/
    for (res = cs_ast_iter_first (ast, csatf_normal, &trav_ast, &it); 
         res == CS_SUCCESS;
         res = cs_ast_iter_next (csatd_normal, &trav_ast, &it)){

        /* We don't care about the variable bindings generated when
         * the pattern is matched, only in whether or not the match
         * succeeded. Therefore, we don't need to provide space to
         * store the binding.*/
        res = cs_ast_match(trav_ast,pattern,NULL,0,&bsize);
     
        /* Note that CS_TRUNCATED is the expected result of a
         * successful match, since we didn't provide any space for the
         * resulting binding. The visitor returns as soon as the
         * pattern is matched. */        
        if ((res == CS_TRUNCATED)||(res == CS_SUCCESS)){
            csonar_report_warning(pdgv,badcast,"unsafe cast",csrf_none,NULL,NULL);
            cs_ast_iter_close(&it);
            return;
        }
    }
 
    /* Check the return codes from calls to cs_ast_iter_next() and
     * cs_ast_iter_close() in case there are unexpected problems.*/
    if (res != CS_OUT_OF_ELEMENTS)
        printf("Problem with cs_ast_iter_next(): %s\n", 
               cs_resolve_error(res));    

    res = cs_ast_iter_close(&it);
    if (res != CS_SUCCESS)
        printf("Problem with cs_ast_iter_close(): %s\n", 
               cs_resolve_error(res));
}
                          
   
/* We can't clean up global variable pattern until the program
 * traversal has finished, so cs_ast_pattern_close(pattern) must be
 * called inside a program finish visitor.*/

static void close_pattern(void *ctx){
    cs_ast_pattern_close(pattern);
}




/* The plug-in defines a new warning class to go with its new
 * check. We do not associate any \link
 * WarningClasses/MnemonicHierarchy.html CodeSonar mnemonics \endlink
 * or \link WarningClasses/CWE/CWE.html CWE identifiers 
 * \endlink 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_badcast(void *ctx){
    badcast = csonar_create_warningclass(
            "Cast from const to non-const",
            "",
            1,
            csonar_bcf_warning_point_is_start_point,
            csws_security );
}



/* Every C plug-in for CodeSonar must define this function, which must
 * contain all the calls to csonar_add_*_visitor() required for the
 * plug-in.*/

static void cs_plug_main(void){

    cs_result res;
    cs_const_string patprob;
    cs_const_string paterr;
    
    /* The \link API/AST/API_AST_Patterns.html AST pattern-matching
     * functionality \endlink in the C API uses cs_ast_pattern
     * objects, which must be created with this function. 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
     */

    res = cs_ast_pattern_compile (
        "(c:cast"
        "  :2 (?e"
        "    :type (c:pointer"
        "      :pointed-to (?c :is-const #t)))"
        "  :type (c:pointer"
        "    :pointed-to (?k :is-const #f)))", 
        &pattern, 
        &patprob, 
        &paterr);
    if (res!=CS_SUCCESS){
        printf("%s, error string is %s\n error problem is %s", 
               cs_resolve_error(res), 
               paterr, 
               patprob);
        return;
    }

    /* setup_badcast() must be added as a setup visitor so that the
     * warning class is set up correctly.
     * check_cast() must be added as a PDG_VERTEX visitor inside
     * cs_plug_main() in order for CodeSonar to add it to the visitor
     * list. 
     * close_pattern() must be added as a program finish visitor
     * to ensure that the pattern is properly disposed of at the end
     * of the program traversal.*/
    csonar_add_setup_visitor(NULL, setup_badcast, NULL);
    csonar_add_pdg_vertex_visitor(NULL, check_cast, NULL);
    csonar_add_program_finish_visitor(NULL, close_pattern, NULL);

}

/* All CodeSonar C plug-ins require 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 (which will be
 * either UnsafeCast_plugin.dll or UnsafeCast_plugin.so, depending on your
 * system).*/

CS_DEFINE_PLUGIN(UnsafeCast_plugin)
