443 lines
16 KiB
HTML
443 lines
16 KiB
HTML
<!--startcut ==============================================-->
|
|
<!-- *** BEGIN HTML header *** -->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
<HTML><HEAD>
|
|
<title>Static checking of C programs with LCLint LG #51</title>
|
|
</HEAD>
|
|
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
|
|
ALINK="#FF0000">
|
|
<!-- *** END HTML header *** -->
|
|
|
|
<!-- *** BEGIN navbar *** -->
|
|
<A HREF="index.html"><IMG ALT="[ Table of Contents ]"
|
|
SRC="../gx/indexnew.gif" WIDTH=163 HEIGHT=60 ALIGN=bottom ></A>
|
|
<A HREF="../index.html"><IMG ALT="[ Front Page ]"
|
|
SRC="../gx/homenew.gif" WIDTH=163 HEIGHT=60 ALIGN=bottom></A>
|
|
<A HREF="nielsen.html"><IMG ALT="[ Prev ]" SRC="../gx/back2.gif" WIDTH=41 HEIGHT=60 ALIGN=bottom></A>
|
|
<A HREF="../faq/index.html"><IMG ALT="[ Linux Gazette FAQ ]"
|
|
SRC="./../gx/dennis/faq.gif"WIDTH=163 HEIGHT=60 ALIGN=bottom></A>
|
|
<A HREF="steffler.html"><IMG ALT="[ Next ]" SRC="../gx/fwd.gif" WIDTH=41 HEIGHT=60 ALIGN=bottom ></A>
|
|
<!-- *** END navbar *** -->
|
|
|
|
<!--endcut ============================================================-->
|
|
|
|
<H4>
|
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
|
</H4>
|
|
|
|
<P> <HR> <P>
|
|
<!--===================================================================-->
|
|
|
|
<center>
|
|
<H1><font color="maroon">Static checking of C programs with LCLint</font></H1>
|
|
<H4>By <a href="mailto:iclabs@vsnl.com">Pramode C E and Gopakumar C E</a></H4>
|
|
</center>
|
|
<P> <HR> <P>
|
|
|
|
<!-- END header -->
|
|
|
|
|
|
|
|
|
|
<BLOCKQUOTE>
|
|
<p> "Thou shalt run lint frequently and study its pronouncements with
|
|
care, for verily its perception and judgment oft exceed thine."
|
|
<DIV ALIGN="right"><cite>-Henry Spencer, "The Ten Commandments for C Programmers"</cite></DIV>
|
|
</BLOCKQUOTE>
|
|
<p> C programmers take pride in thinking(and often proclaiming to the
|
|
world) that they know what they are doing. This supreme self confidence
|
|
(or shall we say arrogance?) is not a bad thing - but a little caution
|
|
is always judicious as C is a language with many dark corners(why
|
|
should people write books like "C Traps and Pitfalls"?). Taking Lint as
|
|
a companion with you in your journey into the dark woods of C will
|
|
always be worthwhile - though this companion is at times a bit noisy
|
|
and tiring!
|
|
|
|
<H2><b>What is Lint?</b></H2>
|
|
|
|
<p>In the good old days(it is said), a decision was made to take out
|
|
full semantic checking from the C compiler and put it in a stand alone
|
|
program called <b>lint</b>(the usual reasons - making the compiler
|
|
smaller, simpler and faster - worshiping at the altar of the little
|
|
Tin God of efficiency). The C programmer, so sure of himself,
|
|
never took the trouble to run lint on his code - with the extremely
|
|
gratifying result that he got buggy code which compiled very fast! Lint
|
|
is a tool which shows you how your smart C compiler may spring
|
|
surprises on you - ignore him at your own peril.
|
|
|
|
<H2><b>Which Lint should I use?</b></H2>
|
|
<p>You can give LCLint a try. LCLint is a powerful tool which is available for free in source form from <a href="http://lclint.cs.virginia.edu/ftp/lclint/lclint-2.4b.src.tar.gz">http://lclint.cs.virginia.edu/ftp/lclint/lclint-2.4b.src.tar.gz</a>
|
|
LCLint, as we will see later, is much
|
|
more than a lint.</p>
|
|
|
|
<H2><b>What can LCLint do?</b></H2>
|
|
<p>LCLint does the traditional lint checks like detecting:
|
|
<ol>
|
|
<li>Unused declarations
|
|
<li>Type inconsistencies
|
|
<li>Unreachable code
|
|
<li>Use before definition
|
|
<li>Likely infinite loops and fall through cases
|
|
<li>Ignored return values and execution paths with no return
|
|
</ol>
|
|
But the specialty of LCLint is that it can do much more powerful and extensive checks by making use of
|
|
annotations (in the form of special comments) in your source program.
|
|
|
|
<H2><b>Show me LCLint in action</b></H2>
|
|
|
|
<p>Here is a small C program:
|
|
<PRE>
|
|
|
|
main()
|
|
{
|
|
int a[10];
|
|
if (sizeof(a)/sizeof(a[0]) > -1)
|
|
printf("hello\n");
|
|
}
|
|
|
|
</PRE>
|
|
We expected this to print hello, but it did not. gcc did not give us any hint. Let us see what lint has to say
|
|
about this beauty. Here is the output from running 'lclint a.c':
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
a.c: (in function main)
|
|
a.c:4:15: Operands of > have incompatible types (arbitrary unsigned integral
|
|
type, int): sizeof((a)) / sizeof((a[0])) > -1
|
|
To ignore signs in type comparisons use +ignoresigns
|
|
a.c:6:2: Path with no return in function declared to return int
|
|
There is a path through a function declared to return a value on which there
|
|
is no return statement. This means the execution may fall through without
|
|
returning a meaningful result to the caller. (-noret will suppress message)
|
|
|
|
Finished LCLint checking --- 2 code errors found
|
|
|
|
</PRE>
|
|
Oh, oh, sizeof gives you the size as an unsigned value. We are comparing this to -1, which when interpreted
|
|
as an unsigned yields a big number.</p>
|
|
|
|
<p> The output of LCLint is verbose, but it is in a form readable by ordinary mortals, and not ANSI (or ISO or
|
|
whatever) legalese. The output also displays enough of context to help us immediately locate the trouble spot.
|
|
Note that we are also told how to turn off such errors, ie, use +ignoresigns as an option when invoking LCLint.
|
|
You may call LCLint a program with a very 'helping mentality'.
|
|
|
|
<p> Let us see another example, a goof-up which any C programmer worth his name should have made when he was a
|
|
toddler:
|
|
<PRE>
|
|
|
|
main()
|
|
{
|
|
int a=0;
|
|
while (a=1)
|
|
printf("hello\n");
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
|
|
|
|
|
|
LCLint is justifiably angry at such amateurish use of C, but he is gentle in his admonishments:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
c.c: (in function main)
|
|
c.c:4:14: Test expression for while is assignment expression: a = 1
|
|
The condition test is an assignment expression. Probably, you mean to use ==
|
|
instead of =. If an assignment is intended, add an extra parentheses nesting
|
|
(e.g., if ((a = b)) ...) to suppress this message. (-predassign will suppress
|
|
message)
|
|
c.c:4:14: Test expression for while not boolean, type int: a = 1
|
|
Test expression type is not boolean or int. (-predboolint will suppress
|
|
message)
|
|
|
|
Finished LCLint checking --- 2 code errors found
|
|
|
|
</PRE>
|
|
|
|
<H2><b>Memory management pitfalls</b></H2>
|
|
<p> LCLint is capable of detecting many memory management gotchas. Here is one:
|
|
<PRE>
|
|
|
|
#include <stdlib.h>
|
|
int main()
|
|
{
|
|
int *p = malloc(5*sizeof(int));
|
|
*p = 1;
|
|
free(p);
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
If you thought LCLint would be fooled, you are mistaken:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
d.c: (in function main)
|
|
d.c:5:7: Dereference of possibly null pointer p: *p
|
|
A possibly null pointer is dereferenced. Value is either the result of a
|
|
function which may return null (in which case, code should check it is not
|
|
null), or a global, parameter or structure field declared with the null
|
|
qualifier. (-nullderef will suppress message)
|
|
d.c:4:14: Storage p may become null
|
|
|
|
Finished LCLint checking --- 1 code error found
|
|
|
|
</PRE>
|
|
When the program is rewritten as follows:
|
|
<PRE>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
int main()
|
|
{
|
|
int *p = malloc(5*sizeof(int));
|
|
if (p == NULL) {
|
|
fprintf(stderr, "error in malloc");
|
|
exit(EXIT_FAILURE);
|
|
} else *p = 1;
|
|
free(p);
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
LCLint is perfectly happy.
|
|
|
|
<p> Here is an example of code which tries to free a block twice:
|
|
<PRE>
|
|
|
|
#include <stdlib.h>
|
|
main()
|
|
{
|
|
int *p = malloc(5*sizeof(int));
|
|
int *q;
|
|
q = p;
|
|
free(q); free(p);
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
This is how LCLint responds:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
f.c: (in function main)
|
|
f.c:7:19: Dead storage p passed as out parameter: p
|
|
Memory is used after it has been released (either by passing as an only param
|
|
or assigning to and only global. (-usereleased will suppress message)
|
|
f.c:7:10: Storage p is released
|
|
|
|
Finished LCLint checking --- 1 code error found
|
|
|
|
</PRE>
|
|
|
|
<H2><b>Checking macros</b></H2>
|
|
<p>One can write perfectly horrible C programs without any assistance from the macro preprocessor and yet
|
|
some people are not satisfied. They forget that the C macro preprocessor is a simple program designed to
|
|
do simple things and proceed to build grandiose designs with dancing #defines, #ifdef's , #endif's and so
|
|
on. The result is utter chaos. The designers of LCLint are very much aware of the C programmer's passion
|
|
for macros and they have built into their program the ability to detect many kinds of macro programming
|
|
errors.
|
|
<p>Here is a typical instance of how a macro defined to work like a function does not work like one.
|
|
<PRE>
|
|
|
|
#define sqr(p) p * p
|
|
main()
|
|
{
|
|
int i=2, j;
|
|
j = sqr(i+1);
|
|
printf("%d", j); /* prints 5 */
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
LCLint is quick to point out the error. Please note that when you run lclint, you must specify that you expect
|
|
your macros (with parameters) to behave like functions by using the flag +fcn-macros. Thus, we would invoke the
|
|
above program as 'lclint i.c +fcn-macros'. Here is the output from LCLint:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
i.c:1: Parameterized macro has no prototype or specification: sqr
|
|
Function macro has no declaration. (-macrofcndecl will suppress message)
|
|
i.c: (in macro sqr)
|
|
i.c:1:13: Macro parameter p used more than once
|
|
A macro parameter is not used exactly once in all possible invocations of the
|
|
macro. To behave like a function, each macro parameter must be used exactly
|
|
once on all invocations of the macro so that parameters with side-effects are
|
|
evaluated exactly once. Use /*@sef@*/ to denote parameters that must be
|
|
side-effect free. (-macroparams will suppress message)
|
|
i.c:1:16: Macro parameter used without parentheses: p
|
|
A macro parameter is used without parentheses. This could be dangerous if the
|
|
macro is invoked with a complex expression and precedence rules will change
|
|
the evaluation inside the macro. (-macroparens will suppress message)
|
|
i.c:1:20: Macro parameter used without parentheses: p
|
|
|
|
Finished LCLint checking --- 4 code errors found
|
|
|
|
</PRE>
|
|
The third error message clearly tells you that you need to use parenthesis.
|
|
|
|
<H2><b>Annotations - the key to LCLint's power</b></H2>
|
|
|
|
<p>What does a function prototype do? Well, the prototype tells you what all arguments the function accepts - the type
|
|
and number of the arguments and the return type of the function. It acts as a sort of interface between the
|
|
function and its caller. The caller is required to abide by the interface if he wishes peace for himself, his
|
|
program and the world at large. The prototype might also be thought of as placing some sort of constraint on
|
|
the legal use of the function.
|
|
<p>The provision of constraints on functions comes to your aid when you start building large systems. You
|
|
are sure that your function foo_bar() is always called with the right number and type arguments if you
|
|
ensure that all your function calls take place in the presence of prototypes. There are several other
|
|
constraints which you would like to place on your function, like defining the list of globals which the function
|
|
is allowed to modify. The C language does not permit any such constraints, so the only option you have
|
|
is to use tools like LCLint.
|
|
<p>Here is an example of the use of an annotation.
|
|
<PRE>
|
|
|
|
static void foo(int *a, int *b) /*@modifies *a@*/
|
|
{
|
|
*a=1, *b=2;
|
|
}
|
|
main()
|
|
{
|
|
int p=10, q=20;
|
|
foo(&p, &q);
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
Note the comment(a stylized comment) /*@modifies *a@/. This is a hint to LCLint that
|
|
function foo is constrained to modify the value of *a only. Let us see what output LCLint
|
|
produces:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
j.c: (in function foo)
|
|
j.c:3:11: Undocumented modification of *b: *b = 2
|
|
An externally-visible object is modified by a function, but not listed in its
|
|
modifies clause. (-mods will suppress message)
|
|
|
|
Finished LCLint checking --- 1 code error found
|
|
|
|
</PRE>
|
|
Here is another example:
|
|
<PRE>
|
|
|
|
static void foo(int *a, int *b) /*@modifies nothing@*/
|
|
{
|
|
*a=1, *b=2;
|
|
}
|
|
main()
|
|
{
|
|
int p=10, q=20;
|
|
foo(&p, &q);
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
LCLint tells you:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
k.c: (in function foo)
|
|
k.c:3:5: Undocumented modification of *a: *a = 1
|
|
An externally-visible object is modified by a function, but not listed in its
|
|
modifies clause. (-mods will suppress message)
|
|
k.c:3:11: Undocumented modification of *b: *b = 2
|
|
k.c: (in function main)
|
|
k.c:8:5: Statement has no effect: foo(&p, &q)
|
|
Statement has no visible effect --- no values are modified. (-noeffect will
|
|
suppress message)
|
|
|
|
Finished LCLint checking --- 3 code errors found
|
|
|
|
</PRE>
|
|
Here is another one dealing with global variables:
|
|
<PRE>
|
|
|
|
/*@checkedstrict@*/ static int abc, def;
|
|
static void foo() /*@globals abc@*/
|
|
{
|
|
|
|
def = 1;
|
|
}
|
|
main()
|
|
{
|
|
int p=10, q=20;
|
|
foo(&p, &q);
|
|
return 0;
|
|
}
|
|
|
|
</PRE>
|
|
The annotation /*@checkedstrict@*/ tells LCLint to provide error messages on all undocumented accesses of
|
|
global variables, whether it be for reading or writing:
|
|
<PRE>
|
|
|
|
LCLint 2.4b --- 18 Apr 98
|
|
|
|
l.c: (in function foo)
|
|
l.c:5:5: Undocumented use of file static def
|
|
A checked global variable is used in the function, but not listed in its
|
|
globals clause. By default, only globals specified in .lcl files are checked.
|
|
To check all globals, use +allglobals. To check globals selectively use
|
|
/*@checked@*/ in the global declaration. (-globs will suppress message)
|
|
l.c:2:13: Global abc listed but not used
|
|
A global variable listed in the function's globals list is not used in the
|
|
body of the function. (-globuse will suppress message)
|
|
l.c: (in function main)
|
|
l.c:10:5: Called procedure foo may access file static abc
|
|
l.c:1:32: File static variable abc declared but not used
|
|
A variable is declared but never used. Use /*@unused@*/ in front of
|
|
declaration to suppress message. (-varuse will suppress message)
|
|
|
|
Finished LCLint checking --- 4 code errors found
|
|
|
|
</PRE>
|
|
|
|
<p>We have not even scratched the surface of LCLint's capabilities. If you feel that you wish to explore
|
|
more, go over to <a href="http://www.sds.lcs.mit.edu/lclint/">http://www.sds.lcs.mit.edu/lclint/</a>.
|
|
|
|
<H2><b>Concluding remarks</b></H2>
|
|
<p>Here is an advice, not from us, but from people who have learned it the hard way - if you wish to use lint
|
|
in your project, start from the word go, or risk insanity (Peter van der Linden, in his book 'Expert C programming - Deep C secrets',
|
|
talks of a 'lint party' he had at Sun Microsystems. He must have got a kick out of it!).
|
|
<p>Lint, especially a very powerful version like LCLint, can be used to learn more about C programming. Just
|
|
thinking about the error messages and trying to make them go away will give you a lot of insight.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- *** BEGIN copyright *** -->
|
|
<P> <hr> <!-- P -->
|
|
<H5 ALIGN=center>
|
|
|
|
Copyright © 2000, Pramode C E and Gopakumar C E<BR>
|
|
Published in Issue 51 of <i>Linux Gazette</i>, March 2000</H5>
|
|
<!-- *** END copyright *** -->
|
|
|
|
<!--startcut ==========================================================-->
|
|
<!-- P --> <HR> <!-- P -->
|
|
<A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue51/pramode.html">
|
|
<FONT SIZE="+2">Talkbacks</FONT></A>
|
|
<P>
|
|
<!-- *** BEGIN navbar *** -->
|
|
<A HREF="index.html"><IMG ALT="[ Table of Contents ]"
|
|
SRC="../gx/indexnew.gif" WIDTH=163 HEIGHT=60 ALIGN=bottom ></A>
|
|
<A HREF="../index.html"><IMG ALT="[ Front Page ]"
|
|
SRC="../gx/homenew.gif" WIDTH=163 HEIGHT=60 ALIGN=bottom></A>
|
|
<A HREF="nielsen.html"><IMG ALT="[ Prev ]" SRC="../gx/back2.gif" WIDTH=41 HEIGHT=60 ALIGN=bottom></A>
|
|
<A HREF="../faq/index.html"><IMG ALT="[ Linux Gazette FAQ ]"
|
|
SRC="./../gx/dennis/faq.gif"WIDTH=163 HEIGHT=60 ALIGN=bottom></A>
|
|
<A HREF="steffler.html"><IMG ALT="[ Next ]" SRC="../gx/fwd.gif" WIDTH=41 HEIGHT=60 ALIGN=bottom ></A>
|
|
<!-- *** END navbar *** -->
|
|
</BODY></HTML>
|
|
<!--endcut ============================================================-->
|