dynamically allocating memory to save user input in it - c

I'm trying to write this simple code which takes the user input message, saves it on the stack, and then shows it back to the user.
I don't want to limit the number of characters the user can type in, so I used dynamic memory allocation each time the user enters a new character.
The code runs well if the user entered a small no. of characters, but it doesn't work if the user typed in a big no. of characters
for example: if I entered "Ahmed" it will show it back to me, but if I typed something with more characters it doesn't.
this is my code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *UserInput;
UserInput=(char *)calloc(1,sizeof(char));
int i=0,ii=0;
printf("Enter a message! \n");
while(*(UserInput+ii)!='\n'){
scanf("%c",(UserInput+i));
ii=i;
i++;
UserInput=realloc(UserInput,i*sizeof(char));
}
for(i=0;i<=ii;i++){
printf("%c",*(UserInput+i));
}
return 0;
}

Changes to be made in your program to run properly:
UserInput = realloc(UserInput, ((i + 1) * sizeof(char)));
Explanation:
When you are accepting input by scanf, you are taking in a sequence of characters (including '\n'). As your format specifier is %c it is supposed to take in only a single character. The input is not a single character, but a character sequence. Also %c does not filter out '\n'. The extra characters get stored into the buffer of scanf. Next time when scanf is called, it takes the input from the buffer.
By using calloc, you are allocating the UserInput 1 byte of space in the beginning, but when you are calling scanf in each iteration the character is stored in UserInput + 1th location, which has not been allocated to your variable by calloc, but which is still in the buffer of calloc i.e. it still has not touched system memory/heap. you are reallocating your memory at end of iteration. i.e. you are using unallocated memory and after that you are allocating it to UserInput.
For small character sequences, this will not give any errors as the buffer of calloc is not that small, but for large character sequences, you will get a error - "corrupted size vs. prev_size" - which is an indicator of heap attack/exploitation.
This happened because the calloc buffer is now exhausted, and you are using the memory from system heap, which sends the system into frenzy. You modify memory outside the range that was allocated for you to use, and the system finds that its control data has been corrupted, and is not really happy with that.
also don't forget to free(UserInput);
Prost !

Related

what will be happen to the size of string in this code?

#include <stdio.h>
#include <stdlib.h>
int main()
{
int size=10;
char string1[50];
char *string2;
string2=(char *)malloc(size*sizeof(char));
fgets(string1,10,stdin);
printf("%s",string1);
fgets(string2,10,stdin);
printf("%s",string2);
}
There are two strings in this code one is an array and another one is dynamically created using pointer.
If my input is less than 50 for string1 and less than 10 for string2 will the space that is not filled get wasted ,if so how to reduce the size.
In case of string 2 malloc size parameter is 10 and fgets size parameters is 10 what will happen if i increase the size to fgets(string2,50,stdin) which is greater than malloc's size?
how to calculate the final size of input string in each case?I have used sizeof operator but it gave the hardcoded size that is 50 and 10 respectively for string1 and string2
Is there any other better approach to create a dynamic string?
Yes, it will be wasted. You can use variable-length arrays to use a different limit, or use dynamic allocation. Whether or not you should worry about the wasted space is a separate question: if your program reads strings that the user inputs manually (as opposed to reading a file) you can waste a lot of space before it starts to matter, unless you are on an embedded system with severe memory constraints.
You will get undefined behavior, so your program will be invalid. Don't do that - it is precisely why fgets takes the maximum length of the string.
Call strlen to compute the length of the string. Add 1 for null terminator. Remember that '\n' is part of the string when you use fgets and the input has '\n' in it.
You can use POSIX extension to scanf, and pass %ms format and a pointer to char*. This will allocate the string at the exact length, but your program will be less portable. Obviously, you are required to deallocate these strings to avoid memory leaks.

Heap Overflow Attack

I am learning about heap overflow attacks and my textbook provides the following vulnerable C code:
/* record type to allocate on heap */
typedef struct chunk {
char inp[64]; /* vulnerable input buffer */
void (*process)(char *); /* pointer to function to process inp */
} chunk_t;
void showlen(char *buf)
{
int len;
len = strlen(buf);
printf("buffer5 read %d chars\n", len);
}
int main(int argc, char *argv[])
{
chunk_t *next;
setbuf(stdin, NULL);
next = malloc(sizeof(chunk_t));
next->process = showlen;
printf("Enter value: ");
gets(next->inp);
next->process(next->inp);
printf("buffer5 done\n");
}
However, the textbook doesn't explain how one would fix this vulnerability. If anyone could please explain the vulnerability and a way(s) to fix it that would be great. (Part of the problem is that I am coming from Java, not C)
The problem is that gets() will keep reading into the buffer until it reads a newline or reaches EOF. It doesn't know the size of the buffer, so it doesn't know that it should stop when it hits its limit. If the line is 64 bytes or longer, this will go outside the buffer, and overwrite process. If the user entering the input knows about this, he can type just the right characters at position 64 to replace the function pointer with a pointer to some other function that he wants to make the program call instead.
The fix is to use a function other than gets(), so you can specify a limit on the amount of input that will be read. Instead of
gets(next->inp);
you can use:
fgets(next->inp, sizeof(next->inp), stdin);
The second argument to fgets() tells it to write at most 64 bytes into next->inp. So it will read at most 63 bytes from stdin (it needs to allow a byte for the null string terminator).
The code uses gets, which is infamous for its potential security problem: there's no way to specify the length of the buffer you pass to it, it'll just keep reading from stdin until it encounters \n or EOF. It may therefore overflow your buffer and write to memory outside of it, and then bad things will happen - it could crash, it could keep running, it could start playing porn.
To fix this, you should use fgets instead.
You can fill up next with more than 64 bytes you will by setting the address for process. Thereby enable one to insert whatever address one wishes. The address could be a pointer to any function.
To fix simple ensure that only 63 bytes (one for null) is read into the array inp - use fgets
The function gets does not limit the amount of text that comes from stdin. If more than 63 chars come from stdin, there will be an overflow.
The gets discards the LF char, that would be an [Enter] key, but it adds a null char at the end, thus the 63 chars limit.
If the value at inp is filled with 64 non-null chars, as it can be directly accessed, the showlen function will trigger an access violation, as strlen will search for the null-char beyond inp to determine its size.
Using fgets would be a good fix to the first problem but it will also add a LF char and the null, so the new limit of readable text would be 62.
For the second, just take care of what is written on inp.

Bus error caused while reading in a string

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char *input = (char *)malloc(sizeof(char));
input = "\0";
while (1){
scanf("%s\n", input);
if (strcmp(input, "0 0 0") == 0) break;
printf("%s\n",input);
}
}
I'm trying to read in a string of integers until "0 0 0" is entered in.
The program spits out bus error as soon as it executes the scanf line, and I have no clue how to fix it.
Below is the error log.
[1] 59443 bus error
You set input to point to the first element of a string literal (while leaking the recently allocated buffer):
input = "\0"; // now the malloc'd buffer is lost
Then you try to modify said literal:
scanf("%s\n", input);
That is undefined behaviour. You can't write to that location. You can fix that problem by removing the first line, input = "\0";.
Next, note that you're only allocating space for one character:
char *input = (char *)malloc(sizeof(char));
Once you fix the memory leak and the undefined behaviour, you can think about allocating more space. How much space you need is for you to say, but you need enough to contain the longest string you want to read in plus an extra character for the null terminator. For example,
char *input = malloc(257);
would allow you to read in strings up to 256 characters long.
The immediate problem, (thanks to another answer) is that you're initializing input wrong, by pointing it at read-only data, then later trying to write to it via scanf. (Yes, even the lowly literal "" is a pointer to a memory area where the empty string is stored.)
The next problem is semantic: there's no point in trying to initialize it when scanf() will soon overwrite whatever you put there. But if you wanted to, a valid way is input[0] = '\0', which would be appropriate for, say, a loop using strcat().
And finally, waiting in the wings to bite you is a deeper issue: You need to understand malloc() and sizeof() better. You're only allocating enough space for one character, then overrunning the 1-char buffer with a string of arbitrary length (up to the maximum that your terminal will allow on a line.)
A rough cut would be to allocate far more, say 256 chars, than you'll ever need, but scanf is an awful function for this reason -- makes buffer overruns painfully easy especially for novices. I'll leave it to others to suggest alternatives.
Interestingly, the type of crash can indicate something about what you did wrong. A Bus error often relates to modifying read-only memory (which is still a mapped page), such as you're trying to do, but a Segmentation Violation often indicates overrunning a buffer of a writable memory range, by hitting an unmapped page.
input = "\0";
is wrong.
'input' is pointer, not memory.
"\0" is string, not char.
You assigning pointer to a new value which points to a segment of memory which holds constants because "\0" is constant string literal.
Now when you are trying to modify this constant memory, you are getting bus error which is expected.
In your case i assume you wanted to initialize 'input' with empty string.
Use
input[0]='\0';
note single quotes around 0.
Next problem is malloc:
char *input = (char *)malloc(sizeof(char));
you are allocating memory for 1 character only.
When user will enter "0 0 0" which is 5 characters + zero you will get buffer overflow and will probably corrupt some innocent variable.
Allocate enough memory upfront to store all user input. Usual values are 256, 8192 bytes it doesn't matter.
Then,
scanf("%s\n", input);
may still overrun the buffer if user enters alot of text. Use fgets(buf, limit(like 8192), stdin), that would be safer.

Why Won't free() Work?

Im getting an error with free() every time I store input above the allocated space in the char*. Heres the error:
*** Error in ./input': free(): invalid next size (fast): 0x09713008 ***
When I remove the free(), the program runs perfectly even though I'm entering more than the allocated size. Why is this happening? How can I prevent it? Here is my code for reference:
int main(void){
float x; // used to store the float the user entered.
char c; // character used to check if the user entered a character after the float
int loop=0;
char * usr_input = malloc(50); //allocates memory to store the string from the stdin
// loops until the user enters a float and only a float
do{
//gets the input string from stdin
scanf("%s",usr_input);
if(usr_input==NULL)
printf("You've entered a large number that isnt supported. Please use at most 5 digits\n");
// parses the input received and checks if the user entered a float and only a float.
// breaks the loop if they did
else if(sscanf(usr_input,"%f %c",&x,&c) == 1){
if (x!=inf)
loop=1;
else
printf("Input was too large. Try again");
}
// tells the user they entered invalid input. Loop value doesnt change so function loops again
else{
printf("Invalid input. Try again\n");
}
}
while(loop==0); // condition for the loop
free(usr_input);//crashes here
return x; // returns the valid float that was entered
}
When I remove the free(), the program runs perfectly even though I'm entering more than the allocated size.
Entering more than allocated size is called undefined behavior. This is an error, despite the fact that your program may appear to be "running fine".
The main problem with undefined behavior is that your program does not fail fast. Essentially, the penalty for having undefined behavior is delayed until some future time - for example, when you allocate again, or when you free.
malloc stores some special information in the allocated block that lets free run. The "nvalid next size" error usually means that your code has written over some of that hidden block of data.
To fix this problem you need to change your code so that it never writes past the allocated length. If you are having trouble detecting precisely the spots that need to change, consider using valgrind or another memory profiler.
To prevent scanf from writing over your allocated size use the size in the format string:
scanf("%49s",usr_input); // Pass 49 for the length, because you have 50 bytes, and you need 1 byte for '\0'
the program runs perfectly even though I'm entering more than the
allocated size.
No, it does not run perfectly. In fact, the error you get is caused by writing past the bounds of the allocated buffer. Its a buffer overrun and introduces undefined behavior. Your program may work or may crash immediately, though in most cases it will cause problems later, problems that may look completely unrelated and therefore will be very hard to identify and correct.
Make sure you allocate a buffer large enough not to overwrite it.
On the other hand, it makes no sense for you to allocate that small buffer on the heap. It can be a static buffer on the stack and you'd avoid problems with memory allocation and release.

Problem using Scanf?

Why does scanf give a max value in case of "int" but crash the program in case of "char" when the limit is exceeded?
#include<stdio.h>
main(){
int a;
char ch[10];
scanf("%d",&a);
printf("%d",a);
scanf("%s",ch);
printf("%s",ch);
}
It crashes your program in this case because scanf() has an inherently unsafe interface for strings. It has no way of knowing that your parameter ch is an array large enough to hold a 9-character string plus its terminating nul. As a result, it is perfectly happy to keep reading characters from stdin and storing them in memory well past the end of the array.
If your program crashes, you are actually lucky. In the worst case, the attacker has used a carefully crafted string to manipulate the content of the stack in such a way that he has gained control of your computer. This is an example of a buffer overflow attack. It sounds unlikely, but it has been documented to occur on a large number of occasions.
Used for only numbers, scanf is generally safe enough, but it is not very good at handling errors in the input. As a result, it is usually a good idea to use something like fgets() to read the input (it has a buffer length parameter to control overflow) and sscanf() to parse from that buffer, testing its return values for sanity as you go.
Edit: As the comment from R points out, I overstated the dangers inherent to the scanf interface. With care to correctly use the field width specifier on all strings, scanf becomes safer. But then you take responsibility for guaranteeing that the specified width does fit within the buffer. For the example, you should write scanf("%9s",ch); because your buffer was declared to be ten bytes long and you need room for the terminating nul.
Note that you should also be testing the return value from scanf(). It returns the number of fields it successfully matched, or EOF if an I/O error occurred. It might return 0 if the user entered "abc" when it expected a number, for instance.
Because you're not reading a character, you're reading a string. And scanf does not "know" that you only have space for 10 characters (including the null). This is the joy of C programming.
You can protect yourself in this case by adding a width modifier:
%9s

Resources