Void function required to return value? - c

I wrote a function as follows which is used to create pthread:
void *downloadfile(void *arg)
{
char *req_path = NULL;
char local_path[PATH_BUFFER_SIZE];
int returncode = 0;
gfcrequest_t *gfr = NULL;
FILE *file = NULL;
int i = 0;
/* Build your queue of requests here */
for (i = 0; i < nrequests; i++) {
// pthread mutex locking
// doing something
// pthread mutex unlocking
localPath(req_path, local_path);
file = openFile(local_path);
gfr = gfc_create();
gfc_set_server(&gfr, server);
gfc_set_path(&gfr, req_path);
gfc_set_port(&gfr, port);
gfc_set_writefunc(&gfr, writecb);
gfc_set_writearg(&gfr, file);
fprintf(stdout, "Requesting %s%s\n", server, req_path);
if (0 > (returncode = gfc_perform(&gfr))) {
fprintf(stdout, "gfc_perform returned an error %d\n", returncode);
fclose(file);
if (0 > unlink(local_path))
fprintf(stderr, "warning: unlink failed on %s\n", local_path);
} else {
fclose(file);
}
if (gfc_get_status(&gfr) != GF_OK) {
if (0 > unlink(local_path))
fprintf(stderr, "warning: unlink failed on %s\n", local_path);
}
fprintf(stdout, "Status: %s\n", gfc_strstatus(gfc_get_status(&gfr)));
fprintf(stdout, "Received %zu of %zu bytes\n", gfc_get_bytesreceived(&gfr),
gfc_get_filelen(&gfr));
gfc_cleanup(&gfr);
req_path = NULL;
}
// return 0;
}
I commented out the "return 0;" statement because this is a void function thus should not return any value. But the compiler complains that
error: control reaches end of non-void function
If I uncomment the "return 0;" statement, the error goes away. I am so puzzled. How can the compiler require a void function to return value? Could somebody please help out?

From your description I assume that your function downloadfile is to be used as function-pointer argument in a pthread_create call.
So you have a basic misunderstanding. The function pointer used for pthread_create is not a function pointer to a "void function". It's a function pointer to a function that returns a void pointer. (BTW: You can get the return value using the pthread_join function if needed)
So in other other words: Your function must return a void-pointer. If you have nothing meaningful to return just use return NULL;
Extra details:
From https://man7.org/linux/man-pages/man3/pthread_create.3.html we have:
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);
The interresting part here is void *(*start_routine)(void *) which means:
void *(*start_routine)(void *)
\----/\--------------/\------/
| Function ptr |
| to a function /----------\
| that takes void pointer as argument
| and returns
/----------\
void pointer
So again - to use a correct function pointer argument for pthread_create you need a function that returns a void-pointer.

because this is a void function
No it isn't. It is a function returning a void*. Pthreads only work with functions of the signature void* f (void*). That's the only kind of function you can use, as per pthread library design. You don't get to chose anything else.
error: control reaches end of non-void function
This is a nice error to get since your code without the return statement contains undefined behavior. Specifically C17 6.9.1/12 says:
If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.
As in, if you didn't use return and the caller checks the result - which will happen here inside the pthreads lib, then anything can happen. It is a bug.
If you aren't interested in returning anything from the function, you must return NULL; or the equivalent return 0;.
Notably, the difference between returning void and void* is fundamental C - it is something you should have studied at a much earlier stage before moving on to multi-threading, which is a more advanced topic.

Related

Producer Consumer Solution

I've developed the producer / consumer problem in C and for some reason it doesn't compile. I'm getting the error message:
try1.c: In function ‘main’:
try1.c:19:21: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
BUFFER=(char *) malloc(sizeof(char) * BufferSize);
Please could someone identify the issue? Have tried for a while to fix this now however haven't had any luck.
#include <stdio.h>
#include <pthread.h>
#define BufferSize 10
void *Producer();
void *Consumer();
int BufferIndex=0;
char *BUFFER;
pthread_cond_t Buffer_Not_Full=PTHREAD_COND_INITIALIZER;
pthread_cond_t Buffer_Not_Empty=PTHREAD_COND_INITIALIZER;
pthread_mutex_t mVar=PTHREAD_MUTEX_INITIALIZER;
int main()
{
pthread_t ptid,ctid;
BUFFER=(char *) malloc(sizeof(char) * BufferSize);
pthread_create(&ptid,NULL,Producer,NULL);
pthread_create(&ctid,NULL,Consumer,NULL);
pthread_join(ptid,NULL);
pthread_join(ctid,NULL);
return 0;
}
void *Producer()
{
for(;;)
{
pthread_mutex_lock(&mVar);
if(BufferIndex==BufferSize)
{
pthread_cond_wait(&Buffer_Not_Full,&mVar);
}
BUFFER[BufferIndex++]='#';
printf("Produce : %d \n",BufferIndex);
pthread_mutex_unlock(&mVar);
pthread_cond_signal(&Buffer_Not_Empty);
}
}
void *Consumer()
{
for(;;)
{
pthread_mutex_lock(&mVar);
if(BufferIndex==-1)
{
pthread_cond_wait(&Buffer_Not_Empty,&mVar);
}
printf("Consume : %d \n",BufferIndex--);
pthread_mutex_unlock(&mVar);
pthread_cond_signal(&Buffer_Not_Full);
}
}
Thanks a lot for your help.
First of all, this question has nothing to do with producer/consumer. It has to do with the use of a function you didn't declare.
Historically, C allowed calling a function which was never declared. Since technically function declaration is not needed to call it, compiler gladly added instructions to call the unknown function. Allegedly, it allowed developers to save precious keystrokes. However, you need a function declaration to know it's return value, and compiler assumed that return value of such a function is int. And you are having just that - an implicitly declared malloc() with assumed return type of int.
Now, compiler knows what malloc() is. It is often built-in intrinstic function. However, compiler also knows that return value of said malloc() is void*, not int - and thus it complains.
Solution - get rid of implicit declaration, and make a habit of always including apropriate header files for every function you are using.
You also have issues with the way you are using conditional variables, but I would leave it for another question.
The producer should go to sleep when the buffer is full. Next time when the consumer removes data it notifies the producer and producer start producing data again. The consumer should go to sleep when a buffer is empty. Next time when producer adds data it notifies the consumer and consumer starts consuming data. This solution can be achieved using semaphores.
#include<stdio.h>
#include<stdlib.h>
#include <pthread.h>
#include <semaphore.h>
int mutex=1,full=0,empty=3,x=0;
int main()
{
int n;
void producer(void);
void consumer(void);
int waiting(int);
int signaling(int);
printf("\n1.Producer\n2.Consumer\n3.Exit");
while(1)
{
printf("\nEnter your choice:");
scanf("%d",&n);
switch(n)
{
case 1: if((mutex==1)&&(empty!=0))
producer();
else
printf("Buffer is full!!");
break;
case 2: if((mutex==1)&&(full!=0))
consumer();
else
printf("Buffer is empty!!");
break;
case 3:
exit(0);
break;
}
}
return 0;
}
int waiting(int s)
{
return (--s);
}
int signaling(int s)
{
return(++s);
}
void producer()
{
mutex=wait(&mutex);
full=signaling(full);
empty=wait(&empty);
x++;
printf("\nProducer produces the item %d",x);
mutex=signaling(mutex);
}
void consumer()
{
mutex=wait(&mutex);
full=wait(&full);
empty=signaling(empty);
printf("\nConsumer consumes item %d",x);
x--;
mutex=signaling(mutex);
}
Just include the stdlib.h library. This library will include the malloc() function.
You are using malloc without including the header file that declares it (stdlib.h).
Your usage (without that explicit declaration from stdlib.h) creates an "implicit declaration" which doesn't match the one the compiler knows about because yours returns char* and the proper malloc returns void*.
So add include <stdlib.h> and also note that you shouldn't cast the result of a malloc. See Do I cast the result of malloc?

Most Desirable Way To Handle Function Error Messages?

Let's say I have a function to perform a small and particular task that has a fairly good possibility of failure. What is the best way to handle something going wrong? (Assuming I know what the problem is).
For example lets say I have a function that reads a two byte string and returns it:
#include <stdio.h>
#include <stdlib.h>
char *bar(void)
{
char *foo = malloc(3);
scanf("%2s", foo);
return foo;
}
int main(void)
{
char *foo = bar();
puts(foo);
free(foo);
return 0;
}
The above example has absolutely no error handling whatsoever. There are two ways that I would implement some sort of error handling, but I'm not sure which would be more preferred or considered best practice.
Method 1 (print error message To stderr from within the function):
#include <stdio.h>
#include <stdlib.h>
char *bar(void)
{
char *foo;
if(!(foo = malloc(3)))
{
fputs("\nError! Memory allocation failed.", stderr);
return 0x00;
}
scanf("%2s", foo);
return foo;
}
int main(void)
{
char *foo;
if(!(foo = bar())) return 1;
puts(foo);
free(foo);
return 0;
}
Method 2 (print error message to stderr from the calling function):
#include <stdio.h>
#include <stdlib.h>
char *bar(void)
{
char *foo;
if(!(foo = malloc(3))) return 0x00;
scanf("%2s", foo);
return foo;
}
int main(void)
{
char *foo;
if(!(foo = bar()))
{
fputs("\nError! Memory allocation failed.", stderr);
return 1;
}
puts(foo);
free(foo);
return 0;
}
I'm almost thinking that method two would be the best way to go because that way I could get more specific with my error messages depending on what I'm calling that function for at the time.
What I worry about with method two is the fact that I lose the ability to print what specifically went wrong in the function if it has more than one potential point of failure.
Pseudo Code:
IF FAILUREA
PRINT "FAILUREA OCCURED"
RETURN
IF FAILUREB
PRINT "FAILUREB OCCURED"
RETURN
This wouldn't be much of a problem if the function I was calling was an int because then I could just return a different integer value based on what went wrong. But in the case of a char* I typically try to return NULL on failure (so both FAILUREA and FAILUREB would be returning NULL); there would be no way to know what caused the function to fail.
So my question is what is best practice when it comes to handling error messages?
Allowing the caller to handle error reporting is better because:
if the function is forming part of a library stderr may not be available and an alternative reporting mechanism is required.
the calling code may have an alternative action that can be taken and may not deem the failure of function bar() as an actual failure and have no need to report it.
If a function has multiple possible failure reasons then a possibility is to pass an argument to the function that is updated in the event of failure. The calling function can then choose an appropriate action depending on the actual failure reason. For example:
enum Status
{
STATUS_OK,
STATUS_MEMORY_ALLOCATION_FAILURE,
STATUS_ACCESS_DENIED
};
enum Status status;
char* foo = bar(&status);
if (!foo)
{
if (STATUS_MEMORY_ALLOCATION_FAILURE == status)
{
/* report failure. */
}
else if (STATUS_ACCESS_DENIED == status)
{
/* try somewhere else */
}
}
If you can do anything about a failure and if you are going to, then you do it.
Otherwise, you may implement a generic failure function, call it in case of an error and call it a day:
void error(const char* format, ...)
{
va_list vl;
va_start(vl, format);
vfprintf(stderr, format, vl);
va_end(vl);
exit(-1);
}
You can optionally wrap it in a macro supplying it with the line# and file name:
#define ERROR(fmt, ...) \
error("file:'%s',line:%d " fmt, __FILE__, __LINE__, __VA_ARGS__)
This will make errors in the console very easy to figure out because the error messages tell precisely the file and the line in it where the error has occurred.
Typical usage, nothing fancy:
char *bar(void)
{
char *foo;
if ((foo=malloc(3)) == NULL)
ERROR("malloc() failed!\n");
if (scanf("%2s", foo) != 1)
ERROR("scanf() failed!\n");
return foo;
}
You may use longjmp() in place of exit(-1) to immediately return to the caller (=the one that did the respective setjmp()) if you want to actually do something upon the error, maybe close all files open for writing, so the buffered data isn't lost.
If you're writing a simple compiler, for example, this kind of error() is more than enough for most errors internal to the compiler and for problems in the source code being compiled (e.g. a missing colon/paren or something else that makes the code not compilable).
If you cannot or do not want to do any of that, you need to carefully write the code, do proper clean ups and return different error codes to communicate actionable errors to the caller.
You can do in this way if your function return more than 1 error case
#include <stdio.h>
#include <stdlib.h>
int bar(char **foo)
{
if(!(malloc(3))) return 1; /* return error case 1*/
scanf("%2s", *foo);
if(!(malloc(4))) return 2; /* return error case 2*/
return 0; /* no error*/
}
int catcherror(int error)
{
switch (error) {
case 1:
/*do something 1*/
case 2:
/*do something 1*/
case 3:
/*do something 1*/
case 4:
/*do something 1*/
case 5:
/*do something 1*/
default:
/*do something 1*/
}
}
int main(void)
{
char *foo;
int error
error = bar(&foo);
catcherror(error);
puts(foo);
free(foo);
return 0;
}
The catcherror() function could be very useful if your project contains many functions which return a common error cases

Dynamic Library Function Call

I have the following code which simply loads the library test.so from the current directory and executes the version function within that library. What should be returned is a string. What is instead returned is junk from the stack(a pointer location maybe?). Does anyone have any idea why the following code would be failing.
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv){
void *handler;
char *(*version);
char *error;
handler = dlopen("./test.so", RTLD_LAZY);
if(!handler){
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
dlerror(); //Flushes any existing error
*(void **)(&version) = dlsym(handler, "version");
if((error = dlerror()) != NULL){
fprintf(stderr, "%s\n", error);
exit(EXIT_FAILURE);
}
printf("%s\n", version);
dlclose(handler);
exit(EXIT_SUCCESS);
}
Change the declaration:
char *(*version); // this is not a function pointer
to:
char *(*version)(); // but this is
then, change the line:
printf("%s\n", version);
to:
printf("%s\n", version());
dlsym just returns a function pointer. You still need to actually call it, which you can do by casting the (void*) to a function pointer of the appropriate type and calling that.
There are two errors in your code:
The declaration of version: it is a pointer to a pointer to char in your code, which led you to a weird conversion in:
*(void **)(&version) = dlsym(handler, "version");
Instead use a pointer to function returning pointer to char, like so:
char *(*version)();
and use a regular assignment:
version = dlsym(handler, "version");
You are not calling the function, when you print the result. In C, writing just the function name returns its address, so version and &version are equivalent. Your code should be like:
printf("%s\n", version());

C: writing the following code into functions

Dear respected programmers. Please could you help me (again) on how to put the following code into functions for my program.
I have read on-line and understand how functions work but when I do it myself it all goes pear shaped/wrong(I am such a noob).
Please could you help with how to for example to write the code below into functions.(like opening the input file).
My initial code looks like:
main (int argc, char **argv)
{
int bytes_read, bytes_written;
struct stat inode;
int input_fd, output_fd;
char buffer[64];
int eof = 0;
int i;
/* Check the command line arguments */
if (argc != 3)
{
printf("syntax is: %s \n", <fromfile> <tofile>\n", argv[0]);
exit (1);
}
/* Check the input file exists and is a file */
if ((stat(argv[1], &inode) == -1) || (!S_ISREG(inode.st_mode)))
{
printf("%s is not a file\n", argv[1]);
exit(2);
}
/* Check that the output file doesnt exist */
if (stat(argv[2], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[2]);
exit(2);
}
/* Open the input file for reading */
input_fd = open(argv[1], O_RDONLY, 0);
if (input_fd == -1)
{
printf("%s cannot be opened\n", argv[1]);
exit(3);
}
output_fd = open(argv[2], O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if (output_fd == -1)
{
printf("%s cannot be opened\n", argv[2]);
exit(3);
}
/* Begin processing the input file here */
while (!eof)
{
bytes_read = read(input_fd, buffer, sizeof(buffer));
if (bytes_read == -1)
{
printf("%s cannot be read\n", argv[1]);
exit(4);
}
if (bytes_read > > 0)
{
bytes_written = write(output_fd, buffer, bytes_read);
if (bytes_written == -1)
{
printf("There was an error writing to the file %s\n",argv[2]);
exit(4);
}
if (bytes_written != bytes_read)
{
printf("Devistating failure! Bytes have either magically appeared and been written or dissapeard and been skipped. Data is inconsistant!\n");
exit(101);
}
}
else
{
eof = 1;
}
}
close(input_fd);
close(output_fd);
}
My attempt at opening an output file:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Any help on how I would now reference to this in my program is appreciated thank you.
I tried:
ouputfile (but I cant figure out what goes here and why either).
Maybe the most useful function for you is:
#include <stdio.h>
#include <stdarg.h>
extern void error_exit(int rc, const char *format, ...); /* In a header */
void error_exit(int rc, const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(rc);
}
You can then write:
if (stat(argv[2], &inode) != -1)
error_exit(2, "Warning: The file %s exists. Not going to overwrite\n",
argv[2]);
Which has the merit of brevity.
You write functions to do sub-tasks. Deciding where to break up your code into functions is tricky - as much art as science. Your code is not so big that it is completely awful to leave it as it is - one function (though the error handling can be simplified as above).
If you want to practice writing functions, consider splitting it up:
open_input_file()
open_output_file()
checked_read()
checked_write()
checked_close()
These functions would allow your main code to be written as:
int main(int argc, char **argv)
{
int bytes_read;
int input_fd, output_fd;
char buffer[64];
if (argc != 3)
error_exit(1, "Usage: %s <fromfile> <tofile>\n", argv[0]);
input_fd = open_input_file(argv[1]);
output_fd = open_output_file(argv[2]);
while ((bytes_read = checked_read(input_fd, buffer, sizeof(buffer)) > 0)
check_write(output_fd, buffer, bytes_read);
checked_close(input_fd);
checked_close(output_fd);
return 0;
}
Because you've tucked the error handling out of sight, it is now much easier to see the structure of the program. If you don't have enough functions yet, you can bury the loop into a function void file_copy(int fd_in, int fd_out). That removes more clutter from main() and leaves you with very simple code.
Given an initial attempt at a function to open the output file:
void outputFile(int argc, char **argv)
{
/* Check that the output file doesnt exist */
if (stat(argv[argc-1], &inode) != -1)
{
printf("Warning: The file %s already exists. Not going to overwrite\n", argv[argc-1]);
return -1;
}
/*Opening ouput files*/
file_desc_out = open(argv[i],O_CREAT | O_WRONLY | O_EXCL , S_IRUSR|S_IWUSR);
if(file_desc_out == -1)
{
printf("Error: %s cannot be opened. \n",argv[i]); //insted of argv[2] have pointer i.
return -1;
}
}
Critique:
You have to define the variables used by the function in the function (you will want to avoid global variables as much as possible, and there is no call for any global variable in this code).
You have to define the return type. You are opening a file - how is the file descriptor going to be returned to the calling code? So, the return type should be int.
You pass only the information needed to the function - a simple form of 'information hiding'. In this case, you only need to pass the name of the file; the information about file modes and the like is implicit in the name of the function.
In general, you have to decide how to handle errors. Unless you have directives otherwise from your homework setter, it is reasonable to exit on error with an appropriate message. If you return an error indicator, then the calling code has to test for it, and decide what to do about the error.
Errors and warnings should be written to stderr, not to stdout. The main program output (if any) goes to stdout.
Your code is confused about whether argv[i] or argv[argc-1] is the name of the output file. In a sense, this criticism is irrelevant once you pass just the filename to the function. However, consistency is a major virtue in programming, and using the same expression to identify the same thing is usually a good idea.
Consistency of layout is also important. Don't use both if( and if ( in your programs; use the canonical if ( notation as used by the language's founding fathers, K&R.
Similarly, be consistent with no spaces before commas, a space after a comma, and be consistent with spaces around operators such as '|'. Consistency makes your code easier to read, and you'll be reading your code a lot more often than you write it (at least, once you've finished your course, you will do more reading than writing).
You cannot have return -1; inside a function that returns no value.
When you a splitting up code into functions, you need to copy/move the paragraphs of code that you are extracting, leaving behind a call to the new function. You also need to copy the relevant local variables from the calling function into the new function - possibly eliminating the variables in the calling function if they are no longer used there. You do compile with most warnings enabled, don't you? You want to know about unused variables etc.
When you create the new function, one of the most important parts is working out what the correct signature of the function is. Does it return a value? If so, which value, and what is its type? If not, how does it handle errors? In this case, you probably want the function to bail out (terminate the program) if it runs into an error. In bigger systems, you might need to consistently return an error indicator (0 implies success, negative implies failure, different negatives indicating different errors). When you work with function that return an error indicator, it is almost always crucial that you check the error indicators in the calling code. For big programs, big swathes of the code can be all about error handling. Similarly, you need to work out which values are passed into the function.
I'm omitting advice about things such as 'be const correct' as overkill for your stage in learning to program in C.
you seem to actually understand how to make a function. making a function really isnt that hard. first, you need to kind of understand that a function has a type. in otherwords, argc has type int and argv has type char *, your function (currently) has type void. void means it has no value, which means when you return, you return nothing.
however, if you look at your code, you do return -1. it looks like you want to return an interger. so you should change the top from void outputfile(...) to int outputfile(...).
next, your function must return. it wont compile if there is a circumstance where it won't return (besides infinite loops). so at the very bottom, if no errors happen, it will reach the end. since you're no longer using "void" as the return type, you must return something before the end of the function. so i suggest putting a return 1; to show that everything went great
There's several things.
The function return type isn't what you want. You either want to return a file descriptor or an error code. IIRC, the file descriptor is a nonnegative int, so you can use a return type of int rather than void. You also need to return something on either path, either -1 or file_desc_out.
You probably don't want to pass in the command-line arguments as a whole, but rather something like argv[argc - 1]. In that case, the argument should be something like char * filename rather than the argc/argv it has now. (Note that the argv[i] you've got in the last printf is almost certainly wrong.)
This means it would be called something like
int file_desc_out = outputFile(argv[argc - 1]);
You need to have all variables declared in the function, specifically inode and file_desc_out.
Finally, put an extra level of indentation on the code inside the { and } of the function itself.

Not null terminated string - a KlocWork error with no understandable reason

I've recently installed "klocwork" and am trying to get rid of bugs on an existing code.
The error shown seems to be simple. No null at the termination of the char * _p_.
I have manually added a null termination (even though there is no need), but it doesn't please the Klocwork. Any ideas?
The exact message is:-
Incorrectly terminated string 'p' causes a buffer overflow in p.
char *ptr;
int writtenchars = 0 ;
va_list args;
char* destStr;
if (argc != 2) {
printf(" wrong parameters number - %d instead of %d\n", argc, 2);
char str[25]="wrong parameters number ";
char *_p_; /********************************************************/
va_start(args, str);
destStr = (char*) malloc(SNMP_BUF_LEN);
_p_= destStr;
if (destStr == NULL) {
printf("WARNING: Failed to alloc memory in in function \"snmp_rebuildstringinbuf!!!\" \n");
destStr="kukuRiko";
}
else {
writtenchars = (int) vsnprintf(destStr, 4095, str, args);
if (writtenchars>SNMP_BUF_LEN) {
printf("WARNING: Too long string rebuilded in function \"snmp_rebuildstringinbuf!!!\" %d chars\n",writtenchars);
}
destStr[writtenchars] = '\0' ; //Moshe - making sure the last value of the string is null terminated in order to prevent future buffer overflows.
}
va_end(args);
/******************************************************************************/
//The KlocWork error relates to this line //
logCWriteLog_msg(moduleId, level, __FILE__, __LINE__, _p_, ltrue);
free (_p_);
===========================================================
Hi Guys,
Thanks for your answers, but it seems a bit more obscure than that. I have refined the code to this simple case:-
When the code is written all in one function there is no error, whereas, when the allocation section is wrapped in a function (and a text passed as parameter) the Klocwork error returns.
See this code:- version without an error:-
char *_p_; /*+++++++++++++++++++*/
int writtenchars = 0 ;
va_list args;
char* destStr;
char* str = "hello World";
va_start(args, str);
destStr = (char*)malloc(SNMP_BUF_LEN);
if (destStr == NULL) {
printf("WARNING: Failed to alloc memory in function \n");
}
else {
writtenchars = (int) vsnprintf(destStr, (SNMP_BUF_LEN) - 1, str, args);
}
/*+++++++++++++++++++*/
_p_ = destStr ;
if (_p_ != NULL) {
logCWriteLog_msg(moduleId, level, __FILE__, __LINE__, _p_, ltrue);
}
free (_p_);
/***********************************************************/
whereas when taking the code between /*++++ */ and wrapping it in a function returns the above KlocWork error.
Hence,
char *writingToSomeBuffer (char * str) {
int writtenchars = 0 ;
va_list args;
char* destStr;
va_start(args, str);
destStr = (char*)malloc(SNMP_BUF_LEN);
if (destStr == NULL) {
printf("WARNING: Failed to alloc memory in function \n");
}
else {
writtenchars = (int) vsnprintf(destStr, (SNMP_BUF_LEN) - 1, str, args);
}
return destStr;
}
int main () {
char *_p_;
_p_ = writingToSomeBuffer("hello world");
if (_p_ != NULL) {
logCWriteLog_msg(moduleId, level, __FILE__, __LINE__, _p_, ltrue);
}
free (_p_);
return 0 ;
}
any ideas?
KlocWork is correctly diagnosing the problem that you can be writing with a null pointer if memory allocation fails:
_p_= destStr;
if (destStr == NULL)
{
printf("WARNING: Failed to alloc memory in in function ...\n");
destStr = "kukuRiko";
At this point, the (horribly named) '_p_' variable is still null, but you go ahead and use it in the printing operation below.
Also note that the 'trivial' fix of adding '_p_' after this breaks your memory management; you later do 'free(_p_);' which will lead to horrible problems if '_p_' points to the constant string.
You also have 'memory in in function' in the message. And 'wrong parameters number' does mean roughly the same as 'wrong number of parameters' but the latter is more idiomatic English. I'm not convinced any of the exclamation marks are helpful in the error message; there is a strong argument that they should go outside the double quotes surrounding the function name even if one of them is deemed desirable.
With the revised version of the problem, I wonder if Klocwork is diagnosing what Microsoft says of its vsnprintf(), that it does not guarantee null termination (which is different from what C99 and POSIX says).
Jonathan has it right. We've recently broken up this checker into two families that might explain it better:
http://www.klocwork.com/products/documentation/Insight-9.1/Checkers:NNTS.MIGHT
http://www.klocwork.com/products/documentation/Insight-9.1/Checkers:NNTS.MUST
We are currently under development to clean this up and make it easier to understand. Not only the problem but the solution as well.
Klocwork's error aside, I think this code is wrong. Why are you limiting the vsnprintf to 4096, while the buffer size is SNMP_BUF_LEN? How do those two related to each other? If SNMP_BUF_LEN < 4096, then you may have just overflowed your buffer. Why wouldn't you pass SNMP_BUF_LEN as the limiting argument in vsnprintf?
Also, the write to destStr[writtenchars] is suspect. Depending on the variant of vsnprintf (they do vary), writtenchars might be the number of characters it wanted to write, which would again cause you to write past the end of your buffer.
That all said, Klocwork isn't perfect. We had macros that were very explicitly trying to be safe, and Klocwork mis-detected them as potentially overrunning the string. I think that was a snprintf case as well.
Overall a good product, but it does have a few holes and you can't fix all it's complaints.

Resources