Understanding malloc with char pointers and strcpy - c

I want to write a simple program and I want it to fail to understand strcpy and proper memory management but it still executes. I am trying to dynamically allocate memory (with malloc) for a string enough for only 3 (or anything less than the source) chars to be used as the destination and allocate less memory (or chars) than the string array of the source which is allocated on the stack (string of 10 chars). It copies and prints the content no matter how I define the memory allocation. what's the mistake here?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *ch = NULL;
char name[10] = "something";
ch = (char*)malloc(3 * sizeof(char));
strcpy(ch, name);
printf("%s\n", name);
free(ch);
ch = NULL;
}

The behaviour of writing array out of bounds is undefined. On my computer, without optimization:
% gcc overflow.c
% ./a.out
something
and with optimization enabled
% gcc -O3 overflow.c
In file included from /usr/include/string.h:495,
from overflow.c:2:
In function ‘strcpy’,
inlined from ‘main’ at overflow.c:10:5:
/usr/include/x86_64-linux-gnu/bits/string_fortified.h:90:10: warning:
‘__builtin___memcpy_chk’ writing 10 bytes into a region of size 3
overflows the destination [-Wstringop-overflow=]
90 | return __builtin___strcpy_chk (__dest, __src, __bos (__dest));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% ./a.out
*** buffer overflow detected ***: terminated
[2] 240741 abort (core dumped) ./a.out
The reason is that with optimization GCC will actually propagate the array sizes and will produce machine code equivalent to
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char name[10] = "something";
char *ch = malloc(3);
__memcpy_chk(ch, name, sizeof name, 3);
puts(name);
free(ch);
}
and __memcpy_chk will check the length of the destination buffer, and since it is exceeded, the program will abort at runtime.
Always when developing, remember to test your code optimizations enabled too!

The mistake here is you're relying on undefined behaviour.
Compiler does not throw any error for out-of-bound (or invalid) memory access, but when your program try to access an invalid memory, the behaviour is undefined.
For strcpy(), in case the destination buffer is not large enough to hold the content from the source, including the null-terminator, then essentially you'll be accessing memory out of bound, which is invalid, causing the undefined behaviour. It's the responsibility of the programmer to ensure the destination buffer has enough space to store the string to be copied.
From the man page
The strcpy() function copies the string pointed to by src, including the terminating null byte ('\0'), to the buffer pointed to by dest. The strings may not overlap, and the destination string dest must be large enough to receive the copy.[....]

Related

Memory confusion for strncpy in C

This week one problem was discussed by my colleague regarding memory:
Sample code 1:
int main()
{
#define Str "This is String."
char dest[1];
char buff[10];
strncpy(dest, Str, sizeof(Str));
printf("Dest: %s\n", dest);
printf("Buff: %s\n", buff);
}
Output:
Dest: This is String.
Buff: his is String.
Sample Code 2:
int main()
{
#define Str "This is String."
char dest[1];
//char buff[10];
strncpy(dest, Str, sizeof(Str));
printf("Dest: %s\n", dest);
//printf("Buff: %s\n", buff);
}
Output:
Dest: This is String.
*** stack smashing detected ***: ./test terminated
Aborted (core dumped)
I am not understanding why i am getting that output in case 1? as buff is not even used in strncpy, and if i comment variable buff it will give stack smashing detected but with output for dest.
Also for buff why i am getting Output as "his as string."
This is an interesting problem that we all wish to understand at some point or the other. The problem that occurs here is known as “Buffer Overflow”. The side effects of this problem can vary from system to system (also referred as undefined behavior). Just to explain you what might be happening in your case lets assume that the memory layout of the variables in your program is as below
Note above representation is just for understanding and doesn't show actual representation for any architecture.
After the strncpy command is executed the contents of this memory region are as below
Now when you print buff you can see that the start address of buf now has 'h' in it. The printf starts printing this until it finds a null character which is past the buff memory region. Hence you get 'his is String' when you print buf.
However note that program 1 doesn't generate a stack smashing error because of stack guard (which is system/implementation) dependent. So if you execute this code on a system that doesn't include this the Program 1 will also crash (You can test this by increasing Str to a long string).
In case of Program 2 the strncpy just goes past the stack guard over writing the return address from main and hence you get a crash.
Hope this helps.
P.S. All above description is for understanding and doesn't show any actual system representation.
The C Standard specifies strncpy this way:
7.24.2.4 The strncpy function
Synopsis
#include <string.h>
char *strncpy(char * restrict s1,
const char * restrict s2,
size_t n);
Description
The strncpy function copies not more than n characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by s1.
If copying takes place between objects that overlap, the behavior is undefined.
If the array pointed to by s2 is a string that is shorter than n characters, null characters are appended to the copy in the array pointed to by s1, until n characters in all have been written.
Returns
The strncpy function returns the value of s1.
These semantics are widely misunderstood: strncpy is not a safe version of strcpy, the destination array is NOT null terminated if the source string is longer than the n argument.
In your example, this n argument is larger than the size of the destination array: the behavior is undefined because characters are written beyond the end of the destination array.
You can observe this is the first example as the buff array is positioned by the compiler just after the end of the dest array in automatic storage (aka on the stack) and is overwritten by strncpy. The compiler could use a different method so the observed behavior is by no means guaranteed.
My advice is to NEVER USE THIS FUNCTION. An opinion shared by other C experts such as Bruce Dawson: Stop using strncpy already!
You should favor a less error-prone function such as this one:
// Utility function: copy with truncation, return source string length
// truncation occurred if return value >= size argument
size_t bstrcpy(char *dest, size_t size, const char *src) {
size_t i;
/* copy the portion that fits */
for (i = 0; i + 1 < size && src[i] != '\0'; i++) {
dest[i] = src[i];
}
/* null terminate destination unless size == 0 */
if (i < size) {
dest[i] = '\0';
}
/* compute necessary length to allow truncation detection */
while (src[i] != '\0') {
i++;
}
return i;
}
You would use it this way in your example:
int main(void) {
#define Str "This is String."
char dest[12];
// the size of the destination array is passed
// after the pointer, just as for `snprintf`
bstrcpy(dest, sizeof dest, Str);
printf("Dest: %s\n", dest);
return 0;
}
Output:
This is a S
strncpy(dest, Str, sizeof(Str));
Your dest is only one byte, so here you are writing in memory which you are not supposed to and this invokes undefined behavior. In other words, anything can happen depending on how compiler implement these things.
The most probable reason for buf getting written is that the compiler places dest after buf. So when you are writing past the boundary of dest you are writing to buf. When you comment out buf it leads to crash.
But as I said before, you may get completely different behavior if a different compiler or even different version of same compiler is used.
Summary: Never do anything that invokes undefined behavior. In strncpy you are supposed to use sizeof(dest), not sizeof(src) and allocate sufficient memory for destination so that data from source is not lost.
The location of the variables on your stack is :-
0. dest
1. buff
12. canary
16. Return address
When buff is present, it protects the canary and return address from damage.
This is undefined behavior (writing more data into dest than fits). The canary has a special random value within it, that is set up when the function starts, and is tested before executing the return instruction. This adds some form of protection to buffer overruns.
Examples of undefined nature, is that the program may have crashed with "illegal instruction # xxxxxx" due to not having a canary.
The program may have behaved normally, if the return address was separate from the variable location.
The stack will typically grow in a negative direction on most current CPUs. Also the location of dest vs buff is compiler dependent. It may have switched them round, or if (for example) you took away the second printf, the compiler may have removed the storage for dest, as it may have decided it was not correctly used.

My program is crashing when it calls `strcpy()' [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *str1 = "Bangladesh Bogus Party";
char *str2 = "Sad is a beautiful Country";
strcpy(str1 + 11, str2 + 4);
strcat(str1, "! !! !!!");
printf("\n%s", str1);
return 0;
}
In the code blocks ,it has found....
-------------- Build: Debug in catcpy (compiler: GNU GCC Compiler)---------------
Target is up to date.
Nothing to be done (all items are up-to-date).
-------------- Run: Debug in catcpy (compiler: GNU GCC Compiler)---------------
Checking for existence: E:\Sobar Jonno C (Niton)\String\catcpy\bin\Debug\catcpy.exe
Executing: "C:\Program Files (x86)\CodeBlocks/cb_console_runner.exe" "E:\Sobar Jonno C (Niton)\String\catcpy\bin\Debug\catcpy.exe" (in E:\Sobar Jonno C (Niton)\String\catcpy\.)
You can't overwrite your str1 pointer to a string literal because they are read only memory and your program is trying to write to the memory the pointer points to which causes Undefined Behavior most of the time it causes a Segmentation Fault. And even worst you can't concatenate without previously allocating the necessary space.
If you want your code to work, you need something like this
#include <stdio.h>
#include <string.h>
int main(void)
{
char str1[42] = "Bangladesh Bogus Party";
const char *str2 = "Sad is a beautiful country";
strcpy(str1 + 11, str2 + 4);
strcat(str1, "! !! !!!");
printf("%s\n", str1);
return 0;
}
Note that str1 is no longer a string literal, whil str2 is. That is because str2 is never modified, and I added the const specifier to prevent from accidentally doing it. It's impossible to completely prevent this but const can help you notice when you do it unintentionally.
The main issue is that str1 points to a string literal. These live in a read-only section of memory, so attempting to change them will typically cause your program to crash. Rather than defining str1 as a pointer, define it as an array initialized with the literal in question:
char str1[] = "Bangladesh Bogus Party";
If you do this, however, the array doesn't have enough room for the extra characters to be added. So make the array larger so that is has enough space:
char str1[50] = "Bangladesh Bogus Party";
strcpy and strcat both require that the first operand points to memory that is both writable and big enough to hold the result.
In your case neither of these requirements is fulfilled.
String literals are often placed in write-protected memory by the compiler. gcc places string literals in write-protected memory. Other compilers can do it differently.
The problem can be solved by creating str1 in writable memory and increasing its size so that it is big enough to hold the entire string.
The resulting string will be 41 characters + 1 for the string terminator.
Here are a couple of different ways to do it:
char str1[42] = "Bangladesh Bogus Party"; // Declare str1 as an array
or
char *str1 = malloc(42); // Allocate str1 dynamically
strcpy(str1, "Bangladesh Bogus Party"); // and copy the string to it
or
char *str1 = strdup("Bangladesh Bogus Party"); // Allocate str1 dynamically and copy the string to it. It will only be just big enough to hold the initial data
str1 = realloc(str1, 42); // Increase the size to 42.

Assigning character to char[x] results in segmentation fault

I have already written a couple of C programs and consider this awkward to ask. But why do I receive a segmentation fault for the following code being supposed to replace "test" by "aaaa"?
#include <stdio.h>
int main(int argc, char* argv[])
{
char* s = "test\0";
printf("old: %s \n", s);
int x = 0;
while(s[x] != 0)
{
s[x++] = 'a'; // segmentation fault here
}
printf("new: %s \n", s); // expecting aaaa
return 0;
}
This assignment is writing to a string literal, which is stored in a read-only section of your executable when it is loaded in memory.
Also, note that the \0 in the literal is redundant.
One way to fix this (as suggested in comments) without copying the string: declare your variable as an array:
char s[] = "test";
This will cause the function to allocate at least 5 bytes of space for the string on the stack, which is normally writeable memory.
Also, you should generally declare a pointer to a string literal as const char*. This will cause the compiler to complain if you try to write to it, which is good, since the system loader will often mark the memory it points to as read-only.
Answering the OP's question in the comment posted to #antron.
What you need is to allocate a character array, then use strcpy() to initialize it with your text, then overwrite with a-s.
Allocation can be done statically (i.e., char s[10];) but make sure the space is enough to store the length of your init string (including the terminating \0).
Alternatively, you can dynamically allocate memory using malloc() and free it using free(). This enables you to allocate exactly enough space to hold your init string (figure it out in run-time using strlen()).

fread weird behavior in c

I wrote a code just to understand how fread works and I don't understand how this is possible. Here my code :
#include <stdio.h>
#include <stdlib.h>
void main(){
char spool[5] = "hello";
fread(spool, sizeof(char), 6, stdin); //stdin == "bonjour"
printf("\n%s\n", spool);
}
So first, I thought spool's value would be replaced by "bonjo" but I got "bonjou". I don't understand why I get a 6 characters string instead of a 5 characters string.
char spool[5] = "hello";
This is wrong, because strings in C are null terminated. You don't allocate space for the null termination. A decent compiler should give you a warning about this. (Apparently gcc does not even with -Wall -Wextra, so that kind of sucks).
Reading "bounjour" into that array is also wrong, because C arrays have no boundary checks, nor do any of the library functions. So if you try to store more data in the array than you have room, you will write outside the boundaries of the array and the program will (hopefully) crash. You invoke undefined behavior, so anything might happen.
Solve this by declaring a large enough array, and study null termination of strings.
You need to increase the size of the spool buffer, (it cannot store more than 5 chars, that leads to Undefined beahvior):
#include <stdio.h>
#include <stdlib.h>
int main(){
char spool[15] = "hello";
fread(spool, sizeof(char), 6, stdin); //stdin == "bonjour"
printf("\n%s\n", spool);
}
Output:
bonjou
What you have is undefined behavior. You are trying to read 6 characters into a array which can hold 5 characters.Please note that the array should be large enough to hold a nul terminator.
Increase the size of your array spool to hold required characters.

Segmentation fault (core dumped) and zlib

I am very new to both using Linux and creating anything remotely serious on C. I've been trying to create a program which will simply compress a single string, but I keep getting this Segmentation fault when trying to run the compiled file.
I compiled it using:
gcc 2.c -o test.o -lz
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <assert.h>
int main ()
{
char *istream = malloc(10), *ostream = malloc(120);
istream = "istream";
int res = compress(ostream, (uLongf *)strlen(ostream), istream,(ulong)strlen(istream));
return 0;
}
Could anyone explain to me why this error is happening and how can I improve my code?
This line would appear to be the main problem:
(uLongf*)strlen(ostream)
You are interpreting a size_t value as a pointer to a buffer. You are meant to pass the address of an unsigned long containing the length of the output buffer. Take another look at the documentation for compress.
On top of that you don't yet understand how C strings work. The assignment operator when used with a char* lvalue merely copies an address and not the contents of a string. I suggest that you declare your buffers like this:
const char *istream = "istream";
char ostream[120];
I think your program should be something along these lines:
int main(void)
{
const char *istream = "istream";
char ostream[120];
uLongf destLen = sizeof(ostream);
int res = compress((Bytef*)ostream, &destLen, (Bytef*)istream, strlen(istream));
return 0;
}
Note that I wrote the code assuming that you are using a C compiler. And hence int main(void).
First you make istream point to memory you allocate:
char *istream = malloc(10)
then you make it point to a literal (and therefore constant and read-only) string:
istream = "istream";
You need to copy the string literal into the allocated memory, or you will no longer have the original pointer you allocated and have a memory leak. You also will not be able to free that pointer, since istream points to something you haven't allocated with malloc.
As for the crash, see the answer by David Heffernan.
As a side-note, there is no C++ in your code, only pure and plain C.
Change:
istream = "istream"
To
strcpy(istream,"istream");
In addition, what did you expect strlen(ostream) to return? 120?
strlen returns the index of the first 0 character encountered within the input string.
In your case, the contents of the memory pointed by ostream is unknown (i.e. "junk").
strlen will scan this memory until a 0 character is encountered, but will probably exceed the 120-byte memory space and cause a memory access violation.
Change strlen(ostream) to 120 if that was your intention.

Resources