There are three types of standards behaviour you should be interested in.
1/ Defined behaviour. This will work on all complying implementations. Use this freely.
2/ Implementation-defined behaviour. As stated, it depends on the implementation but at least it’s still defined. Implementations are required to document what they do in these cases. Use this if you don’t care about portability.
3/ Undefined behaviour. Anything can happen. And we mean anything, up to and including your entire computer collapsing into a naked singularity and swallowing itself, you and a large proportion of your workmates. Never use this. Ever! Seriously! Don’t make me come over there.
Copying more that 4 characters and a zero-byte to a char[5]
is undefined behaviour.
Seriously, it doesn’t matter why your program crashes with 14 characters but not 13, you’re almost certainly overwriting some non-crashing information on the stack and your program will most likely produce incorrect results anyway. In fact, the crash is better since at least it stops you relying on the possibly bad effects.
Increase the size of the array to something more suitable (char[14]
in this case with the available information) or use some other data structure that can cope.
Update:
Since you seem so concerned with finding out why an extra 7 characters doesn’t cause problems but 8 characters does, let’s envisage the possible stack layout on entering main()
. I say «possible» since the actual layout depends on the calling convention that your compiler uses. Since the C start-up code calls main()
with argc
and argv
, the stack at the start of main()
, after allocating space for a char[5]
, could look like this:
+------------------------------------+
| C start-up code return address (4) |
| argc (4) |
| argv (4) |
| x = char[5] (5) |
+------------------------------------+
When you write the bytes Hello1234567\0
with:
strcpy (x, "Hello1234567");
to x
, it overwrites the argc
and argv
but, on return from main()
, that’s okay. Specifically Hello
populates x
, 1234
populates argv
and 567\0
populates argc
. Provided you don’t actually try to use argc
and/or argv
after that, you’ll be okay:
+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4) | '567<NUL>'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
However, if you write Hello12345678\0
(note the extra «8») to x
, it overwrites the argc
and argv
and also one byte of the return address so that, when main()
attempts to return to the C start-up code, it goes off into fairy land instead:
+------------------------------------+ Overwrites with:
| C start-up code return address (4) | '<NUL>'
| argc (4) | '5678'
| argv (4) | '1234'
| x = char[5] (5) | 'Hello'
+------------------------------------+
Again, this depends entirely on the calling convention of your compiler. It’s possible a different compiler would always pad out arrays to a multiple of 4 bytes and the code wouldn’t fail there until you wrote another three characters. Even the same compiler may allocate variables on the stack frame differently to ensure alignment is satisfied.
That’s what they mean by undefined: you don’t know what’s going to happen.
There are actually three segmentation faults here:
fread(content,1,lsize,file_pointer);
strcpy(temp,buffer);
row = strtok(temp,"\n");
The first one is fread()
which is attempting to write to memory that does not yet exist as far as your process is concerned.
The second one is strcpy()
, (expounding on the first) you are attempting to copy to a pointer that points to nothing. No memory (other than the pointer reference itself) has been allocated for temp
, statically or dynamically.
Fix this via changing temp
to look like this (allocating it statically):
char temp[1024];
Or use malloc()
to dynamically allocate memory for it (as well as your other pointers, so they actually point to something), likewise for content
. If you know the needed buffer size at compile time, use static allocation. If not, use malloc()
. ‘Knowing’ is the subject of another question.
The third one is strtok()
, which is going to modify temp
en situ (in place), which it obviously can not do, since temp
was never allocated. In any event, don’t expect temp
to be the same once strtok()
is done with it. By the name of the variable, I assume you know that.
Also, Initializing a pointer is not the same thing as allocating memory for it:
char *temp = NULL; // temp is initialized
char *temp = (char *) malloc(size); // temp is allocated if malloc returns agreeably, cast return to not break c++
Finally, please get in the habit of using strncpy()
over strcpy()
, its much safer.
I have this code that I need to use to perform a ret2libc
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buf[256];
printf("buff is at:%p\n",buf);
printf("%s",argv[1]);
strcpy(buf, argv[1]);
printf(buf);
}
I compile it as gcc -m32 -fno-stack-protector ./rt2.c -ort2
and than start it with a cyclic patter (generated with pwntools) as follows:
./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
I was expecting a segmentation fault at the end of the main…it instead happens at the end of the strcpy…actually what happens (by looking with gdb) is that at the end of the strcpy the ESP points to 0x6361616e which is part of the input string.
Another strange thing is that printf before the strcpy does not print out anything
this is the result of the execution:
$ ./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
buff is at:0xffffcf20
Segmentation fault (core dumped)
$ dmesg | tail -1
[ 4432.704356] rt2[3280]: segfault at 6361616e ip 00000000565555e0 sp 000000006361616e error 4 in rt2[56555000+1000]
Even more strange, to me, if I comment the strcpy the printf before of it does instead print out…
I tried to execute it with with valgrind and it confirmed that at some point ESP got overwritten.
valgrind ./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
==2735== Memcheck, a memory error detector
==2735== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2735== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2735== Command: ./rt2 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
==2735==
buff is at:0xfeffce80
==2735== Warning: client switching stacks? SP change: 0xfeffcf98 --> 0x6361616e
==2735== to suppress, use: --max-stackframe=1684115926 or greater
==2735== Invalid read of size 4
==2735== at 0x1085E0: main (rt2.c:20)
==2735== Address 0x6361616e is on thread 1's stack
==2735==
==2735==
==2735== Process terminating with default action of signal 11 (SIGSEGV)
==2735== Access not within mapped region at address 0x6361616E
==2735== at 0x1085E0: main (rt2.c:20)
==2735== If you believe this happened as a result of a stack
==2735== overflow in your program's main thread (unlikely but
==2735== possible), you can try to increase the size of the
==2735== main thread stack using the --main-stacksize= flag.
==2735== The main thread stack size used in this run was 8388608.
--2735-- VALGRIND INTERNAL ERROR: Valgrind received a signal 11 (SIGSEGV) - exiting
--2735-- si_code=1; Faulting address: 0x6361616E; sp: 0x82d8cf20
valgrind: the 'impossible' happened:
Killed by fatal signal
host stacktrace:
==2735== at 0x5803F9F6: ??? (in /usr/lib/valgrind/memcheck-x86-linux)
sched status:
running_tid=1
Thread 1: status = VgTs_Runnable (lwpid 2735)
==2735== at 0x482A4D0: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-x86-linux.so)
I also tried to compile with as: gcc -g -m32 -fno-stack-protector -z stack-size=4194304 ./rt2.c -ort2
but nothing changed
Segmentation faults are common errors that occur when a program tries to access memory that does not belong to it. This error can be caused by many things, but one common culprit is the inappropriate use of the strcpy
function.
In C programming, strcpy
is used for copying one string to another. However, if not used correctly, it can lead to a segmentation fault. The following are some ways to fix segmentation faults when using strcpy
.
1. Use strncpy
instead
One way to avoid segmentation faults when using strcpy
is to use strncpy
instead. strncpy
is a safer function that copies a specified number of characters from one string to another, while strcpy
is not limited in the number of characters it copies.
char dest[100] = "hello";
char src[] = "world";
strncpy(dest, src, sizeof(dest)-1);
dest[sizeof(dest)-1] = '\0';
In the above example, strncpy
copies the entire src
string into dest
, but only up to the size of dest
minus one character (to leave room for the null terminator). It then adds a null terminator to the end of dest
. This ensures that dest
does not exceed its boundary, avoiding a segmentation fault.
2. Use strlcpy
Another way to avoid segmentation faults when using strcpy
is to use strlcpy
. strlcpy
is a function that has been created to replace strcpy
in many libraries, as it is safer than strcpy
.
char dest[100] = "hello";
char src[] = "world";
strlcpy(dest, src, sizeof(dest));
In the above example, strlcpy
copies the entire src
string into dest
, but only up to the size of dest
. This ensures that dest
does not exceed its boundary, avoiding a segmentation fault.
3. Check boundaries
If you must use strcpy
, it is important to check your boundaries. This means ensuring that the destination string has enough space to hold the entire source string, plus a null terminator.
char dest[10];
char *src = "this is too long";
if(sizeof(src) < sizeof(dest))
{
strcpy(dest, src);
}
In the above example, we are checking that the size of the source string is less than or equal to the size of the destination string. If it is, we can safely use strcpy
. If not, we could use strncpy
or strlcpy
.
4. Use malloc
If the size of the string is unknown, you can allocate memory dynamically using malloc
. This ensures that you are not limited by the size of the destination string.
char *dest = malloc(strlen(src)+1);
strcpy(dest, src);
In the above example, we are using malloc
to allocate enough memory to hold the entire source string plus a null terminator. We can then safely use strcpy
to copy the string into dest
.
In conclusion, segmentation faults can occur when using strcpy
in C programming, but there are ways to avoid them. You can use strncpy
or strlcpy
instead of strcpy
, check boundaries before using strcpy
, or allocate memory dynamically using malloc
. By following these tips, you can write safer code and avoid segmentation faults.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
typedef struct client{
int threadid;
int argc;
char *argv[3];
}client;
void exit(int status);
void error(char *msg);
void *threadClient(void *socket_desc);
int main(int argc, char *argv[]){
client info[10];
pthread_t thread[10];
printf("%s\n%s\n%s\n", argv[0], argv[1], argv[2]);
// Error happens here
for (int i=0; i<=10; i++){
info[i].threadid = i;
strcpy(info[i].argv[0], argv[0]);
strcpy(info[i].argv[1], argv[1]);
strcpy(info[i].argv[2], argv[2]);
info[i].argc = argc;
printf("here");
if( pthread_create( &thread[i] , NULL , threadClient , (void*)&info[i]) < 0){
perror("could not create thread");
return 1;
}
sleep(3);
}
pthread_exit(NULL);
return 0;
}
During the loop, when I am trying to copy the info from argv
to my struct I get a segmentation fault. Why does it happen?
Program received signal SIGSEGV, Segmentation fault. __strcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:296 296 ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.
There are two problem here.
First, your argv
array in your info
structure is an array of pointers. These start out uninitialized. When you later call strcpy
, giving one of these array elements as the first parameter, it expects that pointer to point to valid memory. So you end up dereferencing an uninitialized pointer. This invokes undefined behavior, which in this case manifests as a segfault.
You need to assign something to these pointers. You can either use strdup
to make a copy of these strings:
info[i].argv[0] = strdup(argv[0]);
info[i].argv[1] = strdup(argv[1]);
info[i].argv[2] = strdup(argv[2]);
Or, if you don’t plan on modifying these values, you can just copy the pointer values directly:
info[i].argv[0] = argv[0];
info[i].argv[1] = argv[1];
info[i].argv[2] = argv[2];
The second issue is an off-by-one error in your loop:
for (int i=0; i<=10; i++){
Because you use <=
, your indexes into the array will range from 0 to 10. However, your array only has 10 elements (with indexes 0 to 9), so you’re writing past the end of the array. This also invokes undefined behavior.
Change your conditional to <
as follows:
for (int i=0; i<10; i++){