void f();
int main(int argc, char** argv)
{
if (f)
{
// other code
}
}
With VS2017, the linker complaint about unsolved external symbol, while it works with GCC. According to C99 spec, is it valid? Or it's implementation detail?
C standard requires that every symbol should be defined exactly once in a correct program, but does not require any diagnostic if the rule is not observed. So if you declare a function that is never defined in any compilation unit any use of that function is beyond C specification.
The gcc compiler is known to have plenty of extensions, some of which are also accepted by clang. If you know that you will only use gcc, you can use them, if you want to write portable programs you should not.
GCC regards a declared function as always existent, even with -O0, and therefore optimizes the code to:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Y\n");
}
If you add common warning options to the compiler command line, GCC will warn you about this, too.
It's common to check if the weak function is defined(usually user
defined callback) in embedded code.
No, it is never done as it cannot be done. Your code may have some pointers to functions and you can check if those pointers are not NULL (ie have been assigned with some function pointer references, but cant check if those references are correct).
Related
I just resolved an absolute headbanger of a problem, and the issue was so simple, yet so elusive. So frustratingly hidden behind a lack of compiler feedback and an excess of compiler complacency (which is rare!). During writing this post, I found a few similar questions, but none that quite match my scenario.
Calling method without typeless argument produces a compiler error when the definition includes strongly typed args.
Why does gcc allow arguments to be passed to a function defined to be with no arguments? and C function with incomplete declaration both pass excess arguments to an argumentless function.
Why does an empty declaration work for definitions with int arguments but not for float arguments? does contain a successfully building declaration/definition mismatch, but has no invocation, where I would expect to see a too few arguments message.
I have a function declaration with no args, a call to that function with no args, and the function definition below with args. Somehow, C manages to successfully call the function, no warning, no error, but very undefined behaviour. Where does the function get the missing argument from? Why don't I get a linker error since the no-arg function isn't defined? Why don't I get a compiler error because I'm redefining a function with a different signature? Why, oh why, is this allowed?
Compiling as C++ code (gcc -x c++, enabling Compile To Binary on Godbolt) I get a linker error as expected, because of course C++ allows overloading, and the no-arg overload isn't defined. By checking with Godbolt, compiling with Clang and MSVC as C code also both build successfully, with only MSVC spitting out a minor warning.
Here is my reduced example for Godbolt.
// Compile with GCC or Clang -x c -Wall -Wextra
// Compile with MSVC /Wall /W4 /Tc
#include <stdio.h>
#include <stdlib.h>
// This is just so Godbolt can do an MSVC build
#ifndef _MSC_VER
# include <unistd.h>
#else
# define read(file, output, count) (InputBuffer[count] = count, fd)
#endif
static char InputBuffer[16];
int ReadInput(); // <-- declared with no args
int main(void)
{
int count;
count = ReadInput(); // <-- called with no args
printf("%c", InputBuffer[0]); // just so the results, and hence the entire function call,
printf("%d", count); // don't get optimised away by not being used (even though I'm
return 0; // not using any optimisation... just being cautious)
};
int ReadInput(int fd) // <-- defined with args!
{
return read(fd, InputBuffer, 1); // arg is definitely used, it's not like it's optimised away!
};
Where does the function get the missing argument from?
Typically, the called function is compiled to get its parameters from the places the arguments would be passed according to the ABI (Application Binary Interface) being used. This is necessarily true when the called function is in a separate translation unit (and there is no link-time optimization), so the compiler cannot adjust it according to the calling code. If the call and the called function are in the same translation unit, the compiler could do other things.
For example, if the ABI says the first int class parameter is passed in processor register r4, then the called function will get its parameter from register r4. Since the caller has not put an argument there, the called function gets whatever value happens to be in r4 from previous use.
Why don't I get a linker error since the no-arg function isn't defined?
C implementations generally resolve identifiers by name only. Type information is not part of the name or part of resolution. A function declared as int ReadInput() has the same name as a function declared as int ReadInput(int fd), and, as far as the linker is concerned, a definition of one will satisfy a reference to the other.
Why don't I get a compiler error because I'm redefining a function with a different signature?
The definitions are compatible. In C, the declaration int ReadInput() does not mean the function has no parameters. It means “There is a function named ReadInput that returns int, and I am not telling you what its parameters are.
The declaration int ReadInput(int fd) means “There is a function named ReadInput that returns int, and it takes one parameter, an int. These declarations are compatible; neither says anything inconsistent with the other.
Why, oh why, is this allowed?
History. Originally, C did not supply parameter information in function declarations, just in definitions. The prototype-less declarations are still allowed so that old software continues to work.
Other answers explained why it is legal to call a function that was declared without a prototype (but that it is your responsibility to get the arguments right). But you might be interested in the -Wstrict-prototypes warning option accepted by both GCC and clang, which is documented to "Warn if a function is declared or defined without specifying the argument types." Your code then yields warning: function declaration isn't a prototype.
Try it on godbolt.
(I'm kind of surprised this warning isn't enabled with -Wall -Wextra.)
In C, unlike in C++, declaring a function with no arguments means that the function may have as many arguments as you'd like. If you want to make it really not have any arguments, you just have to explicitly declare that:
int ReadInput(void);
I have a bunch of executables written in C that are statically analyzed with Polyspace Code Prover and Bug Finder. Both tools flag my main() functions for violation of MISRA's Guideline 8.4, with the following message:
"A compatible declaration shall be visible when an object or function with external linkage is defined.
Function 'main' has no visible compatible prototype at definition."
Forward declaring main() seems to solve it, but that is very "weird" for me and it introduces problems when documenting the project with Doxygen.
Here's the function:
int main(int argument_counter, char const *arg_vector[])
also as you can see, we couldn't use the traditional argc and argv[] parameter names because they were too similar to some variables it found on the external headers, which is also superweird in my opinion.
Is this a code problem or is there something wrong with the tools configuration?
You often get these kind of false positives from static analysers regarding main, when you use an implementation-defined form. But notably, a strictly conforming hosted program shall use this form:
int main(int argc, char *argv[])
The name of the parameters doesn't matter, but their types do. char* [] is not the same type as const char* []. The const in your code doesn't mark the actual character arrays as const, but rather the array of pointers to them. Which is a bit weird, I don't really see why anyone would attempt to overwrite those.
Also notable, argc and argv must be writable in a strictly conforming program, C17 5.1.2.2.1 §2:
The parameters argc and argv and the strings pointed to by the argv array shall
be modifiable by the program, and retain their last-stored values between program
startup and program termination
So you should ideally just change the types to be the ones required by a strictly conforming program.
However, many C programs are not strictly conforming hosted programs, so the static analyser must be able to swallow implementation-defined forms of main too. There's really no harm in forward declaring main either - and you are safe to assume that the compiler does not do so (C17 5.1.2.2.1 §1 "The implementation declares no
prototype for this function.").
Suppose you have the implementation-defined form void main (void). To silence the tool you can simply write:
void main (void);
void main (void)
{ ...
I strongly suspect the reason for the tool warning is that it's too blunt to recognize that main is a special case. Similarly you can get warnings for using int as return value from main, instead of int32_t - which is a false positive, as MISRA-C has an explicit exception for the return type of main.
main() is an exception to many rules, both within MISRA and without...
For the avoidance of doubt, MISRA C:2012 Technical Corrigendum 1 adds an explicit exception to Rule 8.4 for main():
The function main need not have a separate declaration.
I just debugged a C program for a long time, only to find that I missed an argument when making a function call, so junk instead filled the missing argument. Stupid mistakes like this are really frustrating, but I suppose compilers should be able to detect this. (C doesn't even support default arguments; even in C++, default arguments need to be explicitly declared.)
Update: The prototype was found to be wrong, too...
So, is there a GCC flag for warning unmatched function call argument number? I always have -Wall and -pedantic on; it's quite surprising that such an obvious error goes undetected. (Actually I suppose there is some reason that GCC does not report, but I can't think of any at this time.)
Embarrassing code example:
static void dfs();
int main(int argc, const char *argv[]) {
dfs(1);
}
static void
dfs(int remain, int last) {
// dfs
}
Another discovery I just made is that if the prototype contains argument, the compiler will report; but the prototype happened to contains no arguments, then the compiler just slipped.
Unmatched number of function call arguments is a mandatory diagnostic that all compilers will and must provide without any special setting. It is mandated by the standard.
C99Standard 6.5.2.2 Function calls:
Constraints
If the expression that denotes the called function has a type that includes a prototype, the
number of arguments shall agree with the number of parameters. Each argument shall
have a type such that its value may be assigned to an object with the unqualified version
of the type of its corresponding parameter.
static void dfs();
Tells the compiler dfs is a static function which returns a void and can take unspecified number of arguments. Further you provide a definition for the function which takes 2 arguments & call the same. As you see there is no breaking of contract. The problem is the declaration of the function is incorrect. If you want to declare a function which takes no arguments you must use:
static void dfs(void);
Once you do that the compiler will provide you a diagnostic.
There are various options you can use:
-Wstrict-prototypes
-Wmissing-prototypes
-Wold-style-definition
-Wold-style-declaration
These work even if you're not using -std=c99 or something similar. I'm plagued by antique code at the office and use these (except -Wstrict-prototypes) when I'm cleaning up some code that hasn't been given 'the treatment'. The exception is because the pointers to functions in the code base are mostly without the necessary argument lists. For personal code, or code I'm in charge of, this isn't a problem.
I'm looking at example abo3.c from Insecure Programming and I'm not grokking the casting in the example below. Could someone enlighten me?
int main(int argv,char **argc)
{
extern system,puts;
void (*fn)(char*)=(void(*)(char*))&system;
char buf[256];
fn=(void(*)(char*))&puts;
strcpy(buf,argc[1]);
fn(argc[2]);
exit(1);
}
So - what's with the casting for system and puts? They both return an int so why cast it to void?
I'd really appreciate an explanation of the whole program to put it in perspective.
[EDIT]
Thank you both for your input!
Jonathan Leffler, there is actually a reason for the code to be 'bad'. It's supposed to be exploitable, overflowing buffers and function pointers etc. mishou.org has a blog post on how to exploit the above code. A lot of it is still above my head.
bta, I gather from the above blog post that casting system would somehow prevent the linker from removing it.
One thing that is not immediately clear is that the system and puts addresses are both written to the same location, I think that might be what gera is talking about “so the linker doesn’t remove it”.
While we are on the subject of function pointers, I'd like to ask a follow-up question now that the syntax is clearer. I was looking at some more advanced examples using function pointers and stumbled upon this abomination, taken from a site hosting shellcode.
#include <stdio.h>
char shellcode[] = "some shellcode";
int main(void)
{
fprintf(stdout,"Length: %d\n",strlen(shellcode));
(*(void(*)()) shellcode)();
}
So the array is getting cast to a function returning void, referenced and called? That just looks nasty - so what's the purpose of the above code?
[/EDIT]
Original question
User bta has given a correct explanation of the cast - and commented on the infelicity of casting system.
I'm going to add:
The extern line is at best weird. It is erroneous under strict C99 because there is no type, which makes it invalid; under C89, the type will be assumed to be int. The line says 'there is an externally defined integer called system, and another called puts', which is not correct - there are a pair of functions with those names. The code may actually 'work' because the linker might associate the functions with the supposed integers. But it is not safe for a 64-bit machine where pointers are not the same size as int. Of course, the code should include the correct headers (<stdio.h> for puts() and <stdlib.h> for system() and exit(), and <string.h> for strcpy()).
The exit(1); is bad on two separate counts.
It indicates failure - unconditionally. You exit with 0 or EXIT_SUCCESS to indicate success.
In my view, it is better to use return at the end of main() than exit(). Not everyone necessarily agrees with me, but I do not like to see exit() as the last line of main(). About the only excuse for it is to avoid problems from other bad practices, such as functions registered with atexit() that depend on the continued existence of local variables defined in main().
/usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -c nasty.c
nasty.c: In function ‘main’:
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘system’
nasty.c:3: warning: type defaults to ‘int’ in declaration of ‘puts’
nasty.c:3: warning: built-in function ‘puts’ declared as non-function
nasty.c:8: warning: implicit declaration of function ‘strcpy’
nasty.c:8: warning: incompatible implicit declaration of built-in function ‘strcpy’
nasty.c:10: warning: implicit declaration of function ‘exit’
nasty.c:10: warning: incompatible implicit declaration of built-in function ‘exit’
nasty.c: At top level:
nasty.c:1: warning: unused parameter ‘argv’
Not good code! I worry about a source of information that contains such code and doesn't explain all the awfulness (because the only excuse for showing such messy code is to dissect it and correct it).
There's another weirdness in the code:
int main(int argv,char **argc)
That is 'correct' (it will work) but 100% aconventional. The normal declaration is:
int main(int argc, char **argv)
The names are short for 'argument count' and 'argument vector', and using argc as the name for the vector (array) of strings is abnormal and downright confusing.
Having visited the site referenced, you can see that it is going through a set of graduated examples. I'm not sure whether the author simply has a blind spot on the argc/argv issue or is deliberately messing around ('abo1' suggests that he is playing, but it is not helpful in my view). The examples are supposed to feed your mind, but there isn't much explanation of what they do. I don't think I could recommend the site.
Extension question
What's the cast in this code doing?
#include <stdio.h>
char shellcode[] = "some shellcode";
int main(void)
{
fprintf(stdout,"Length: %d\n",strlen(shellcode));
(*(void(*)()) shellcode)();
}
This takes the address of the string 'shellcode' and treats it as a pointer to a function that takes an indeterminate set of arguments and returns no values and executes it with no arguments. The string contains the binary assembler code for some exploit - usually running the shell - and the objective of the intruder is to get a root-privileged program to execute their shellcode and give them a command prompt, with root privileges. From there, the system is theirs to own. For practicing, the first step is to get a non-root program to execute the shellcode, of course.
Reviewing the analysis
The analysis at Mishou's web site is not as authoritative as I'd like:
One, this code uses the extern keyword in the C language to make the system and puts functions available. What this does (I think) is basically references directly the location of a function defined in the (implied) header files…I get the impression that GDB is auto-magically including the header files stdlib.h for system and stdio.h for puts. One thing that is not immediately clear is that the system and puts addresses are both written to the same location, I think that might be what gera is talking about “so the linker doesn’t remove it”.
Dissecting the commentary:
The first sentence isn't very accurate; it tells the compiler that the symbols system and puts are defined (as integers) somewhere else. When the code is linked, the address of puts()-the-function is known; the code will treat it as an integer variable, but the address of the integer variable is, in fact, the address of the function - so the cast forces the compiler to treat it as a function pointer after all.
The second sentence is not fully accurate; the linker resolves the addresses of the external 'variables' via the function symbols system() and puts() in the C library.
GDB has nothing whatsoever to do the compilation or linking process.
The last sentence does not make any sense at all. The addresses only get written to the same location because you have an initialization and an assignment to the same variable.
This didn't motivate me to read the whole article, it must be said. Due diligence forces me onwards; the explanation afterwards is better, though still not as clear as I think it could be. But the operation of overflowing the buffer with an overlong but carefully crafted argument string is the core of the operation. The code mentions both puts() and system() so that when run in non-exploit mode, the puts() function is a known symbol (otherwise, you'd have to use dlopen() to find its address), and so that when run in exploit mode, the code has the symbol system() available for direct use. Unused external references are not made available in the executable - a good thing when you realize how many symbols there are in a typical system header compared with the number used by a program that includes the header.
There are some neat tricks shown - though the implementation of those tricks is not shown on the specific page; I assume (without having verified it) that the information for getenvaddr program is available.
The abo3.c code can be written as:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
void (*fn)(char*) = (void(*)(char*))system;
char buf[256];
fn = (void(*)(char*))puts;
strcpy(buf, argv[1]);
fn(argv[2]);
exit(1);
}
Now it compiles with only one warning with the fussy compilation options I originally used - and that's the accurate warning that 'argc' is not used. It is just as exploitable as the original; it is 'better' code though because it compiles cleanly. The indirections were unnecessary mystique, not a crucial part of making the code exploitable.
Both system and puts normally return int. The code is casting them to a pointer that returns void, presumably because they want to ignore whatever value is returned. This should be equivalent to using (void)fn(argc[2]); as the penultimate line if the cast didn't change the return type. Casting away the return type is sometimes done for callback functions, and this code snippet seems to be a simplistic example of a callback.
Why the cast for system if it is never used is beyond me. I'm assuming that there's more code that isn't shown here.
Splint gives me the following warning:
encrypt.c:4:8: Function exported but not used outside encrypt: flip
A declaration is exported, but not used outside this module. Declaration can
use static qualifier. (Use -exportlocal to inhibit warning)
encrypt.c:10:1: Definition of flip
Since I called splint only on this file how does it know that?
#include <stdio.h>
#include <stdlib.h>
int flip( int a)
{
int b;
b = a;
b ^= 0x000C;
return b;
}
int blah(int argc, char *argv[]) {
FILE *fp = NULL, *fpOut=NULL;
int ch;
ch = 20; flip(20); return (ERROR_SUCCESS);
}
I even got rid of main so that it could not figure out that the file is complete in any way. I am totally stumped!
You might find that if you included a header that declared flip() - as you should, of course - then splint would not complain. You should also declare blah() in the header as well.
I'm not wholly convinced that this is the explanation because blah() is not used at all (though it uses flip()) and you don't mention splint complaining about that.
However, it is a good practice to make every function (in C) static until you can demonstrate that it is needed outside its source file, and then you ensure that there is a header that declares the function, and that header is used in the file that defines the function and in every file that uses the function.
In C++, the 'every function should be static' advice becomes 'every function should be defined in the anonymous namespace'.
Since I called splint only on this file how does it know that?
You have answered your question. You've fed in one file to lint, so lint knows there is only file to be taken care of (apart from the standard header includes, of course).
int flip() is not declared as static, so it can be potentially used externally. Since you invoked splint with only one source file, it correctly says that your function, if not used externally, must be declared static
It can only report on what it sees. Ignore the warning or follow the instructions to inhibit it if you know better than what it says. Don't assume that a tool like this necessarily knows your program better than you do.
If it really is not intended to be used outside of the file, you can declare it static and it should correct the problem, but it will be inaccessible from other files.