1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
|
<!--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 ============================================================-->
|