I've been asked to analyze some C code with Flawfinder:
char * buffer;
size_t len;
// my_fd is a file descriptor
read(my_fd, &len, sizeof(len));
buffer = malloc(len + 1);
read(my_fd, buffer, len);
buffer[len] = '\0';
I get the following warnings on the 2 read:
test.c:xx: [1] (buffer) read:
Check buffer boundaries if used in a loop including recursive loops
(CWE-120, CWE-20).
test.c:xx: [1] (buffer) read:
Check buffer boundaries if used in a loop including recursive loops
(CWE-120, CWE-20).
I tried following this answer, modifying the function as follows:
char * buffer;
size_t len;
// my_fd is a file descriptor
ssize_t ret = read(my_fd, &len, sizeof(len));
if (ret == -1 || ret != sizeof len) {
buffer = NULL;
} else {
buffer = malloc(len + 1);
ret = read(my_fd, buffer, len);
buffer[ret] = '\0';
}
free(buffer);
But the vulnerabilities are still detected. What am I missing?
Update #1:
I updated the function according to #4386427 suggestions, checking both read() and malloc():
char * buffer = NULL;
size_t len;
ssize_t ret = read(my_fd, &len, sizeof(len));
if (ret == sizeof len)
{
buffer = malloc(len + 1);
if (buffer != NULL)
{
ret = read(my_fd, buffer, len);
if (ret == len)
{
buffer[ret] = '\0';
}
free(buffer);
}
}
But nothing has changed, how can i further improve security?
Update #2
Because Flawfinder only does pattern check, and because it seems that no more improvements can be applied; at this point I'm marking these errors as false positive.
I see two places in your last code snippet where you don't handle return values correct. 1) You don't check the malloc 2) You don't check the read
Try:
char * buffer;
size_t len;
// my_fd is a file descriptor
ssize_t ret = read(my_fd, &len, sizeof(len));
if (ret != sizeof len) {
buffer = NULL;
} else {
buffer = malloc(len + 1);
if (buffer != NULL) // Check that malloc was ok
{
ret = read(my_fd, buffer, len);
if (ret == -1) // Check that read was ok
{
// error handling....
//
// for now just do:
ret = 0;
}
else if (ret != len)
{
// Didn't get as much data as expected
//
// Add some error handling....
}
buffer[ret] = '\0';
}
}
free(buffer);
Related
I have written the server client program using linux tcp sockets.
Client ask server for present directory files list by
sending the ls command
server replies all the list of files in server dir.
I was testing it for more files in server working dir.
server response format in the buffer
file/dir [tab] file_name [tab] file_change_time
for each 1000 files to client.
Server Sending Code:
#define BUFSIZE 1400
void lsfun(node_t *pclient)
{
DIR *directory;
int status;
int cpylen = 0;
int msglen = 0;
unsigned int tt_count = 0;
unsigned int no_files = 0;
unsigned int no_sends = 0;
int clientfd = *(pclient->client_socket);
char *filectime;
char *buffer = malloc(BUFSIZE * sizeof(char));
char *tmp = malloc(BUFSIZE * sizeof(char));
char ending[] = "#####";
struct dirent *dir;
struct stat type;
pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);
if(tmp == NULL || buffer == NULL)
printf("malloc error for client conn:%d\n", clientfd);
if(directory)
{
while((dir = readdir(directory)) != NULL)
{
if(!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))
continue;
status = stat(dir->d_name, &type);
if(status == 0)
{
filectime = ctime(&type.st_ctime);
if(dir->d_type != DT_REG)
cpylen = snprintf(tmp, BUFSIZE, "dir\t%s\t%s", dir->d_name, filectime);
else
cpylen = snprintf(tmp, BUFSIZE, "file\t%s\t%s", dir->d_name, filectime);
tmp[cpylen] = 0;
if((cpylen + msglen) < BUFSIZE)
{
strlcpy(buffer + msglen, tmp, cpylen);
msglen += cpylen;
no_files += 1;
}
else
{
tt_count += msglen;
printf("%s", buffer);
fflush(stdout);
send(clientfd, buffer, strlen(buffer), 0);
memset(buffer, 0, BUFSIZE + 5);
snprintf(buffer, cpylen, "%s", tmp);
msglen = cpylen;
cpylen = 0;
no_files += 1;
no_sends += 1;
}
}
else
{
cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
msglen += cpylen;
}
memset(tmp, 0, BUFSIZE);
}
}
cpylen = strlen(buffer);
if(msglen == cpylen)
send(clientfd, buffer, strlen(buffer), 0);
send(clientfd, ending, strlen(ending), 0); //sending msg ending for client read to close
printf("\nlssize :%d\tnofile:%d, msglen:%d\tcpylen:%d\tno_sends:%d\n", tt_count + msglen, no_files, msglen, cpylen, no_sends);
free(tmp);
free(buffer);
closedir(directory);
}
Client receiving Code:
#define BUFSIZE 1400
while(true)
{
msgsize = read(socketfd, buffer, BUFSIZE);
buffer[msgsize] = 0;
snprintf(ending, 6, "%s", buffer + (strlen(buffer) - 5));
if(strcmp(ending, "#####") == 0)
{
buffer[strlen(buffer) - 5] = 0;
if(buffer[strlen(buffer) - 1] == '\n')
printf("%s", buffer);
else
printf("%s\n", buffer);
fflush(stdout);
break;
}
else
{
printf("%s", buffer);
memset(buffer, 0, BUFSIZE);
}
}
Server replay debug print:
lssize :19931 nofile:501, msglen:437 cpylen:39 no_sends:14
why am I only receiving two packet instead of 14 packets from
server packets of around 1400 bytes each ?
where is the mistake ?
also welcome any code improvement suggestions.
In addition to the bugs pointed out in the comments, there are more fundamental issues with your code that are too extensive to merely comment on.
This code implies multithreaded use:
pthread_mutex_lock(&lock);
chdir(pclient->pwd);
directory = opendir(".");
pthread_mutex_unlock(&lock);
BUT, this code assumes the current working directory is always the current function's pclient->pwd:
status = stat(dir->d_name, &type);
It won't be if another thread calls chdir() to another directory while the loop is running, so your results
stat() always checks a relative path from the current working directory of the entire process. Which, in your posted code, can change.
A good rule to follow is to NEVER do things that change any global property of a process if you're writing multithreaded code.
Your use of snprintf() is also bug-prone. For example:
cpylen = snprintf(buffer + msglen, BUFSIZE, "%s%s\n", "file stat error:", dir->d_name);
Per 7.21.6.5 The snprintf function, paragraph 2 of the C11 standard(bolding mine):
The snprintf function returns the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n.
You are blindly assuming that every one of your calls to snprintf() work. If any one call to snprintf() fails, your message contents are wildly indeterminate and your value for msglen will not accurately reflect the contents of your buffer.
That means this code won't send anything:
cpylen = strlen(buffer);
if(msglen == cpylen)
send(clientfd, buffer, strlen(buffer), 0);
I have this code and run it with Flawinder, and i get this output on the read() functions:
Check buffer boundaries if used in a loop including recursive loops
Can anyone see the problem?
#include <stdlib.h>
void func(int fd)
{
char *buf;
size_t len;
read(fd, &len, sizeof(len));
if (len > 1024)
return;
buf = malloc(len+1);
read(fd, buf, len);
buf[len] = '\0';
}
you should check the return value of read() to know whether call to read() was success or failure or if read() was interrupted by a signal then set the errno. For e.g
ssize_t ret = read(fd, &len, sizeof len);
if( (ret == -1 || ret != sizeof len) {
/* error handling #TODO */
}
Most importantly here
ret = read(fd, buf, len); /* read() may read less than len characters */
read() returns the number of bytes read, so instead of this
buf[len] = '\0';
use
buf[ret] = '\0'; /* correct way */
Sample Code
void func(int fd) { /* assume fd is a valid file descriptor */
char *buf = NULL;
size_t len;
errno = 0; /* set this to 0 */
ssize_t ret = read(fd, &len, sizeof len);
if( (ret == -1 || ret != sizeof len) {
/* error handling #TODO */
}
if (len > 1024) {
return;
}
buf = malloc(len+1);
if(buf == NULL) {
/* error handling #TODO */
}
ret = read(fd, buf, len);
if(ret!=-1) {
buf[ret] = '\0';
/* do something with buf and free it once usage is done*/
} free(buf); /* free the buf */
else { /* if read failed */
free(buf); /* free the buf */
}
}
I want to implement a simple TCP server with blocking read, that receives messages sent from a client character by character until a separator. Once a message is received, it has to wait until the next message appears. Here is my pseudocode:
// Messages sent from the client
char *message1 = "mssg1\n"
char *message2 = "mssg2\n"
// On server side
char buffer;
char completeMessage[5]
while(1){
while(buffer != '\n'){
recv(sock, &buffer, 1, 0); // 1 is the read size
if(buffer != '\n') {
printf("buffer: %c\n", buffer);
completeMessage[n] = buffer;
count ++;
}
else{
printf("Complete message: %s\n", completeMessage);
count = 0;
}
}
}
And the result is the following:
buffer: m
buffer: s
buffer: s
buffer: g
buffer: 1
Complete message: mssg1
buffer:
buffer:
buffer:
buffer:
buffer:
buffer:
// Error due to buffer overflow
I don't know why recv instead of waiting for the next message character (blocking read), it continues reading blank spaces. My questions are the following:
Is recv really a socket blocking read function?
Is there something wrong or missing in the code?
Any other suggestions for implementing this?
Is recv really a socket blocking read function?
Yes, unless you made the handle non-blocking.
Is there something wrong or missing in the code?,
You're not checking what recv returns. 0 indicates EOF, and -1 indicates an error.
You don't check how full your buffer is, so you risk buffer overflows.
You're not terminating the string in completeMessage with a NUL as required by printf %s.
Any other suggestions for implementing this?
You shouldn't read a character at a time!
#define BUFFER_SIZE (64*1024)
char* extract_string(const char* start, const char* end) {
size_t len = end - start;
char* dst = malloc(len+1);
if (dst == NULL)
return NULL;
memcpy(dst, src, len);
dst[len] = '\0';
return dst;
}
{
char buf_start[BUFFER_SIZE];
char* buf_end = buf_start + BUFFER_SIZE;
char* window_start = buf_start;
char* window_end = buf_start;
while (1) {
if (window_end == buf_end) { // No more space.
fprintf(stderr, "Overly large message");
return 0;
}
ssize_t rv = recv(sock, window_end, buf_end-window_end, 0);
if (rv == -1) { // Error.
perror("recv");
return 0;
}
if (rv == 0) { // EOF.
return 1;
}
while (rv--) {
if (*(window_end++) == '\n') {
char* msg = extract_string(window_start, window_end-1); // Excl LF.
if (msg == NULL) {
fprintf(stderr, "Out of memory");
return 0;
}
// Do something with msg
printf("Complete message: %s\n", msg);
free(msg);
window_start = window_end;
}
}
memmove(buf_start, window_start, window_end-window_start);
window_end -= (window_start - buf_start);
window_start = buf_start;
}
}
There are quite a number of problems with your code, namely that you are ignoring the return value of recv(), you are not null-terminating your buffer before printing it, and you are not protecting yourself from a buffer overflow.
Try something more like this instead:
char ch, *tmp, *message = NULL;
int ret, length = 0, allocated = 0;
while (1)
{
ret = recv(sock, &ch, 1, 0);
if (ret <= 0)
{
if (ret < 0)
printf("Read error: %d\n", errno); // or WSAGetLastError() on Windows
else
printf("Client disconnected\n");
break;
}
if (ch == '\n')
{
if ((length > 0) && (message[length-1] == '\r'))
--length;
printf("Complete message: '%.*s'\n", length, message);
length = 0;
}
else
{
printf("ch: %c\n", ch);
if (length == allocated)
{
if (length >= 5000) // some max length of your choosing...
{
printf("Message length too large!\n");
break;
}
// just for example. You should use a more robust growth algorithm in production code...
tmp = (char*) realloc(message, allocated + 10);
if (!tmp)
{
printf("Memory allocation failed\n");
break;
}
message = tmp;
allocated += 10;
}
message[length] = ch;
++length;
}
}
free(message);
Alternatively, don't read char-by-char. Read as much data as you can from the socket on any given read and store it all in a growing buffer, and then scan that buffer for complete messages, eg:
char *buffer = (char*) malloc(100);
if (!buffer)
{
printf("Memory allocation failed\n");
}
else
{
int ret, offset, remaining, inbuf = 0, allocated = 100;
char *ptr;
while (1)
{
if (inbuf == allocated)
{
if (inbuf >= 5000) // some max length of your choosing...
{
printf("Buffer length too large!\n");
break;
}
// just for example. You should use a more robust growth algorithm in production code...
tmp = (char*) realloc(buffer, allocated + 100);
if (!tmp)
{
printf("Memory allocation failed\n");
break;
}
buffer = tmp;
allocated += 100;
}
ret = recv(sock, buffer+inbuf, allocated-inbuf, 0);
if (ret <= 0)
{
if (ret < 0)
printf("Read error: %d\n", errno); // or WSAGetLastError() on Windows
else
printf("Client disconnected\n");
break;
}
printf("Received: %.*s\n", ret, buffer+inbuf);
inbuf += ret;
while (ptr = (char*)memchr(buffer, '\n', inbuf))
{
offset = (ptr-buffer);
if ((offset > 0) && (buffer[offset-1] == '\r'))
--offset;
printf("Complete message: '%.s'\n", offset, buffer);
++ptr;
remaining = (inbuf - (ptr - buffer));
if (remaining > 0)
memmove(buffer, ptr, remaining);
inbuf = remaining;
}
}
free(buffer);
}
I'm trying to get the source code of my website using c, I'm able to connect and everything but when I implement the recv() code, it only receives the last few bytes of the source code. I'd like to dynamically allocate space for the buffer to receive more using the C functions malloc and realloc.
This is the code I have so far:
char *buffer = NULL;
unsigned int i = 0;
unsigned long LEN = 200;
unsigned long cur_size = 0;
buffer = (char*)malloc(sizeof(char)*LEN);
do
{
if( status >= LEN )
{
cur_size += status;
buffer = (char*)realloc(buffer, cur_size);
}
status = recv(cSocket, buffer, LEN, 0);
if( status == 0 )
{
printf("Bye\n");
}
else if( status > 0 )
{
printf("%d\n", status);
}
else
{
printf("socket error=%d\n", WSAGetLastError());
break;
}
}while( status > 0 );
printf("%s\n", buffer);
It still doesn't print the whole source code. How should I go about this?
Pseudocode:
buffer = 'len chars';
loop:
if( status >= buffer ) buffer = 'resize to status chars';
status = recv(sock, buffer, len, 0);
end loop
As you resize the buffer in advance this needs to be reflected by its size. Which currently is not the case.
To fix this you could, for example, initialise cur_size with LEN by changing
unsigned long cur_size = 0;
to
unsigned long cur_size = LEN;
Assuming the fix above, you want to append to the buffer and not overwrite it with every call to recv().
To do so change this line
status = recv(cSocket, buffer, LEN, 0);
to be
status = recv(cSocket, buffer + cur_size - LEN, LEN, 0);
A more straight forward approach would be to not track the size of the buffer, but the number of bytes received and just always increase the buffer by a constant size.
Also the two calls to allocate memory can be replaced by one:
char *buffer = NULL;
unsigned long LEN = 200;
unsigned long bytes_received = 0;
unsigned long cur_size = 0;
int status = 0;
do
{
if (bytes_received >= cur_size)
{
char * tmp;
cur_size += LEN;
tmp = realloc(buffer, cur_size);
if (NULL == tmp)
{
fprintf(stderr, "realloc error=%d\n", WSAGetLastError());
break;
}
buffer = tmp;
}
status = recv(cSocket, buffer + bytes_received, LEN, 0);
if (status == 0)
{
printf("Bye\n");
}
else if (status > 0)
{
bytes_received += status;
printf("%d\n", status);
}
else /* < 0 */
{
fprintf(stderr, "socket error=%d\n", WSAGetLastError());
}
} while (status > 0);
printf("%s\n", buffer);
Well, after a bit of research, I came across this website and finally found what I was looking for.
Binary tides
Although it uses linux's fcntl, the windows equivalent is ioctlsocket which is used to set the socket's non-blocking mode.
To see the exact function, visit the website. I modified the version and set my socket to blocking mode.
int total_recv(SOCKET s)
{
int size_recv = 0, total_size = 0, block = 00;
char chunk[BUFLEN];
ioctlsocket(s, FIONBIO, (unsigned long*)&block); // set mode to block
// not necessary but clarification of function, mode is block by
// default
while( 1 )
{
memset(chunk, 0, BUFLEN);
if( ( size_recv = recv(s, chunk, BUFLEN, 0) ) == SOCKET_ERROR )
{
printf("Error receiving\n");
}
else if( size_recv == 0 )
{
break;
}
else
{
total_size += size_recv;
// i used file since console wouldn't show full source code
FILE *fp = NULL;
fp = fopen("source.txt", "a");
fprintf(fp, chunk);
fclose(fp);
}
}
return total_size;
}
So I programmed a multi threaded web server, here is one function from the program. This function takes output file descriptor (fd), content type, pointer to data to be served (*buf) and size of the data (numbytes). It always gets stuck at 5775 bytes! I've tried using write() instead of send(), but no avail! I tried to send whole buf at a time, and even tried to transfer it in chunks, but wget shows that it gets stck at 5775 bytes! Here is the code:
int return_result(int fd, char *content_type, char *buf, int numbytes)
{
char out_buf[BUF_SIZE], numb[6];
int buf_len, total = 0, buf_size;
long int i = 0;
sprintf(numb, "%d", numbytes);
strcpy(out_buf, "HTTP/1.1 200 OK \nContent-Type: ");
strcat(out_buf, content_type);
strcat(out_buf, "\nContent-Length: ");
strcat(out_buf, numb);
strcat(out_buf, "\nConnection: Close\n \n");
printf("\nSending HTTP Header\n %d bytes sent!",
send(fd, out_buf, strlen(out_buf), 0));
char *start = NULL, *str = NULL, *temp = NULL;
start = buf;
printf("\n Start Pointer Val = %ld", &start);
while (start != NULL) {
printf("\n While Loop");
if (i + 2048 * sizeof(char) < numbytes) {
printf("\n If 1");
str = (char *)malloc(sizeof(char) * 2048);
memcpy(str, start, sizeof(char) * 2048);
i = i + 2048 * sizeof(char);
buf_size = send(fd, str, 2048, 0);
free(str);
printf("\n Sent %d bytes total : %d", buf_size, total =
total + buf_size);
temp = start + sizeof(char) * 2048;
start = temp;
} else {
i = numbytes - i * sizeof(char);
if (i > 0) {
printf("\n If 2");
printf("\n Value of i %d", i);
str = (char *)malloc(sizeof(char) * i);
memcpy(str, start, sizeof(char) * i);
printf("Total bytes finally sent:%d", total =
total + send(fd, str, i, 0));
if (total == numbytes) {
printf("\nTransfer Complete!");
}
free(str);
}
start = NULL;
}
}
printf("out of loop!");
return 0;
}
I'd like to suggest replacing your code with the following writen() function from Advanced Programming in the Unix Environment, 2nd edition:
ssize_t /* Write "n" bytes to a descriptor */
writen(int fd, const void *ptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) < 0) {
if (nleft == n)
return(-1); /* error, return -1 */
else
break; /* error, return amount written so far */
} else if (nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft); /* return >= 0 */
}
This code is already debugged and known working, and further allows write(2) to write PIPE_BUF bytes at a go for better speed when things are working well.
send(2) should block if it cannot send all the data you have requested, though. I think more interesting would be debugging the version with plain send(2) without any of the surrounding efforts to break things into blocks.
Better than both write(2) and send(2) would be sendfile(2) -- open the file, pass the descriptor and socket to sendfile(2), and let the kernel handle it all for you, using zero-copy mechanisms if possible.
One last point: HTTP uses CRLF, not plain carriage returns. Each \n should be replaced with \r\n.
Try something like this (printf() statements omitted for clarity):
int send_buf(in fd, void *buf, int numbytes)
{
char *start = (char*) buf;
while (numbytes > 0)
{
int sent = send(fd, start, numbytes, 0);
if (sent <= 0)
{
if ((sent == -1) && (errno == EAGAIN))
{
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
if (select(fd + 1, NULL, &wfds, NULL, NULL) == 1)
continue;
}
return -1;
}
start += sent;
numbytes -= sent;
}
return 0;
}
int return_result(int fd, char *content_type, void *buf, int numbytes)
{
char out_buf[BUF_SIZE],
int len = sprintf(out_buf,
"HTTP/1.1 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: Close\r\n"
"\r\n",
content_type,
numb);
if (send_buf(fd, out_buf, len) != 0)
return -1;
if (send_buf(fd, buf, numbytes) != 0)
return -1;
return 0;
}