Bug 243234 - ptrcheck doesnt handle sscanf properly
Summary: ptrcheck doesnt handle sscanf properly
Status: RESOLVED INTENTIONAL
Alias: None
Product: valgrind
Classification: Developer tools
Component: sgcheck (show other bugs)
Version: 3.6 SVN
Platform: Compiled Sources Linux
: NOR normal
Target Milestone: ---
Assignee: Julian Seward
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-06-30 11:06 UTC by plasmahh
Modified: 2013-11-03 18:53 UTC (History)
3 users (show)

See Also:
Latest Commit:
Version Fixed In:


Attachments
the test case (c++ but should compile as C too I hope) (230 bytes, text/x-c++src)
2010-06-30 11:06 UTC, plasmahh
Details

Note You need to log in before you can comment on or make changes to this bug.
Description plasmahh 2010-06-30 11:06:43 UTC
Created attachment 48480 [details]
the test case (c++ but should compile as C too I hope)

Version:           3.6 SVN
OS:                Linux

when using sscanf with "%s %s" as format (or anything similar) ptrcheck seems to confuse the parameters passed to it

Reproducible: Always

Steps to Reproduce:
compile and run attached example code with ptrcheck

Actual Results:  
==22024== Invalid write of size 1
==22024==    at 0x55F3330: _IO_vfscanf (vfscanf.c:2787)
==22024==    by 0x5604404: vsscanf (iovsscanf.c:45)
==22024==    by 0x55FE237: sscanf (sscanf.c:34)
==22024==    by 0x400847: main (sscanf.cxx:10)
==22024==  Address 0x7fefff7b5 expected vs actual:
==22024==  Expected: stack array "buf0" in frame 3 back from here
==22024==  Actual:   stack array "buf1" in frame 3 back from here


Expected Results:  
no error should be reported
Comment 1 Tom Hughes 2010-06-30 11:15:23 UTC
This is probably because the sscanf implementation has a pointer that is being jumped from argument to argument which means that it appears to move outside the bounds of the object it is originally pointing at.

I've seen similar problems in other formatting functions of this type but it's not clear there's any way to solve it other than adding a suppression - the whole modus operandi of ptrcheck is to complain if a variable move outside the object it points at.
Comment 2 plasmahh 2010-06-30 11:50:16 UTC
I have not really looked at the sscanf implementation (and my doctor says I should not as it won't be good for my heart), but if the pointer jumps from one arg to the other, shouldn't ptrcheck somehow get that it has been re-initialized from a new input parameter then? I would understand perfectly the message if this internal pointer somehow managed to derive from buf0 a ptr that is then suddenly pointing into buf1, buf if that ptr is really *set* to point to buf1 -- the parameter to sscanf -- shouldn't ptrcheck see that as not being derived from the previos buf0 pointer, but set to something from "outside"?

if an implementation would do 

int x = param2 - param1;
char* ptr = param1;
/* use ptr */
ptr += x;
/* use it again*/

then I would say the complaint is correct, buf if it does

char* ptr = param1;
/* use ptr */
char* ptr = param2;

imho ptrcheck should be able to detect it.


Now that I think of it, the almost 100 suppressions that I wrote for our software seem to be of a similar kind. We have a ptr variable that is always re-used for pointing to an element determine within the looping.

It would really be nice if there could be a fix for this since it is a really tedious thing to always write suppressions, since for every new case you have to determine whether it is not maybe a bug, and with our old legacy code here we find quite some bugs with ptrcheck that no other tool shows us...
Comment 3 Julian Seward 2010-06-30 12:16:27 UTC
Tom's analysis is right.  To make it a bit more concrete though:

You are doing 

  char buf0[..];
  char buf1[..];
  fscanf("%s %s", buf0, buf1);

Ptrcheck assumes that any insn that reads or writes a stack or
global array once will continue to read or write in that same
array, until the stack frame of the function exits.  So what you
have here is a call to _IO_vfscanf, and somewhere in that fn is a loop
that copies data to destination arrays in response to %s in the
format string.  Since you have two %s here, first it copies data
to buf0 (so Ptrcheck assumes that is the intended destination).
Then, it switches to writing buf1, and so it complains.

Of course when _IO_vfscanf exits, then Ptrcheck "forgets" everything
it knows about the instructions in _IO_vfscanf.  It has to do that so
that later calls to _IO_vfscanf are not checked against the buffers
that earlier calls wrote to.
Comment 4 Julian Seward 2010-06-30 12:29:41 UTC
(In reply to comment #2)
> I have not really looked at the sscanf implementation (and my doctor says I
> should not as it won't be good for my heart), but if the pointer jumps from one
> arg to the other, shouldn't ptrcheck somehow get that it has been
> re-initialized from a new input parameter then?

The problem is ..

The basic assumption is: if a memory referencing insn accesses a stack
or global array, then it will continue to access that same array until
the containing stack frame exits.  See

http://www.valgrind.org/docs/manual/pc-manual.html#pc-manual.how-works.sg-checks

So .. how, in this case, can it know that the insn which copies data
into %s destinations is switching between destination arrays?  I don't
see a way to do this.


> Now that I think of it, the almost 100 suppressions that I wrote for our
> software seem to be of a similar kind. We have a ptr variable that is always
> re-used for pointing to an element determine within the looping.

Yes.  See bullet point 5 of
http://www.valgrind.org/docs/manual/pc-manual.html#pc-manual.limitations


> It would really be nice if there could be a fix for this since it is a really
> tedious thing to always write suppressions, since for every new case you have
> to determine whether it is not maybe a bug, and

Maybe it could be improved with better heuristics, but I couldn't
think of any during development.  If you have any ideas ...


> with our old legacy code here
> we find quite some bugs with ptrcheck that no other tool shows us...

Wow.  This is the first time I heard that Ptrcheck finds real bugs.
That's great.

Are these from the stack/global array checking only?  I have
(uncommitted) a cut-down version which just does stack/global checks,
and omits the heap checks, since Memcheck does those faster.

In fact, looking at the stack/global array checking code, I think I
did a pretty stupid implementation.  It could be redone to be much
faster, but until now I never put much effort into improving it
because I assumed the tool as a whole is not very useful (didn't find
many bugs in the testing I did.)
Comment 5 Julian Seward 2010-07-30 17:35:11 UTC
Closing this as WONTFIX.  I actually mean CANTFIX -- I have no idea
how to, as per comment #4.  Suggestions welcomed.
Comment 6 Lars Kr. Lundin 2013-11-03 18:53:24 UTC
Similar to the write warning in sscanf with "%s %s" as format, sprintf will cause a read warning on the same format with the same char arrays, e.g.

==4995== Invalid read of size 1
==4995==    at 0x52A9040: vfprintf (vfprintf.c:1655)
==4995==    by 0x52CDAA3: vsprintf (iovsprintf.c:42)
==4995==    by 0x52B01D6: sprintf (sprintf.c:32)
==4995==    by 0x4194A8: xf_do_job (xphireengine.c:1911)
==4995==    by 0x4119C6: xf_main (xphireengine.c:642)
==4995==    by 0x4110FB: main (xphireengine.c:490)
==4995==  Address 0xffeffd800 expected vs actual:
==4995==  Expected: stack array "tmsg" of size 80 in frame 3 back from here
==4995==  Actual:   stack array "t2msg" of size 80 in frame 3 back from here
==4995==  Actual:   is 0 after Expected

(This comment is added in the hope that it can help users find false positives from sgcheck).