I am trying to execute clang static analyzer (version 3.8) on some of the examples shown in its documentation (https://clang-analyzer.llvm.org/alpha_checks.html#security_alpha_checkers).
I created a small C program, as follows:
// note: requires alpha.security.taint check turned on.
void test() {
char s[] = "abc";
int x = getchar();
char c = s[x]; // warn: index is tainted
}
I am executing following command to analyze the above code:
/usr/lib/llvm-3.8/bin/scan-build -enable-checker alpha.security.taint.TaintPropagation clang -c example.c
The above command generates following error report:
scan-build: Using '/usr/lib/llvm-3.8/bin/clang' for static analysis
example.c:5:8: warning: Value stored to 'c' during its initialization is never read
char c = s[x]; // warn: index is tainted
^ ~~~~
1 warning generated.
scan-build: 1 bug found.
scan-build: Run 'scan-view /tmp/scan-build-2018-04-09-143549-15413-1' to examine bug reports.
I was expecting clang SA will complain about possible buffer overflow and buffer underflow at line 5, but it seems like taint analysis is not performed.
Can someone please suggest how to enable "alpha.security.taint" check?
To get a warning when using a tainted array index, you have to enable alpha.security.ArrayBoundV2 and alpha.security.taint.TaintPropagation:
$ ~/bld/llvm-project/build/bin/scan-build -enable-checker \
alpha.security.taint.TaintPropagation,alpha.security.ArrayBoundV2 \
gcc -c taint2.c
scan-build: Using '/home/scott/bld/llvm-project/build/bin/clang-9' for static analysis
taint2.c:6:10: warning: Value stored to 'c' during its initialization is never read
char c = s[x]; // warn: index is tainted
^ ~~~~
taint2.c:6:14: warning: Out of bound memory access (index is tainted)
char c = s[x]; // warn: index is tainted
^~~~
2 warnings generated.
scan-build: 2 bugs found.
scan-build: Run 'scan-view /tmp/scan-build-2019-09-11-204837-97704-1' to examine bug reports.
The TaintPropagation checker reports some things by itself, for example, passing tainted data to system(). It also exports tainting information for other checkers to use.
(I discovered this information primarily by looking at the source code, and secondarily through trial and error. The documentation isn't much help.)
Related
I'm trying to compile a large project (https://github.com/ESCOMP/CTSM). I would like to compile it as-is, without editing the code, if possible (it is known to build successfully on many platforms).
I'm using gcc (SUSE Linux) 11.2.1. and I get
In function ncmp : /run/media/dominic/hdbtrfs/dominic/git/ESCOMP/CTSM/cime/src/share/timing/gptl.c:4069:1: error: control reaches end of non-void function [-Werror=return-type]
from the following function. I believe that earlier versions of gcc only gives a warning rather than an error in this instance.
/*
** ncmp: compares values of memory adresses pointed to by a pointer. for use with qsort
*/
static int ncmp( const void *pa, const void *pb )
{
static const char *thisfunc = "GPTLsetoption";
const char** x = (const char**)pa;
const char** y = (const char**)pb;
if( *x > *y )
return 1;
if( *x < *y )
return -1;
if( *x == *y )
GPTLerror("%s: shared memory address between timers\n", thisfunc);
}
I expect this can be fixed by inserting a spurious return statement at the end of the function, but since I am interested to try to build an unmodified version of the code (I'm not currently a contributor on the project, so can't push changes upstream) I'm wondering if there's a workaround to convert this error to a warning using compiler flags?
As requested here is the gcc call that is genertaed by the makefile:
mpicc -c -I/run/media/dominic/hdbtrfs/dominic/git/ESCOMP
/CTSM/cime/src/share/timing -std=gnu99 -O -DCESMCOUPLED
-DFORTRANUNDERSCORE -DNO_R16 -DCPRGNU -DCESMCOUPLED
-DFORTRANUNDERSCORE -DNO_R16 -DCPRGNU -DNUOPC_INTERFACE
-DHAVE_MPI /run/media/dominic/hdbtrfs/dominic/git/ESCOMP
/CTSM/cime/src/share/timing/gptl.c
I'm wondering if there's a workaround to convert this error to a warning using compiler flags?
I would expect the -Wno-error option to have that effect. It should also be possible to narrow the scope of that to the specific diagnostic you report, but beware: there is no way with command-line options alone to narrow the effect to this particular instance of the issue.
Addendum
The question having been edited to show that the diagnostic category is return-type, I can say that one would use -Wno-error=return-type to make all diagnostics of this type warnings instead of errors.
-Werror elevates all warnings to errors. You can turn reverse this for specific warnings with -Wno-error=WarningName, such as -Wno-error=return-type, as is clearly documented in the GCC documentation for warning options:
This switch [-Werror=] takes a negative form, to be used to negate -Werror for specific warnings; for example -Wno-error=switch makes -Wswitch warnings not be errors, even when -Werror is in effect.
https://github.com/noyesno/awka
For the above repo, I got the following error when I try to compile it on macOS (gcc is just clang). I have no idea how to fix the problem according to the error message. It compiles fine on Linux.
I also tried the real gcc from Homebrew to compile the package. It also show the same error. How can I fix this problem on macOS?
$ ./configure
$ make
...
gcc -O -Dawka_LIBDIR=\"/usr/local/lib\" -Dawka_INCDIR=\"/usr/local/include\" -c -o print.o print.c
print.c:52:11: error: expected parameter declarator
int PROTO(sprintf, (char *, const char *,...)) ;
^
print.c:52:11: error: expected ')'
print.c:52:11: note: to match this '('
print.c:52:11: error: conflicting types for '__builtin___sprintf_chk'
int PROTO(sprintf, (char *, const char *,...)) ;
^
print.c:52:11: note: '__builtin___sprintf_chk' is a builtin with type 'int (char *, int, unsigned long, const char *, ...)'
3 errors generated.
make[1]: *** [<builtin>: print.o] Error 1
make[1]: Leaving directory '/private/tmp/awka/awka'
make: *** [Makefile:48: awka_exe] Error 2
I'm not going to spend ages on this, but it looks as though configure is gripping stdio.h looking for sprintf. It is unable to find it in the header, and so it adds the #define:
NO_SPRINTF_IN_STDIO
which it sets to 1, and uses it to add its own prototype for sprintf. Unfortunately, this appears to be a macro in this case, which replaces sprintf with '__builtin___sprintf_chk' instead (which has additional string length checks by the looks of it).
Possible solutions:
Comment out the line in print.c, and make sure stdio.h is included somewhere.
After running configure, search for where it defines NO_SPRINTF_IN_STDIO and set that var to 1?
Fix the configure to so a more rigorous test?
This is a 40-line MCVE (Minimal, Complete, Verifiable Example) — or something close to minimal — cut down from a 1675 line source file that originally included 32 headers (and most of those included multiple other headers — compiling it with gcc -H lists 464 headers from the project and the system, many of them several times). That file is working code that previously compiled without warnings (GCC 8.3.0), but not with GCC 9.1.0. All structure, function, type, variable names have been changed.
pf31.c
#include <string.h>
enum { SERVERNAME_LEN = 128 };
typedef struct ServerQueue
{
char server_name[SERVERNAME_LEN + 1];
struct ServerQueue *next;
} ServerQueue;
extern int function_under_test(char *servername);
#ifdef SUPPRESS_BUG
extern int function_using_name(char *name);
#endif /* SUPPRESS_BUG */
extern int GetServerQueue(const char *servername, ServerQueue *queue);
int
function_under_test(char *servername)
{
ServerQueue queue;
char name[SERVERNAME_LEN + 1];
if (GetServerQueue(servername, &queue) != 0)
return -1;
char *name_in_queue = queue.server_name;
if (name_in_queue)
strncpy(name, name_in_queue, SERVERNAME_LEN);
else
strncpy(name, servername, SERVERNAME_LEN);
name[SERVERNAME_LEN] = '\0';
#ifdef SUPPRESS_BUG
return function_using_name(name);
#else
return 0;
#endif /* SUPPRESS_BUG */
}
Compilation
When compiled using GCC 9.1.0 (on a Mac running macOS 10.14.5 Mojave, or on a Linux VM running RedHat 5.x — don't ask!), with the option -DSUPPRESS_BUG I get no error, but with the option -USUPPRESS_BUG, I get an error:
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -DSUPPRESS_BUG -c pf31.c
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -USUPPRESS_BUG -c pf31.c
In file included from /usr/include/string.h:417,
from pf31.c:1:
pf31.c: In function ‘function_under_test’:
pf31.c:30:9: error: ‘__builtin_strncpy’ output may be truncated copying 128 bytes from a string of length 128 [-Werror=stringop-truncation]
30 | strncpy(name, name_in_queue, SERVERNAME_LEN);
| ^~~~~~~
cc1: all warnings being treated as errors
$
When I compile using GCC 8.3.0, I get no errors reported.
Question
Two sides of one question:
Why does GCC 9.1.0 complain about the use of strncpy() when the code is compiled with -USUPPRESS_BUG?
Why doesn't it complain when the code is compiled with -DSUPPRESS_BUG?
Corollary: is there a way to work around this unwanted warning that works with older GCC versions as well as 9.1.0. I've not yet found one. There's also a strong element of "I don't think it should be necessary, because this is using strncpy() to limit the amount of data copied, which is what it is designed for".
Another variant
I have another non-erroring variant, changing the signature of the function_under_test() — here's a set of diffs:
11c11
< extern int function_under_test(char *servername);
---
> extern int function_under_test(char *servername, ServerQueue *queue);
20c20
< function_under_test(char *servername)
---
> function_under_test(char *servername, ServerQueue *queue)
22d21
< ServerQueue queue;
25c24
< if (GetServerQueue(servername, &queue) != 0)
---
> if (GetServerQueue(servername, queue) != 0)
27c26
< char *name_in_queue = queue.server_name;
---
> char *name_in_queue = queue->server_name;
This compiles cleanly regardless of whether SUPPRESS_BUG is defined or not.
As you can guess from the SUPPRESS_BUG terminology, I'm tending towards the view that this is bug in GCC, but I'm kinda cautious about claiming it is one just yet.
More about the the original code: the function itself was 540 lines long; the strncpy() block occurs about 170 lines into the function; the variable corresponding to name was used further down the function in a number of function call, some of which take name as an argument and supply a return value for the function. This corresponds more to the -DSUPPRESS_BUG code, except that in the 'real code', the bug is not suppressed.
This is a GCC bug tracked as PR88780. According to Martin's comment, this warning did not exist prior to GCC 8.
GCC is shipped with this known bug, as it is not deemed release-critical.
To be honest, I am not 100% sure it is the bug. The point is, there are known false-positives. If you feel like helping the GCC project, you can find the most appropriate bug among strncpy / Wstringop-truncation bugs and post your example there. It would be more helpful if you minimized it further (say, with creduce); minimizing the compile string is also appreciated (that would be rather trivial, I guess).
Several compilation warnings related to strncpy were found in GCC 9.0 and reported here and here.
One of them is the error mentioned in the question which seems to occur in the file string_fortified.h:
/usr/include/bits/string_fortified.h:106:10: warning: ‘__builtin_strncpy’ output may be truncated copying 16 bytes from a string of length 16 [-Wstringop-truncation]
106 | return __builtin___strncpy_chk (__dest, __src, __len, __bos (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The response to this was given on April 15, 2019 was:
Thank you for the report, however as GCC 9 still under development. We do not see the above errors in the current stable GCC 7.4 or GCC 8.3. We appreciate the advanced notice, and will accept PRs to fix issues against GCC 9, but for now our target compiler is gcc stable.
So I believe the errors are probably a result of versions 9 and 9.1 being not stable versions. Hopefully they will be eliminated when these versions become stable.
Can I detect a possible segmentation fault at compile-time?
I understand the circumstance of a segmentation fault. But I am curious if GCC as a compiler has some flags to check for the basic scenarios resulting in segmentation faults.
This would help enormously to take precautions before releasing a library.
Can I detect a possible segmentation fault at compile time?
Sometimes, but no, you can't flawlessly detect these scenarios at compile time. Consider the general case in this C code:
volatile extern int mem[];
void foo (int access)
{
mem[access];
}
A compiler would be too noisy if it were to warn about this access at compile time, the code is valid C and a warning is, in general, inappropriate. Static analysis can't do anything with this code unless you have a mechanism for whole-program or link-time analysis.
An additonal optimization flag in GCC 4.8 which can sometimes catch a few out-of-bounds access in loops is `-faggressive-loop-optimizations'. This found a number of issues in the SPEC benchmark suite last year (http://blog.regehr.org/archives/918)
I understand the circumstance of segmentation fault. But i am curious if GCC as a compiler has some flags to check for the basic scenarios resulting in segmention faults.
GCC 4.8 comes with an address sanitizer which can help catch some of these run-time only issues (out of bounds/use-after-free bugs). You can use it with
-fsanitize=address.
http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Debugging-Options.html#Debugging-Options
GCC 4.9 (which will be released within the next few months) comes with an undefined behaviour sanitizer and more aggressive optimization of NULL pointer paths, which might help you catch some more issues. When it comes, it will be available with -fsanitize=undefined
http://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options
Note however that neither of these are "compile-time" solutions, they both rely on instrumenting the binary and performing run-time checks.
Yes, there are ways of detecting some faults that may cause runtime errors such as segmentation faults. Those ways are called warnings. Many warnings messages are places where you have undefined behavior, and undefined behavior is often the leading cause of runtime crashes.
When I build, I always use the -Wall, -Wextra and -pedantic flags.
Other than that, there are really no good way of detecting all places that may cause segmentation faults (or other runtime errors), except strict coding guidelines, code reviews and plenty of testing.
gcc -Wall -Werror as mention by Joachim Pileborg are very good ideas. You could also use another compiler maybe. some reports more memory issues. I think you can not do a lot more at compile time.
At running time, I highly recommend Valgrind, which is a amazing tool for detecting memory issues. (don't forget to compile with the -g option)
Can I detect a possible segmentation fault at compile-time?
Yes, it is possible. Unfortunately, it is very limited what the compiler can do. Here is a buggy code example and the output from gcc and clang:
#include <stdlib.h>
int main() {
int a[4];
int x, y;
a[5]=1;
if(x)
y = 5;
x = a[y];
int* p = malloc(3*sizeof(int));
p[5] = 0;
free(p);
free(p);
}
For this buggy code, gcc -Wall -Wextra corrupt.c gives
corrupt.c: In function ‘main’:
corrupt.c:13:1: warning: control reaches end of non-void function [-Wreturn-type]
corrupt.c:6:7: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
clang catches more:
corrupt.c:5:5: warning: array index 5 is past the end of the array (which contains 4 elements) [-Warray-bounds]
a[5]=1;
^ ~
corrupt.c:3:5: note: array 'a' declared here
int a[4];
^
corrupt.c:6:8: warning: variable 'y' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
if(x)
^
corrupt.c:8:11: note: uninitialized use occurs here
x = a[y];
^
corrupt.c:6:5: note: remove the 'if' if its condition is always true
if(x)
^~~~~
corrupt.c:4:13: note: initialize the variable 'y' to silence this warning
int x, y;
^
= 0
corrupt.c:6:8: warning: variable 'x' is uninitialized when used here [-Wuninitialized]
if(x)
^
corrupt.c:4:10: note: initialize the variable 'x' to silence this warning
int x, y;
^
= 0
3 warnings generated.
I believe the above code example gives you insight what to expect. (Even though I tried, I could not get the static analyzer in clang to work.)
This would help enormously to take precautions before releasing a library.
As you can see above, it won't be an enormous help, unfortunately. I can only confirm that instrumentation is currently the best way to debug your code. Here is another code example:
#include <stdlib.h>
int main() {
int* p = malloc(3*sizeof(int));
p[5] = 0; /* line 4 */
free(p);
p[1]=42; /* line 6 */
free(p); /* line 7 */
}
Compiled as clang -O0 -fsanitize=address -g -Weverything memsen.c. (GCC 4.8 also has address santizier but I only have gcc 4.7.2.) The output:
==3476==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000f004 at pc 0x4887a7 bp 0x7fff9544be30 sp 0x7fff9544be28
WRITE of size 4 at 0x60200000f004 thread T0
#0 0x4887a6 in main /home/ali/tmp/memsen.c:4
[...]
Awesome, we know what went wrong (heap-buffer-overflow) and where (in main /home/ali/tmp/memsen.c:4). Now, I comment out line 4 and get:
==3481==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000eff4 at pc 0x4887d7 bp 0x7fff27a00d50 sp 0x7fff27a00d48
WRITE of size 4 at 0x60200000eff4 thread T0
#0 0x4887d6 in main /home/ali/tmp/memsen.c:6
[...]
Again, we see what went wrong and where. Finally, I comment out line 6.
==3486==ERROR: AddressSanitizer: attempting double-free on 0x60200000eff0 in thread T0:
#0 0x46dba1 in free /home/ali/llvm/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:65
#1 0x48878c in main /home/ali/tmp/memsen.c:7
[...]
Also caught the problem.
If your code has tests, or at least you can run your code with different inputs on your machine before releasing the library, you could probably track down a significant portion of the bugs. Unfortunately, it is not a compile-time solution and you probably don't want to release instrumented code (code compiled with -fsanitize=* flag). So if the user runs your code with an input that triggers a bug, the program will still crash with a segmentation fault.
We want to start using -Wall -Werror on a large project.
Due to the size, this change has to be phased, and we want to start with the most important warnings first.
The best way to do it seems to be using -Wall -Werror, with exceptions for specific warnings. The exceptional warnings are those which we have a lot of (so fixing them all is hard and risky), and we don't consider them very dangerous.
I'm not saying we don't want to fix all these warnings - just not on the first phase.
I know two ways to exclude a warning from -Werror - the best is -Wno-error=xxx, and if it doesn't work - -Wno-xxx (of course, we prefer to see the warning and ignore it, rather than hide it).
My problem is with warnings which are enabled by default, and don't have a -Wxxx flag related to them. I couldn't find any way to alllow them when -Werror is used.
I'm specifically concerned about two specific warnings. Here's a program that exhibits them and the compiler output:
#include <stdio.h>
void f(int *p) { printf("%p\n", p); }
int main(int argc, char *argv[]) {
const int *p = NULL;
const unsigned int *q = NULL;
f(p); /* Line 7: p is const, f expects non const */
if (p == q) { /* Line 8: p is signed, q is unsigned */
printf("Both NULL\n");
}
return 0;
}
% gcc warn.c
warn.c: In function 'main':
warn.c:7: warning: passing argument 1 of 'f' discards qualifiers from pointer target type
warn.c:8: warning: comparison of distinct pointer types lacks a cast
I know the best solution is to fix these warnings, but it's much easier said than done. In order for this change to be successful, we have to do this phased, and can't do too many changes at once.
Any suggestions?
Thanks.
What about phasing on a compilation unit/module/library basis instead of per warning? Is triggering a subtarget compilation an option (a good-enough build system in place)?
It might be folly, but ...
Why not a simple grep ?
something like
gcc teste.c 2>&1 | grep -v 'comparison of distinct' | grep -v 'some_other_string'
You probably want to hide these greps in a script, and call the script from your makefile instead of gcc
According to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43245 it will be "-Wdiscarded-qualifiers", but since the bug-fixed entry is from May 1, 2014, the gcc compiler you are using might not support it.