I am currently studying C implementations of Linux terminal commands in class but I can't seem to get the following example to run on my machine. I'm using the latest distro of Ubuntu. It will compile but with a warning. "assignment makes pointer from integer without a cast" it refers to the line with a=ctime(&n->ut_time); I found this code online. It is suppose to replicate the terminal command "Who" to display system users. I'm simply trying to study how it works but I cant seem to get it to run. Any help or explanations would be appreciated.
#include<stdio.h>
#include<sys/utsname.h>
#include<utmp.h>
int main(void)
{
struct utmp *n;
char *a;
int i;
setutent();
n=getutent();
while(n!=NULL)
{
if(n->ut_type==7)
{
printf("%-9s",n->ut_user);
printf("%-12s",n->ut_line);
a=ctime(&n->ut_time);
printf(" ");
for(i=4;i<16;i++)
{
printf("%c",a[i]);
}
printf(" (");
printf("%s",n->ut_host);
printf(")\n");
}
n=getutent();
}
}
Transferring comment to answer
The compiler is telling you you've not got #include <time.h> so ctime() is assumed to return an int and not a char *. All hell breaks loose (segmentation faults, etc) because you are not paying attention to the compiler warnings.
Remember, while you're learning C, the compiler knows a lot more about C than you do. Its warnings should be heeded. (When you know C reasonably well, you still pay attention to the compiler warnings - and make the code compile cleanly without warnings. I use gcc -Wall -Wextra -Werror and some extra options — usually -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Wold-style-declaration; sometimes -Wshadow, -pedantic; and occasionally a few others.)
Related
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.
Disclaimer:
The code I'm sharing here is an isolated version of the real code, nevertheless reproduce the same behaviour.
The following code compiled using gcc 5.4.0 with optimisation enabled on Ubuntu 16.04, when executed, generates a infinite loop:
#include <stdio.h>
void *loop(char *filename){
int counter = 10;
int level = 0;
char *filenames[10];
filenames[0] = filename;
while (counter-- > 0) {
level++;
if (level > 10) {
break;
}
printf("Level %d - MAX_LEVELS %d\n", level, 10);
filenames[level] = filename;
}
return NULL;
}
int main(int argc, char *argv[]) {
loop(argv[0]);
}
The compiler versions:
gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
The compilation command used:
gcc infinite.c -O2 -o infinite
I know that it is caused by the optimisation flag "-02" because it doesn't happen without it. I also Know that adding volatile to the variable "level" also fix the error. But I can't add this keyword to all my variables.
My question is, why this happen and what can I do to avoid it in the future?
Is there any gcc flag that still optimise the code at a similar level of -O2 without this kind of problem?
You've found an example of undefined behaviour causing the optimizer to do something unexpected! GCC can see that if the loop runs 10 times then there is undefined behaviour.
This code will write filenames[1] through filenames[10] i.e. the 2nd through 11th elements of filenames. However filenames is 10 elements long, so the last one is undefined behaviour.
Because you aren't allowed to have undefined behaviour, it can assume that the loop will stop some other way before it gets to 10 (perhaps you have a modified version of printf that will call exit?).
And it sees that if the loop is going to stop anyway before it gets to 10, there is no point having code to make it only run 10 times. So it removes that code.
To prevent this optimization, you can use "-fno-aggressive-loop-optimizations".
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.
So I've been trying to get this code to compile using a gcc compiler using c (I found lots of references to c++ but none to c so I asked this) I kept on getting the error Badly placed ()'s every time I go to run the program. So I simplified it to a very simple Hello World test program and I still get the same error.
What could be causing this error?
#include <stdio.h>
int main(int argc, int* argv[])
{
printf("Hello World\n");
return 0;
}
It seems that you are not trying to execute the compiled binary, but that you have a system that runs a tcsh and you are feeding the C source code directly into that shell:
> tcsh /tmp/badly.c
Badly placed ()'s.
A C program must first be compiled to a binary (here: /tmp/badly), and then you have to execute that binary:
> gcc /tmp/badly.c -Wall -o /tmp/badly
/tmp/badly.c:3:5: warning: second argument of 'main' should be 'char **' [-Wmain]
> /tmp/badly
Hello World
As ouah already noticed in his answer, with the -Wall argument to gcc you also get the informative message that the parameters of your main function are wrong.
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.