JavaScript is not currently enabled, but is required for full CodeSonar manual search and browse functionality.
If you are viewing this file in your hub's Web GUI, enable JavaScript in your browser: you will also need it for GUI functionality.
If you opened this file directly from disk, your browser may be directly suppressing JavaScript functionality: certain browsers perform this suppression on local files (but not files delivered by web servers) for security reasons.
| CodeSonar® 9.0p0 Hot Tips | CONFIDENTIAL | CodeSecure Inc |
This tutorial example uses the CodeSonar AST API (C) to construct 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.
File UnsafeCast_plugin.c is reproduced below.
/* * 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 \warning class \ 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 \normalized AST\ * 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 \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_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 \AST pattern-matching functionality \ 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)
Build the plug-in into a library.
| Compiler | Command Line | Notes | |
|---|---|---|---|
| Windows | |||
| cl |
cl /LD /MD
"/I%CSONAR%\codesonar\include"
"/I%CSONAR%\csurf\include" UnsafeCast_plugin.c "%CSONAR%\codesonar\lib\codesonar.lib" |
|
|
| Cygwin gcc |
Note: Cygwin gcc is only suitable for building
32-bit
CodeSonar plugins. Cygwin gcc only builds Cygwin binaries, so is
not suitable for building 64-bit CodeSonar plug-ins. If you
do not have an alternative gcc, you will need to download and
install one. One option is mingw-w64.
|
|
|
| MinGW gcc |
gcc -shared
"-I$CSONAR\codesonar\include"
"-I$CSONAR\csurf\include" UnsafeCast_plugin.c -o UnsafeCast_plugin.dll "-L$CSONAR\codesonar\lib" -lcodesonar |
|
|
| All Other Systems We provide a full example for gcc. For other compilers, build UnsafeCast_plugin.so from UnsafeCast_plugin.c, including $CSONAR/codesonar/include and $CSONAR/csurf/include. |
|||
| gcc |
gcc -fPIC -shared -o UnsafeCast_plugin.so
UnsafeCast_plugin.c
-I$CSONAR/codesonar/include -I$CSONAR/csurf/include |
|
|
See Writing C Plug-Ins For CodeSonar for further compilation details and troubleshooting hints.
Install the plug-in:
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.
| with cl: |
codesonar analyze testcast host:port cl /c
testcast.c
|
|---|---|
| with gcc: |
codesonar analyze testcast host:port gcc -c
testcast.c
|
If you don't want to perform this check in future: