Hi,
If I write some code like this:
#include <stdio.h>
int main(void)
{
char mystring[5];
printf("String:%s\n",mystring);
return 0;
}
It compiles okay (no warnings), but when runs it has undefined results
because I've not put anything into mystring before attempting to print
it, so it gets whatever garbage is left in memory.
Are there any "pre-compiler" code analysis tools out there that would
pick up this sort of problem? The platform is sparc Solaris 10.
I know I can use dbx with the -check all flag to run it in debug mode,
but it would be nice to have a tool that can scan the source and flag up
a warning without having to execute every branch of the code in a debugger.
Any suggestions please?
Thanks.
|
|
0
|
|
|
|
Reply
|
spamspamspam3 (19)
|
8/8/2011 11:00:11 AM |
|
Martin Crouch <spamspamspam@spaml.com> writes:
> If I write some code like this:
>
> #include <stdio.h>
> int main(void)
> {
> char mystring[5];
>
> printf("String:%s\n",mystring);
>
> return 0;
> }
>
> It compiles okay (no warnings), but when runs it has undefined
> results because I've not put anything into mystring before attempting
> to print it, so it gets whatever garbage is left in memory.
>
> Are there any "pre-compiler" code analysis tools out there that would
> pick up this sort of problem? The platform is sparc Solaris 10.
>
> I know I can use dbx with the -check all flag to run it in debug mode,
> but it would be nice to have a tool that can scan the source and flag
> up a warning without having to execute every branch of the code in a
> debugger.
>
> Any suggestions please?
You're looking for a static analysis tool. You can find a list at:
http://en.wikipedia.org/wiki/List_of_tools_for_static_code_analysis
Some of them are rather expensive.
--
http://www.greenend.org.uk/rjk/
|
|
0
|
|
|
|
Reply
|
rjk (492)
|
8/8/2011 11:09:24 AM
|
|
On 08/ 8/11 11:00 PM, Martin Crouch wrote:
> Hi,
>
> If I write some code like this:
>
> #include<stdio.h>
> int main(void)
> {
> char mystring[5];
>
> printf("String:%s\n",mystring);
>
> return 0;
> }
>
> It compiles okay (no warnings), but when runs it has undefined results
> because I've not put anything into mystring before attempting to print
> it, so it gets whatever garbage is left in memory.
>
> Are there any "pre-compiler" code analysis tools out there that would
> pick up this sort of problem? The platform is sparc Solaris 10.
Unit tests (written first).
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9873)
|
8/8/2011 11:12:12 AM
|
|
On Mon, 08 Aug 2011 12:00:11 +0100, Martin Crouch wrote:
> If I write some code like this:
>
> #include <stdio.h>
> int main(void)
> {
> char mystring[5];
>
> printf("String:%s\n",mystring);
>
> return 0;
> }
> }
> It compiles okay (no warnings),
Really? You should get an error due to the extra closing brace.
> but when runs it has undefined results
> because I've not put anything into mystring before attempting to print it,
> so it gets whatever garbage is left in memory.
>
> Are there any "pre-compiler" code analysis tools out there that would pick
> up this sort of problem? The platform is sparc Solaris 10.
In general, "gcc -O -Wall ..." will warn about using uninitialised
variables. It won't work in the specific case above because gcc doesn't
know that the printf() call will access the elements of mystring. If the
format string had "%p" instead of "%s":
printf("String:%p\n",mystring);
there wouldn't actually be any error in the code. OTOH, this:
printf("String:%c\n",mystring[0]);
will result in a warning (with "-O -Wall") because it can see the access.
Similarly, this:
scanf("String:%s\n",mystring);
doesn't have any issues, because the array is written rather than read.
Note that printf() is a harder case because it's variadic; otherwise, it
would be reasonable to assume that an array passed as a "const char *"
argument is going to be read and should contain something (there are cases
where this needn't be true, e.g. the "%p" example, but any static analysis
tool is going to give the occasional false result).
|
|
0
|
|
|
|
Reply
|
nobody (4804)
|
8/8/2011 1:01:32 PM
|
|
Nobody <nobody@nowhere.com> writes:
> On Mon, 08 Aug 2011 12:00:11 +0100, Martin Crouch wrote:
>
>> If I write some code like this:
>>
>> #include <stdio.h>
>> int main(void)
>> {
>> char mystring[5];
>>
>> printf("String:%s\n",mystring);
>>
>> return 0;
>> }
>> }
>> It compiles okay (no warnings),
>
> Really? You should get an error due to the extra closing brace.
>
>> but when runs it has undefined results
>> because I've not put anything into mystring before attempting to print it,
>> so it gets whatever garbage is left in memory.
>>
>> Are there any "pre-compiler" code analysis tools out there that would pick
>> up this sort of problem? The platform is sparc Solaris 10.
>
> In general, "gcc -O -Wall ..." will warn about using uninitialised
> variables. It won't work in the specific case above because gcc doesn't
> know that the printf() call will access the elements of mystring.
Actually, many compilers, gcc included, parse printf format strings and
verify the arguments are compatible.
--
M�ns Rullg�rd
mans@mansr.com
|
|
0
|
|
|
|
Reply
|
mans (443)
|
8/8/2011 1:47:48 PM
|
|
Ian Collins <ian-news@hotmail.com> writes:
> On 08/ 8/11 11:00 PM, Martin Crouch wrote:
>> If I write some code like this:
>>
>> #include<stdio.h>
>> int main(void)
>> {
>> char mystring[5];
>>
>> printf("String:%s\n",mystring);
>>
>> return 0;
>> }
>>
>> It compiles okay (no warnings), but when runs it has undefined results
>> because I've not put anything into mystring before attempting to print
>> it, so it gets whatever garbage is left in memory.
>>
>> Are there any "pre-compiler" code analysis tools out there that would
>> pick up this sort of problem? The platform is sparc Solaris 10.
>
> Unit tests (written first).
I don't think unit tests should be the first line of defence here. It's
too common to find a forgiving environment where, say, an uninitialised
string happens to be a null string, or where a buffer overrun is silent.
--
Ben.
|
|
0
|
|
|
|
Reply
|
ben.usenet (6515)
|
8/8/2011 2:35:02 PM
|
|
> I don't think unit tests should be the first line of defence here. It's
> too common to find a forgiving environment where, say, an uninitialised
> string happens to be a null string, or where a buffer overrun is silent.
>
That's right, the code we actually had a problem with passed system
testing, user acceptance testing and had been running live for a couple
of years without a problem being noticed. It just happened to get
garbage in the wrong part of memory so the string wasn't empty this time
and it went pop.
|
|
0
|
|
|
|
Reply
|
spamspamspam3 (19)
|
8/8/2011 3:05:17 PM
|
|
>> In general, "gcc -O -Wall ..." will warn about using uninitialised
>> variables. It won't work in the specific case above because gcc doesn't
>> know that the printf() call will access the elements of mystring.
>
> Actually, many compilers, gcc included, parse printf format strings and
> verify the arguments are compatible.
>
We're still using the SUNWspro "cc" compiler maybe I'll get an install
of gcc and see what it throws up. Thanks.
|
|
0
|
|
|
|
Reply
|
spamspamspam3 (19)
|
8/8/2011 3:08:02 PM
|
|
Martin Crouch wrote:
>>> In general, "gcc -O -Wall ..." will warn about using uninitialised
>>> variables. It won't work in the specific case above because gcc doesn't
>>> know that the printf() call will access the elements of mystring.
>>
>> Actually, many compilers, gcc included, parse printf format strings and
>> verify the arguments are compatible.
>
> We're still using the SUNWspro "cc" compiler maybe I'll get an install
> of gcc and see what it throws up. Thanks.
lint (comes with SUNWspro) also parses printf format. For the
particular example posted here, it doesn't complain (well, just about
the unused return value from printf), but then neither do other
compilers.
|
|
0
|
|
|
|
Reply
|
marc.glisse (238)
|
8/8/2011 3:39:51 PM
|
|
Martin Crouch wrote:
> #include <stdio.h>
> int main(void)
> {
> char mystring[5];
>
> printf("String:%s\n",mystring);
>
> return 0;
> }
>
> It compiles okay (no warnings), but when runs it has undefined results
> because I've not put anything into mystring before attempting to print
> it, so it gets whatever garbage is left in memory.
While it is definitely not a pre-analysis tool, valgrind will usually
catch this type of error, along with numerous other types of memory
access and allocation problems. Valgrind can't perform miracles though.
For instance, it can't tell if code is reading from the wrong elements
in an array when all array elements have previously been initialized and
all the reads fall within the array.
Regards,
David Mathog
|
|
0
|
|
|
|
Reply
|
dmathog (163)
|
8/8/2011 3:48:48 PM
|
|
Måns Rullgård <mans@mansr.com> wrote:
> Nobody <nobody@nowhere.com> writes:
<snip>
> > In general, "gcc -O -Wall ..." will warn about using uninitialised
> > variables. It won't work in the specific case above because gcc doesn't
> > know that the printf() call will access the elements of mystring.
>
> Actually, many compilers, gcc included, parse printf format strings and
> verify the arguments are compatible.
The argument types, sure. But I'd bet that the printf bits of GCC are
completely distinct and separate from the data flow analysis bits that can
catch uninitialized _access_.
Issues like this cry out for a patch (likely non-trivial, unless the ground
work has already been laid for routines like memcpy), but the last time I
tried to grok the printf checking code in GCC it took me several weeks to
recuperate. clang doesn't catch it either, but that's another kind of
headache of similar magnitude.
|
|
0
|
|
|
|
Reply
|
William
|
8/8/2011 5:20:22 PM
|
|
On 08/ 9/11 02:35 AM, Ben Bacarisse wrote:
> Ian Collins<ian-news@hotmail.com> writes:
>
>> On 08/ 8/11 11:00 PM, Martin Crouch wrote:
>
>>> If I write some code like this:
>>>
>>> #include<stdio.h>
>>> int main(void)
>>> {
>>> char mystring[5];
>>>
>>> printf("String:%s\n",mystring);
>>>
>>> return 0;
>>> }
>>>
>>> It compiles okay (no warnings), but when runs it has undefined results
>>> because I've not put anything into mystring before attempting to print
>>> it, so it gets whatever garbage is left in memory.
>>>
>>> Are there any "pre-compiler" code analysis tools out there that would
>>> pick up this sort of problem? The platform is sparc Solaris 10.
>>
>> Unit tests (written first).
>
> I don't think unit tests should be the first line of defence here. It's
> too common to find a forgiving environment where, say, an uninitialised
> string happens to be a null string, or where a buffer overrun is silent.
My point was if the code was written to pass a test, the bug probably
wouldn't have been introduced in the first place.
As the answers here show, this kind of problem is a difficult one for
compilers and lint to pick up. None that I know will spot this.
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9873)
|
8/8/2011 8:03:18 PM
|
|
Ian Collins <ian-news@hotmail.com> writes:
> On 08/ 9/11 02:35 AM, Ben Bacarisse wrote:
>> Ian Collins<ian-news@hotmail.com> writes:
>>
>>> On 08/ 8/11 11:00 PM, Martin Crouch wrote:
>>
>>>> If I write some code like this:
>>>>
>>>> #include<stdio.h>
>>>> int main(void)
>>>> {
>>>> char mystring[5];
>>>>
>>>> printf("String:%s\n",mystring);
>>>>
>>>> return 0;
>>>> }
>>>>
>>>> It compiles okay (no warnings), but when runs it has undefined results
>>>> because I've not put anything into mystring before attempting to print
>>>> it, so it gets whatever garbage is left in memory.
>>>>
>>>> Are there any "pre-compiler" code analysis tools out there that would
>>>> pick up this sort of problem? The platform is sparc Solaris 10.
>>>
>>> Unit tests (written first).
>>
>> I don't think unit tests should be the first line of defence here. It's
>> too common to find a forgiving environment where, say, an uninitialised
>> string happens to be a null string, or where a buffer overrun is silent.
>
> My point was if the code was written to pass a test, the bug probably
> wouldn't have been introduced in the first place.
Yes, I got that that was your point! Did you get that I was disagreeing
with it? :-)
We may have to leave it there since we are both making statistical
arguments: you say that units test will "probably" prevent the bug
appearing, and I am saying that cases where it might not are all "too
common". I have no data to back up my statistics except anecdotes to
the effect that it's happened more than once.
> As the answers here show, this kind of problem is a difficult one for
> compilers and lint to pick up. None that I know will spot this.
Unit tests + valgrind would be my proposal:
$ gcc -g -o printf -std=c99 -pedantic -Wall -Wextra printf.c
$ valgrind ./printf
==16796== Memcheck, a memory error detector
==16796== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==16796== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==16796== Command: ./printf
==16796==
==16796== Conditional jump or move depends on uninitialised value(s)
==16796== at 0x4E7635E: vfprintf (vfprintf.c:1614)
==16796== by 0x4E7D539: printf (printf.c:35)
==16796== by 0x400544: main (printf.c:6)
==16796==
... etc...
--
Ben.
|
|
0
|
|
|
|
Reply
|
ben.usenet (6515)
|
8/8/2011 9:51:03 PM
|
|
Martin Crouch <spamspamspam@spaml.com> writes:
[Please include attributions (like the line above) in your posts.]
>> I don't think unit tests should be the first line of defence here. It's
>> too common to find a forgiving environment where, say, an uninitialised
>> string happens to be a null string, or where a buffer overrun is silent.
>>
>
> That's right, the code we actually had a problem with passed system
> testing, user acceptance testing and had been running live for a
> couple of years without a problem being noticed. It just happened to
> get garbage in the wrong part of memory so the string wasn't empty
> this time and it went pop.
Someone has already pointed out valgrind so I'll just second that. In
addition, I've had some good help from splint[1]:
$ splint printf.c
Splint 3.1.2 --- 03 May 2009
printf.c: (in function main)
printf.c:6:23: Passed storage mystring not completely defined (*mystring is
undefined): printf (..., mystring, ...)
Storage derivable from a parameter, return value or global is not defined.
Use /*@out@*/ to denote passed or returned storage which need not be defined.
(Use -compdef to inhibit warning)
Finished checking --- 1 code warning
It's default setting don't seem to suit my coding style, but it's highly
configurable so I may continue to use it. Unfortunately, it results is
lots of split-specific comments to control the output.
[1] <http://www.splint.org>
--
Ben.
|
|
0
|
|
|
|
Reply
|
ben.usenet (6515)
|
8/8/2011 9:57:20 PM
|
|
On 08/ 9/11 09:51 AM, Ben Bacarisse wrote:
> Ian Collins<ian-news@hotmail.com> writes:
>
>> On 08/ 9/11 02:35 AM, Ben Bacarisse wrote:
>>> Ian Collins<ian-news@hotmail.com> writes:
>>>
>>>> On 08/ 8/11 11:00 PM, Martin Crouch wrote:
>>>
>>>>> If I write some code like this:
>>>>>
>>>>> #include<stdio.h>
>>>>> int main(void)
>>>>> {
>>>>> char mystring[5];
>>>>>
>>>>> printf("String:%s\n",mystring);
>>>>>
>>>>> return 0;
>>>>> }
>>>>>
>>>>> It compiles okay (no warnings), but when runs it has undefined results
>>>>> because I've not put anything into mystring before attempting to print
>>>>> it, so it gets whatever garbage is left in memory.
>>>>>
>>>>> Are there any "pre-compiler" code analysis tools out there that would
>>>>> pick up this sort of problem? The platform is sparc Solaris 10.
>>>>
>>>> Unit tests (written first).
>>>
>>> I don't think unit tests should be the first line of defence here. It's
>>> too common to find a forgiving environment where, say, an uninitialised
>>> string happens to be a null string, or where a buffer overrun is silent.
>>
>> My point was if the code was written to pass a test, the bug probably
>> wouldn't have been introduced in the first place.
>
> Yes, I got that that was your point! Did you get that I was disagreeing
> with it? :-)
I do and I agree tests aren't a panacea. I reorder my tests fairly
often to help with detecting code that accidentally relies on
uninitialised memory. It is easy to test for reading uninitialised
dynamic memory, but much harder with automatic variables.
> We may have to leave it there since we are both making statistical
> arguments: you say that units test will "probably" prevent the bug
> appearing, and I am saying that cases where it might not are all "too
> common". I have no data to back up my statistics except anecdotes to
> the effect that it's happened more than once.
>
>> As the answers here show, this kind of problem is a difficult one for
>> compilers and lint to pick up. None that I know will spot this.
>
> Unit tests + valgrind would be my proposal:
>
> $ gcc -g -o printf -std=c99 -pedantic -Wall -Wextra printf.c
> $ valgrind ./printf
> ==16796== Memcheck, a memory error detector
> ==16796== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
> ==16796== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
> ==16796== Command: ./printf
> ==16796==
> ==16796== Conditional jump or move depends on uninitialised value(s)
> ==16796== at 0x4E7635E: vfprintf (vfprintf.c:1614)
> ==16796== by 0x4E7D539: printf (printf.c:35)
> ==16796== by 0x400544: main (printf.c:6)
> ==16796==
> ... etc...
I certainly agree with that, I do much the same by running tests under
dbx with memory checking enabled.
But which is the first line of defence, the tests or valgrind?
:)
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9873)
|
8/8/2011 10:01:42 PM
|
|
Ian Collins <ian-news@hotmail.com> wrote:
> On 08/ 8/11 11:00 PM, Martin Crouch wrote:
> > Hi,
> >
> > If I write some code like this:
> >
> > #include<stdio.h>
> > int main(void)
> > {
> > char mystring[5];
> >
> > printf("String:%s\n",mystring);
> >
> > return 0;
> > }
> >
> > It compiles okay (no warnings), but when runs it has undefined results
> > because I've not put anything into mystring before attempting to print
> > it, so it gets whatever garbage is left in memory.
> >
> > Are there any "pre-compiler" code analysis tools out there that would
> > pick up this sort of problem? The platform is sparc Solaris 10.
> Unit tests (written first).
While I tend to agree with Ben that this may not be one of
the most obvious examples for the usefulness of unit tests I
have a quite different question: I haven't gotten into the
habit of writing unit tests and actually have to admit that
I don't have much more than a rather fuzzy idea what it might
involve. On the other hand I'd rather like to learn a bit more
about this concept. Is there something you could recommend
(going a bit further than e.g. Wikipedia) for reading to get
me started (in the best of all cases not too much tied to a
single language)?
Thanks and best regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
|
|
0
|
|
|
|
Reply
|
jt68 (1134)
|
8/8/2011 10:06:16 PM
|
|
On 08/ 9/11 10:06 AM, Jens Thoms Toerring wrote:
>
> While I tend to agree with Ben that this may not be one of
> the most obvious examples for the usefulness of unit tests I
> have a quite different question: I haven't gotten into the
> habit of writing unit tests and actually have to admit that
> I don't have much more than a rather fuzzy idea what it might
> involve. On the other hand I'd rather like to learn a bit more
> about this concept. Is there something you could recommend
> (going a bit further than e.g. Wikipedia) for reading to get
> me started (in the best of all cases not too much tied to a
> single language)?
The C2 wiki is the original and probably still the best place to start.
http://c2.com/cgi/wiki?UnitTest
http://c2.com/cgi/wiki?CodeUnitTestFirst
There are <pick your language>Unit test frameworks for just about every
common language. The original was jUnit which is integrated into just
about every Java IDE. I use cppUnit for C and C++ and phpUnit for (you
guessed it..) PHP. Both integrate well into my IDE of choice
(NetBeans). jsUnit is an excellent testing tool for JavaScript.
--
Ian Collins
|
|
0
|
|
|
|
Reply
|
ian-news (9873)
|
8/8/2011 10:18:08 PM
|
|
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Martin Crouch <spamspamspam@spaml.com> writes:
>
> [Please include attributions (like the line above) in your posts.]
>
>>> I don't think unit tests should be the first line of defence here. It's
>>> too common to find a forgiving environment where, say, an uninitialised
>>> string happens to be a null string, or where a buffer overrun is silent.
>>>
>>
>> That's right, the code we actually had a problem with passed system
>> testing, user acceptance testing and had been running live for a
>> couple of years without a problem being noticed. It just happened to
>> get garbage in the wrong part of memory so the string wasn't empty
>> this time and it went pop.
>
> Someone has already pointed out valgrind so I'll just second that. In
> addition, I've had some good help from splint[1]:
>
> $ splint printf.c
> Splint 3.1.2 --- 03 May 2009
>
> printf.c: (in function main)
> printf.c:6:23: Passed storage mystring not completely defined (*mystring is
> undefined): printf (..., mystring, ...)
> Storage derivable from a parameter, return value or global is not defined.
> Use /*@out@*/ to denote passed or returned storage which need not be defined.
> (Use -compdef to inhibit warning)
>
> Finished checking --- 1 code warning
IIRC splint will give some error even if you do initialise the array.
It simply has a ridiculous notion of how pointers to should be passed
around.
--
M�ns Rullg�rd
mans@mansr.com
|
|
0
|
|
|
|
Reply
|
mans (443)
|
8/8/2011 10:32:14 PM
|
|
Ian Collins <ian-news@hotmail.com> wrote:
> On 08/ 9/11 10:06 AM, Jens Thoms Toerring wrote:
> >
> > While I tend to agree with Ben that this may not be one of
> > the most obvious examples for the usefulness of unit tests I
> > have a quite different question: I haven't gotten into the
> > habit of writing unit tests and actually have to admit that
> > I don't have much more than a rather fuzzy idea what it might
> > involve. On the other hand I'd rather like to learn a bit more
> > about this concept. Is there something you could recommend
> > (going a bit further than e.g. Wikipedia) for reading to get
> > me started (in the best of all cases not too much tied to a
> > single language)?
> The C2 wiki is the original and probably still the best place to start.
> http://c2.com/cgi/wiki?UnitTest
> http://c2.com/cgi/wiki?CodeUnitTestFirst
> There are <pick your language>Unit test frameworks for just about every
> common language. The original was jUnit which is integrated into just
> about every Java IDE. I use cppUnit for C and C++ and phpUnit for (you
> guessed it..) PHP. Both integrate well into my IDE of choice
> (NetBeans). jsUnit is an excellent testing tool for JavaScript.
Thank you, I guess I will spend some time in the next days
reading and playing around with things;-)
Best regards, Jens
--
\ Jens Thoms Toerring ___ jt@toerring.de
\__________________________ http://toerring.de
|
|
0
|
|
|
|
Reply
|
jt68 (1134)
|
8/8/2011 11:25:43 PM
|
|
On Mon, 08 Aug 2011 14:47:48 +0100, Måns Rullgård wrote:
>> In general, "gcc -O -Wall ..." will warn about using uninitialised
>> variables. It won't work in the specific case above because gcc doesn't
>> know that the printf() call will access the elements of mystring.
>
> Actually, many compilers, gcc included, parse printf format strings and
> verify the arguments are compatible.
That isn't relevant to the part you quoted (it /is/ relevant to the
issue of printf() being variadic, which I mentioned at the end).
Type information alone isn't sufficient to accurately determine behaviour
(although it might be a useful heuristic). In spite of knowing the
argument types, gcc won't complain about this either:
#include <stdio.h>
#include <string.h>
int main(void)
{
char mystring[5];
printf("Length:%d\n",strlen(mystring));
return 0;
}
A heuristic-based analyser might infer that passing a "const" pointer to
uninitialised data is probably an error, although it's not certain.
Ultimately, you can't know for sure without knowing the workings of the
library function.
|
|
0
|
|
|
|
Reply
|
nobody (4804)
|
8/8/2011 11:59:37 PM
|
|
|
19 Replies
41 Views
(page loaded in 0.22 seconds)
|