I'm curious why when reading less than 4 bytes from a file, the output is corrupted.
Here is my test file:
user#UnixVM:~/labs$ cat foo.txt
helloworld
And a simple program to read from the file:
int main()
{
int file=0;
if((file=open("foo.txt",O_RDONLY)) < -1)
return 1;
char buffer[11];
read(file,buffer,3);
printf("%s\n",buffer);
return 0;
}
The output is corrupted and may be different between executions:
user#UnixVM:~/labs$ gcc -Wall lab1_4.c -o lab1_4 ; ./lab1_4
hel2
user#UnixVM:~/labs$ gcc -Wall lab1_4.c -o lab1_4 ; ./lab1_4
helâ–’
But every time I make number of bytes to read greater or equal to 4 (read(file,buffer,4);), it works fine.
Your output is "corrupted" because buffer does not contain a NUL terminated C string. Read more about undefined behavior. Be scared (UB sometimes appears to work, and that might explain what you experiment).
So before your call to read add memset(buffer, 0, sizeof(buffer)) to clear your buffer. Or initialize it with char buffer[11] =""; (both are nearly equivalent and likely, with optimizations enabled e.g. gcc -O2, to generate the same machine code). Since your buffer is 11 bytes long and you read at most 3 bytes you'll then be sure that it is NUL terminated after the read.
Be sure to compile with all warnings and debug info (so gcc -Wall -Wextra -g lab1_4.c -o lab1_4 in your case). Read How to debug small programs
Read carefully the documentation of read(2) and of every function you are using. Notice the return count from read. You should test and use it.
Related
This is my program code:
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <sys/types.h>
void function() {
srand(time(NULL));
while(1) {
int n = rand();
printf("%d ", n);
//sleep(1);
}
}
int main() {
pid_t pid;
pid = fork();
if (pid == 0) {
function();
}
}
With the sleep line commented out (as in the code above) the program works fine (i.e. it prints a bunch of random numbers too fast to even see if they are actually random), but if I remove the comment the program doesn't print anything and exits (not even the first time, before it gets to the sleep), even though it compiles without warnings or errors with or without the comment.
but if I remove the comment the program doesn't print anything and exits
It does not print, but it does not really exit either. It will still be running a process in the background. And that process runs your infinite while loop.
Using your code in p.c:
$ gcc p.c
$ ./a.out
$ ps -A | grep a.out
267282 pts/0 00:00:00 a.out
$ killall a.out
$ killall a.out
a.out: no process found
The problem is that printf does not really print. It only sends data to the output buffer. In order to force the output buffer to be printed, invoke fflush(stdout)
If you're not flushing, then you just rely on the behavior of the terminal you're using. It's very common for terminals to flush when you write a newline character to the output stream. That's one reason why it's preferable to use printf("data\n") instead of printf("\ndata"). See this question for more info: https://softwareengineering.stackexchange.com/q/381711/283695
I'd suspect that if you just leave your program running, it will eventually print. It makes sense that it has a finite buffer and that it flushes when it gets full. But that's just an (educated) guess, and it depends on your terminal.
it prints a bunch of random numbers too fast to even see if they are actually random
How do you see if a sequence of numbers is random? (Playing the devils advocate)
I believe you need to call fflush(3) from time to time. See also setvbuf(3) and stdio(3) and sysconf(3).
I guess that if you coded:
while(1) {
int n = rand();
printf("%d ", n);
if (n % 4 == 0)
fflush(NULL);
sleep(1);
}
The behavior of your program might be more user friendly. The buffer of stdout might have several dozens of kilobytes at least.
BTW, I could be wrong. Check by reading a recent C draft standard (perhaps n2176).
At the very least, see this C reference website then syscalls(2), fork(2) and sleep(3).
You need to call waitpid(2) or a similar function for every successful fork(2).
If on Linux, read also Advanced Linux Programming and use both strace(1) and gdb(1) to understand the behavior of your program. With GCC don't forget to compile it as gcc -Wall -Wextra -g to get all warnings and debug info.
Consider also using the Clang static analyzer.
It seems that I'm completely misusing mkstemp. However I use it, I always get a segfault. I compiled the most basic program below with gcc -ggdb -Wall -Werror main.c and ran it with ./a.out
#include <stdlib.h>
int main(int argc, char **argv) {
mkstemp("XXXXXX");
return 0;
}
This always returns the returncode 139 and it prints [1] 23532 segmentation fault ./a.out on the terminal. (23532 always changes because it's the pid).
I tried:
switching the flags of gcc (none at all, a lot of combinations of the previous flags, -Wextra and -O0)
Changing the code by saving the resulting filedescriptor in a int, sleeping 5 seconds and closing the filedescriptor again. But I don't even reach the start of the sleep...
And now I'm out of ideas...
From the man page:
The last six characters of template must be "XXXXXX" and these are
replaced with a string that makes the filename unique. Since it will
be modified, template must not be a string constant, but should be
declared as a character array.
So you need to declare a character array:
char filename[] = "fileXXXXXX";
mkstemp(filename);
In the book I am reading, Software Exorcism, has this example code for a buffer overflow:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 4
void victim(char *str)
{
char buffer[BUFFER_SIZE];
strcpy(buffer,str);
return;
}
void redirected()
{
printf("\tYou've been redirected!\n");
exit(0);
return;
}
void main()
{
char buffer[]=
{
'1','2','3','4',
'5','6','7','8',
'\x0','\x0','\x0','\x0','\x0'
};
void *fptr;
unsigned long *lptr;
printf("buffer = %s\n", buffer);
fptr = redirected;
lptr = (unsigned long*)(&buffer[8]);
*lptr = (unsigned long)fptr;
printf("main()\n");
victim(buffer);
printf("main()\n");
return;
}
I can get this to work in Windows with Visual Studio 2010 by specifying
Basic Runtime Checks -> Uninitialized variables
Buffer Security Check -> No
With those compile options, I get this behavior when running:
buffer = 12345678
main()
You've been redirected!
My question is about the code not working on Linux. Is there any clear reason why it is so?
Some info on what I've tried:
I've tried to run this with 32-bit Ubuntu 12.04 (downloaded from here), with these options:
[09/01/2014 11:46] root#ubuntu:/home/seed# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
Getting:
[09/01/2014 12:03] seed#ubuntu:~$ gcc -fno-stack-protector -z execstack -o overflow overflow.c
[09/01/2014 12:03] seed#ubuntu:~$ ./overflow
buffer = 12345678
main()
main()
Segmentation fault (core dumped)
And with 64-bit CentOS 6.0, with these options:
[root]# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
[root]# sysctl -w kernel.exec-shield=0
kernel.exec-shield = 0
Getting:
[root]# gcc -fno-stack-protector -z execstack -o overflow overflow.c
[root]# ./overflow
buffer = 12345678
main()
main()
[root]#
Is there something fundamentally different in Linux environment, which would cause the example not working, or am I missing something simple here?
Note: I've been through the related questions such as this one and this one, but haven't been able to find anything that would help on this. I don't think this is a duplicate of previous questions even though there are a lot of them.
Your example overflows the stack, a small and predictable memory layout, in an attempt to modify the return address of the function void victim(), which would then point to void redirected() instead of coming back to main().
It works with Visual. But GCC is a different compiler, and can use some different stack allocation rule, making the exploit fail. C doesn't enforce a strict "stack memory layout", so compilers can make different choices.
A good way to view this hypothesis is to test your code using MinGW (aka GCC for Windows), proving the behavior difference is not related strictly to the OS.
#define BUFFER_SIZE 4
void victim(char *str)
{
char buffer[BUFFER_SIZE];
strcpy(buffer,str);
return;
}
There is another potential problem here if optimizations are enabled. buffer is 12 bytes, and its called as victim(buffer). Then, within victim, you try to copy 12 bytes into a 4 byte buffer with strcpy.
FORTIFY_SOURCES should cause the program to seg fault on the call to strcpy. If the compiler can deduce the destination buffer size (which it should in this case), then the compiler will replace strcpy with a "safer" version that includes the destination buffer size. If the bytes to copy exceeds the destination buffer size, then the "safer" strcpy will call abort().
To turn off FORTIFY_SOURCES, then compile with -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0.
I want to establish build-time cross-language ABI compatibility with Waf. How would I go about checking the size of a type (or any arbitrary type-like checks), and recording it into the build configuration?
You can create a program which outputs that size. I saw this apporach on several ./configure files:
cat << EOF > test.c
#include <stdio.h>
int main ()
{
printf("int %d\n", sizeof(int));
return 0;
}
EOF
$(CC) test.c -o out
./out >> sizes
rm -f test.c out
Of course, with testing of erroneous cases and such.
Edit: See the waf documentation. Specially, read the run_c_code method. For saving the value of sizeof you can write to a file instead and read it from Python/Waf. Or, see the define_ret argument.
The line of code causing the problem is
char command_tb_temp[][1000]={"gcc -Wall ","-o3 -ftree-ccp -fno-align-jumps "," Scripts/*.c -o output -lm && time -f \"%e\" -o TB.log ./output 1.dat"};
When the same code is written by giving only 1 optimizing option like below, It does not return any errors.
char command_tb_temp[][1000]={"gcc -Wall ","-o3 -ftree-ccp "," Scripts/*.c -o output -lm && time -f \"%e\" -o TB.log ./output 1.dat"};
Please help me resolve this issue. Any help is highly appreciated.
Thankyou.
Heres the whole function..
int findtb(int flag)
{
printf("debug -1-1-1");
char command_tb_temp[][1000]={"gcc -Wall ","-o3 -ftree-ccp -fno-align-jumps "," Scripts/*.c -o output -lm && time -f \"%e\" -o TB.log ./output 1.dat"};
char command_tb[]="",line[100];
if(var[initial].exetime>0.00&&flag==1)
{
if(var[initial].rip<0.00)
strcat(finalop,var[initial].name);
else
return 0;
}
strcpy(command_tb_temp[1],finalop);
//strcat(command_tb_temp[1]," -ftree-ccp ");
for(int i=0;i<3;i++)
strcat(command_tb,command_tb_temp[i]);
printf("***** %s ****",command_tb);
system(command_tb);
fp=fopen("TB.log","r");
fscanf(fp,"%s",line);
tb=atof(line);
printf("\nTb=%f\n",tb);
fclose(fp);
return 1;
}
The error is...
*** stack smashing detected ***: ./3 terminated
char command_tb[] = "" defines a character array of size 1, containing just a terminating null character.
strcat(command_tb,command_tb_temp[i]); then writes data to it.
But it writes more data than it can hold, thus corrupting other parts of memory.
You should make it large enough.
Also, it's advisable not to use strcat, strcpy, as they can easily exceed the buffer. Better use strncpy at others, which get the buffer size and won't write more. It's still your responsibility to provide the right size.
But beware of strncat - the meaning of its size parameter is misleading, so read its documentation with care, or just avoid using it.