2

Is there a dynamic checking utility that can flag the following bug? Valgrind cannot. Can Purify or Insure++? This is on Linux Ubuntu latest version.

struct A {
    char buff1[8];
    int jj;
    char buff2[8];
    int ii;
    char buff3[8];
} a;

main(int argc, char *args[])
{
    // Set intermediate fields to known flag value
    a.ii = a.jj = 0xdeadbeef;

    // Write 8 char string into 8 byte buffer - null will overflow into neighboring int field. ERROR
    sprintf(a.buff2, "ABCDEFGH");
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Robert R Evans
  • 177
  • 1
  • 11
  • 1
    snprintf? http://www.cplusplus.com/reference/cstdio/snprintf/ – user3528438 Mar 14 '16 at 20:49
  • This is an astute and helpful (and concise!) comment that does not answer my question, which I think says more about my question than the response! Your suggestion is the correct way to write the code. Unfortunately, I have 100,000 of legacy code, that now yields some 500 Klocwork issues, and would like to find out if any of this crap actually causes run-time errors. We're going to just grit our teeth and plunge into the refactoring. – Robert R Evans Mar 15 '16 at 14:37
  • 3
    In my experience Purify will do just what you want - along with checking stack variable usage.. When IBM owned it, you used to be able to get a 30-day demo license. Also, one of the tools in Oracle's Studio compiler suite *may* also be able to find such a bug and it's free, although using the Oracle tools in my experience has a much greater performance impact than using Purify. Also, my experience with the Oracle tools is on Solaris, which has much better instrumentation than Linux does. – Andrew Henle Mar 16 '16 at 08:35
  • @Andrew Henle I am pretty sure such a tool doesn't exist for reasons mentioned in my answer – tofro Mar 17 '16 at 07:54
  • @tofro Do you have actual experience using the tools I mentioned? [Read this](http://www.ing.iac.es/~docs/external/purify/purify-4_1.pdf): *In addition to detecting access errors in dynamic memory, Purify detects references beyond the boundaries of data in global variables and static variables, that is, data allocated statically at link-time as opposed to dynamically at run time.* I'll even post that as an answer. – Andrew Henle Mar 17 '16 at 10:55
  • Yes, I do. I have been working with Purify for years. My point is: There is *legal and unavoidable cases* for writing over structure bounds. Maybe I did not make myself clear enough. – tofro Mar 17 '16 at 11:05
  • @tofro *There is legal and unavoidable cases for writing over structure bounds.* Ouch. That would be some difficult code to debug, and yes, no tool could do it. Still, given the OP's statement of `100,000 [lines] of legacy code` that need to be debugged, I'd opine that tools such as Purity are **required**. You wouldn't ask a carpenter that only uses a 16-oz claw hammer and a 12" manual miter saw in lieu of modern power tools to build your house - the productivity cost would be prohibitive. Yet developers at $100/hr or more are supposed to eschew tools like Purify over cost? Stupid. – Andrew Henle Mar 17 '16 at 11:36
  • 1
    @Andrew Henle You might want to read my answer below on why I say that. And I didn't talk about whether such tools are useful or not. They are. So the rest of your statement is just preaching to the converted... The OP did, however, ask a very specific question on what tools like Purify can and cannot do. And, to my knowledge, they can't. – tofro Mar 17 '16 at 12:42

4 Answers4

2

Not to my knowledge. Most (or rather: all?) memory verification tools work in a way that embeds read- and write protected pages as guard zones between and around variables in order to provoke traps on accesses beyond the legally allocated areas.

Without severely disturbing structure alignment and integrity, this cannot be easily done in the middle of a structure.

EDIT: Another point is: There is constructs where writing over structure member bounds is perfectly legal and the only reasonable possibility to achieve what you want. One example is copying structures to the heap:

struct x orig, *copy;

orig.a = 100;
strcpy (orig.str, "Test");

copy = malloc (sizeof (struct x));
memcpy (copy, &orig, sizeof (struct x));

This writes beyond structure member bounds as well, but is the only reasonable (and perfectly legal) way to get the structure onto the heap (apart from tedious and slow member-wise copy).

Another example would be

p = malloc (NUM_STRUCTS * sizeof (struct x));
memset (p, NUM_STRUCTS * sizeof (struct x), 0);

This is a perfectly valid constuct that allows you to clear an array of structures on the heap - And it does not even write aross internal struct boundaries, but also between structs.

In some sense, even calloc() would write beyond structure member bounds....

And, as a definite answer from the (admittedly older) Purify User Manual I happend to find in one of my desk drawers:

Purify detects array bounds errors in arrays within C structures only when the access extends beyond the entire structure

That counts as a "no" for me.

tofro
  • 5,640
  • 14
  • 31
  • I take your point about "disturbing structure alignment". Such a hypothetical tool would only be applied to code which is indifferent to alignment or size. – Robert R Evans Mar 15 '16 at 14:32
1

Does it have to be a dynamic tool? The only tool I know which will detect the above scenario is Coverity, a static tool from Synopsis. For the above case it will produce a report on the form of:

Error: OVERRUN:
...
sprintf_overrun: "sprintf" will overrun its first argument "a.buff2" which can accommodate 8 bytes.  The number of bytes written may be 9 bytes, including the terminating null.
FrodeTennebo
  • 531
  • 3
  • 13
-1

Compiling with all warnings enabled is a good start:

chqrlie@mac ~/dev/stackoverflow > clang -O3 -std=c11 -Weverything -lm -o 35996676 35996676.c
35996676.c:9:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
main(int argc, char *args[])
^
35996676.c:12:19: warning: implicit conversion changes signedness: 'unsigned int' to 'int' [-Wsign-conversi
    a.ii = a.jj = 0xdeadbeef;
                ~ ^~~~~~~~~~
35996676.c:15:5: warning: implicitly declaring library function 'sprintf' with type 'int (char *, const cha
    sprintf(a.buff2, "ABCDEFGH");
    ^
35996676.c:15:5: note: include the header <stdio.h> or explicitly provide a declaration for 'sprintf'
35996676.c:9:10: warning: unused parameter 'argc' [-Wunused-parameter]
main(int argc, char *args[])
         ^
35996676.c:9:22: warning: unused parameter 'args' [-Wunused-parameter]
main(int argc, char *args[])
                     ^
35996676.c:7:3: warning: no previous extern declaration for non-static variable 'a' [-Wmissing-variable-dec
} a;
  ^
6 warnings generated.

Fixing these with simple patches, and adding extra obvious bugs:

#include <stdio.h>
#include <string.h>

static struct A {
    char buff1[8];
    unsigned int jj;
    char buff2[8];
    unsigned int ii;
    char buff3[8];
} a;

int main(void) {
    // Set intermediate fields to known flag value
    a.ii = a.jj = 0xdeadbeef;

    // Write 8 char string into 8 byte buffer - null will overflow into neighboring int field. ERROR
    sprintf(a.buff1, "ABCDEFGH");
    strcpy(a.buff2, "ABCDEFGH");
    sprintf(a.buff3, "%s", "ABCDEFGH");

    return 0;
}

Compiles with no warnings, is spite of the obvious sprintf and strcpy bugs that a simplistic static analysis should have caught.

gcc with -Wall -W -Wextra does not see anything wrong either.

It is recommended to use snprintf instead of sprintf, but that would not prevent the strcpy problem. strcpy is unsafe in the general case, but with a string literal as a source, the compiler should definitely complain.

You should try Frama-c, a powerful source code analysis framework available in open source from http://frama-c.com/

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Thx. See my comment to user3528438 above. – Robert R Evans Mar 15 '16 at 16:56
  • 1
    A dynamic tool would indeed need to use some very advanced mechanism to detect such buffer overflows. If you are able to recompile the whole project, specific padding of some of the structure members would be possible and would allow such detection. Fat pointers or some other bound checking techniques could help too. You can try `tinycc` with boundchecking enabled. But static analysis and defensive programming will help locate a lot of problems. – chqrlie Mar 15 '16 at 17:10
-2

In my experience, Purify may do what you want. This is an excerpt from an older Purify user's manual posted to the internet:

How Purify checks statically allocated memory

In addition to detecting access errors in dynamic memory, Purify detects references beyond the boundaries of data in global variables and static variables, that is, data allocated statically at link-time as opposed to dynamically at run time.

Here is an example of data that is handled by the static checking feature:

int array[10];
main() {
    array[11] = 1;
}

In this example, Purify reports an ABW error at the assignment to array[11] because it is 4 bytes beyond the end of the array.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • And that is *not* one of the cases the original question was asking for. – tofro Mar 17 '16 at 11:06
  • @tofro If Purify from almost 20 years ago can find problems accessing static variables like `int x[10];`, it should also be able to find problems accessing `struct A { int x[10]; ... } a;`. They're both just locations in the same type of memory. – Andrew Henle Mar 17 '16 at 11:46
  • 1
    Note: I am *not* talking about static or dynamic variables and the OP wasn't as well. You should read my answer below on why I think a tool *shouldn't* flag such out-of bounds - because they are perfectly legit – tofro Mar 17 '16 at 12:36
  • 1
    Maybe that one helps (From the *Purify* manual): "Purify detects array bounds errors in arrays within C structures only when the access extends beyond the entire structure" – tofro Mar 17 '16 at 13:06