T
Thad Smith
Dave said:Static analysis can't catch all problems of this sort. Consider:
--------
/*Somewhere*/
void foo(int *a,int *b)
{
*a=(*b)++;
}
/*Somewhere else*/
void bar(int x)
{
/*Do some stuff, including:*/
foo(&x,&x);
}
--------
Good point, Dave. While that can be done by a diagnostic virtual
machine, it could also be done by a diagnostic compiler generating
native code, which would insert explicit tests in the generated code for
duplicate pointers which must be different for well-defined behavior.
The example code for the VM (snipped) required that the compiler
identify sequence points, a diagnostic-only feature. If the compiler
generates diagnostic code, it could generate tests in native code (or
function calls) as well.
In your example of pointers passed to a function, detection of the error
depends on the run-time behavior of the program. If pointers are chosen
in an apparently random manner, the code must actually execute the case
in which the pointers match in order to determine an error. I suppose
it is possible to make a static/dynamic diagnostic system that takes the
approach that if it can't prove that the pointers are different, and
there are multiple updates through the pointer pairs between sequence
points, it would issue a warning. It would then be up to the programmer
to prove to the compiler/interpreter than the pointers can't match or to
recode such that it doesn't matter.
--------
char buf[10];
char *str=some_string_pointer;
buf[0]='\0';
/*We want to warn about this (claimed buffer size larger than actual
destination buffer) even if str fits into buf
*/
strncat(buf,str,20);
--------
If a function gets a buffer size argument larger than the real buffer
size, that's a bug, even if what ends up being written into that buffer
does fit; we want to catch that bug as soon as possible even if the
behavior is actually well-defined until a user's cat starts sleeping
on the keyboard.
To protect against overflow, we really want
strncat(buf, str, sizeof(buf)-strlen(buf)-1);
That can be detected with static analysis, in some cases, as well. To
check dynamically for potential errors, we would verify that
len <= sizeof(buf)-strlen(buf)-1,
assuming that debug_strncat() has access to sizeof(buf).
If we're storing pointers as segment-offset-size, then a little bit of
implementation magic will give it the appropriate size.
Note that this isn't directly available to the code the programmer sees if
(as is likely) the buffer isn't a local or global array; buffers passed in
(as a pointer) from elsewhere or obtained from malloc are the ones most
likely to have mismatched sizes, and sizeof won't give the size of the
buffer in those cases.
I lean more towards a diagnostic compiler and library for catching these
types of errors. So pointers _could_ be implemented as an
(address,length) pair, which would include values returned by malloc.
This would allow a diagnostic implementation of strncat() to do the
checking mentioned above.
Once you're doing aggressive dynamic checking in the implementation's
runtime environment anyways, it's much simpler for all concerned to let
the library function check the sizes; it knows how buffer size and size
arguments are related, and has access to implementation magic to get at
the information it needs to check them.
Agreed.
Thad