Heap Overflow attack, what can go wrong with this code - heap-memory

char *test(char *arg1, char* arg2){
size_t length=strlen(arg1);
char *c= malloc(length+4);
for(int i=length;i>0;i--)
*(c+i+4)=*(arg1)^(arg2[i%8]);
*(size_t *) (c) =length;
return c;
}
Does this code suffer from heap overflow attack ?

Lots of things can go wrong there. Most importantly, the expression *(c+i+4)=*(arg1)^(arg2[i%8]) is going to overflow your allocated buffer on the first iteration of the loop.
Imagine that length==1. So you'll allocate 5 bytes for c. The first time through the loop, i is equal to 1. So the expression c+i+4 resolves to c+5, which is one byte beyond the memory you allocated.
Other things that can go wrong:
arg1 is an invalid pointer. Your program crashes.
The string referenced by arg1 is really long, and you can't allocate enough memory for it. malloc fails and your program crashes.
Memory addressed by arg2 is smaller than 8 bytes, and therefore your code is reading beyond the allocated memory. This might not crash, but the result will be ... undefined.
You assume that size_t is 4 bytes. Your malloc should be malloc(length+sizeof(size_t)).

Related

Why char pointer saving data more than allocated memory in C?

While working on dynamic memory allocation in C, I am getting confused when allocating size of memory to a char pointer. While I am only giving 1 byte as limit, the char pointer successfully takes input as long as possible, given that each letter corresponds to 1 byte.
Also I have tried to find sizes of pointer before and after input. How can I understand what is happening here? The output is confusing me.
Look at this code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int limit;
printf("Please enter the limit of your string - ");
gets(&limit);
char *text = (char*) malloc(limit*4);
printf("\n\nThe size of text before input is %d bytes",sizeof(text));
printf("\n\nPlease input your string - ");
scanf("%[^\n]s",text);
printf("\n\nYour string is %s",text);
printf("\n\nThe size of char pointer text after input is %d bytes",sizeof(text));
printf("\n\nThe size of text value after input is %d bytes",sizeof(*text));
printf("\n\nThe size of ++text value after input is %d bytes",sizeof(++text));
free(text);
return 0;
}
Check this output:
It works because malloc usually doesn't allocate the same number of bytes you pass to it.
It reserves memory multiple of "blocks". It usually reserve more memory to "cache" it for next malloc calls as an optimization. (it is an implementation specific)
check glibc malloc internals for example.
Using more memory than allocated by malloc is an undefined behavior. you may overwrite metadata of malloc saved on heap or corrupt other data.
Also I have tried to find sizes of pointer before and after input. How
can I understand what is happening here? The output is confusing me.
The size of pointer is fixed for all pointer types in a machine, it is usually 4/8 bytes depending on the address size. It doesn't have anything to do with data size.
Welcome to the world of Undefined Behaviour!
char *text = malloc(limit*4); (don't cast malloc in C) will make text point the the first element of an array of size limit*4.
C will not prevent you to write past the end of any array, simply the behaviour is undefined by the standard. It may work fine, or it may crash immediately, or you may experience abnormal behaviour later in the program.
Here, the underlying system call has probably allocated a full memory page (often 4k), and as you have not used another malloc you have just used a memory belonging to the process but still officially unused. But do not rely on that and never use it in production code.
And sizeof does not make sense with pointers. sizeof(text) is sizeof(char *) (same for sizeof(++text) for same reason) and is the size of a pointer (generaly 2, 4 or 8 bytes) and sizeof(*text) is sizeof(char) which by definition is 1.
C is confident that you as the programmer know how much memory you have asked, and will not try to use more. Anything can happen if you do (including expected result) but do not blame the language or the compiler if it breaks: only you will be guilty.

Creating malloc() buffer overrun in C

I want to write a check unit test for malloc(3) using the "check library".
This unit test is supposed to produce buffer overruns.
Is allocating a double on an int variable (i.e int *ptr = malloc(3)) a buffer overrun?
What about allocating a bigger number than the maximum value of an int?
Could you please give me other simple examples for buffer overruns?
To overrun a buffer, you need to access a buffer outside its guaranteed size or before its beginning. Allocation doesn't really access any visible buffers, so it's hard to see how any kind of allocation would be a buffer overrun unless there was a bug in the allocation routine itself or its structures were corrupted.
Is allocating a double on an int variable (i.e int *ptr = malloc(3)) a buffer overrun?
No, since no buffer is being accessed.
What about allocating a bigger number than the maximum value of an int?
No, since no buffer is being accessed.
To overrun a buffer, you must first have a buffer and then overrun it. For example:
int* j = malloc (2 * sizeof (int));
j[2] = 1;
Here I allocate a buffer with space for two integers and then overrun it by accessing the third integer (0 is the first, 1 is the second, so 2 is the third).
Writing or reading past the end of a buffer allocated by malloc produces undefined behavior, which means you can't depend on any particular behavior when it happens. The program may appear to work, it may core dump, or it may output unexpected results.
Because overrunning a malloc'ed buffer caused undefined behavior, creating test cases for it is pointless unless you're testing a particular implementation of malloc. Based on the wording of your question, that does not seem to be the case.

Realloc simply not diong anything, not erroring

I've got the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char ** argv)
{
//just checking to see where the stack
printf("The stack is around %p\n", &argc); is, making sure arr isn't in it
char ** arr = malloc(8*sizeof(char*));
printf("arr is size %li, at %p\n", sizeof(arr), arr);
arr = realloc(arr, 100); //I picked a weird number to show it isn't doing anything. I've picked different numbers (like 200, 2*sizeof(char*)*sizeof(arr), and 16)
printf("arr is size %li, at %p\n", sizeof(arr), arr);
}
That's the entirety of the file (it's a unit test; I was noticing it elsewhere)
The output of the above is as follows:
The stack is around 0x7fff5b94d12c
arr is size 8, at 0x120f010
arr is size 8, at 0x120f010
Perhaps I'm misunderstanding what realloc should do. I'm expecting the following output.
The stack is around 0x7fff5b94d12c
arr is size 8, at 0x120f010
arr is size <size>, at <somewhere>
where <size> is... something odd like 12... at least not 8 and <somewhere> is most likely 0x120f010 but possibly anywhere reasonable.
Are my expectations wrong or am I using realloc incorrectly?
The output of your program is correct, because
Neither malloc nor realloc have anything to do with the automatic storage (i.e. "the stack"). They allocate memory from the dynamic storage area (i.e. "the heap"). One should not expect the position of the top of the stack to change in response to calls to malloc, realloc, or for that matter, any other function.
The value of sizeof(arr) does not depend on what you have allocated to it. It is computed at compile time, and it is always equal to the size of a pointer. On your system, pointers use 8 bytes.
malloc often gives you more memory that you ask, and stores the actual value in a special location that realloc can access at a later time. If you realloc down, or realloc within the bounds, the value returned by realloc does not change. That's the reason why it may perform better than simply calling malloc and memcpy.
sizeof arr
That's the same as
sizeof char**
The size of a pointer isn't going to change, and taking the size of a pointer is not going to tell you how much memory it refers to. Pointers are not arrays, and sizeof is evaluated at compile time.
As for the address bit, realloc doesn't guarantee that the memory block was moved. It could simply expand it successfully and return the same address.
Also, I realize this is just example code, but be aware that, if realloc failed, you leaked what arr originally pointed to.
It's not uncommon, and in fact a bit expected, that a malloc call followed directly by realloc would not change the address of the pointer. In many cases the allocator can just extend the amount of memory reserved at the address and not have to move the pointer. This is what is happening here.
This isn't something you should ever depend on though. It's just a quirk of the implementation
If your assumption is that realloc() has to return a different pointer, then your assumption is wrong.
Typically if you're reducing the size of the allocated memory or leaving it "as is", then realloc() can return the same pointer and avoid copying data, etc.
Sometimes if you're increasing the size of the allocated memory realloc() can check if there's free space above the existing space and still return the same pointer (and avoid copying data).
Mostly, it's only when there is no free space above the allocated memory that realloc() must copy the data somewhere else and return a different pointer.

C Language - Malloc unlimited space?

I'm having difficulty learning C language's malloc and pointer:
What I learned so far:
Pointer is memory address pointer.
malloc() allocate memory locations and returns the memory address.
I'm trying to create a program to test malloc and pointer, here's what I have:
#include<stdio.h>
main()
{
char *x;
x = malloc(sizeof(char) * 5);
strcpy(*x, "123456");
printf("%s",*x); //Prints 123456
}
I'm expecting an error since the size I provided to malloc is 5, where I put 6 characters (123456) to the memory location my pointer points to. What is happening here? Please help me.
Update
Where to learn malloc and pointer? I'm confused by the asterisk thing, like when to use asterisk etc. I will not rest till I learn this thing! Thanks!
You are invoking undefined behaviour because you are writing (or trying to write) beyond the bounds of allocated memory.
Other nitpicks:
Because you are using strcpy(), you are copying 7 bytes, not 6 as you claim in the question.
Your call to strcpy() is flawed - you are passing a char instead of a pointer to char as the first argument.
If your compiler is not complaining, you are not using enough warning options. If you're using GCC, you need at least -Wall in your compiler command line.
You need to include both <stdlib.h> for malloc() and <string.h> for strcpy().
You should also explicitly specify int main() (or, better, int main(void)).
Personally, I'm old school enough that I prefer to see an explicit return(0); at the end of main(), even though C99 follows C++98 and allows you to omit it.
You may be unlucky and get away with invoking undefined behaviour for a while, but a tool like valgrind should point out the error of your ways. In practice, many implementations of malloc() allocate a multiple of 8 bytes (and some a multiple of 16 bytes), and given that you delicately do not step over the 8 byte allocation, you may actually get away with it. But a good debugging malloc() or valgrind will point out that you are doing it wrong.
Note that since you don't free() your allocated space before you return from main(), you (relatively harmlessly in this context) leak it. Note too that if your copied string was longer (say as long as the alphabet), and especially if you tried to free() your allocated memory, or tried to allocate other memory chunks after scribbling beyond the end of the first one, then you are more likely to see your code crash.
Undefined behaviour is unconditionally bad. Anything could happen. No system is required to diagnose it. Avoid it!
If you call malloc you get and adress of a memory region on heap.
If it returns e.g. 1000 you memory would look like:
Adr Value
----------
1000 1
1001 2
1002 3
1003 4
1004 5
1005 6
1006 0
after the call to strcpy(). you wrote 7 chars (2 more than allocated).
x == 1000 (pointer address)
*x == 1 (dereferenced the value x points to)
There are no warnings or error messages from the compiler, since C doesn't have any range-checking.
My three cents:
Use x, as (*x) is the value that is stored at x (which is unknown in your case) - you are writing to unknown memory location. It should be:
strcpy(x, "123456");
Secondly - "123456" is not 6 bytes, it's 7. You forgot about trailing zero-terminator.
Your program with it's current code might work, but not guaranteed.
What I would do:
#include<stdio.h>
main()
{
char str[] = "123456";
char *x;
x = malloc(sizeof(str));
strcpy(x, str);
printf("%s",x); //Prints 123456
free(x);
}
Firstly, there is one problem with your code:
x is a pointer to a memory area where you allocated space for 5 characters.
*x it's the value of the first character.
You should use strcpy(x, "123456");
Secondly, the memory after your 5 bytes allocated, can be valid so you will not receive an error.
#include<stdio.h>
main()
{
char *x;
x = malloc(sizeof(char) * 5);
strcpy(x, "123456");
printf("%s",x); //Prints 123456
}
Use this...it will work
See difference in your & mine program
Now here you are allocating 5 bytes & writing 6 byte so 6th byte will be stored in next consecutive address. This extra byte can be allocated to some one else by memory management so any time that extra byte can be changed by other program because 6th byte is not yours because you haven't malloc'd that.. that's why this is called undefined behaviour.

Heap corruption in C

int main ()
{
int * b;
b = (int*) malloc (1);
*b=110000;
free (b);
return 0;
}
Why does heap corruption happen at free (b);?
IMO, heap corruption already happens at *b=110000;.
malloc()'s argument is the number of bytes to allocate. You need to use:
b = (int*) malloc(sizeof(int));
You've allocated too small a block, and then written more bytes to it than you've allocated, which overwrites bookkeeping information next to the block, corrupting the heap.
It is at *b=110000; Because you are allocating the memory for one byte, and then assigning an int into it which is more than one byte. Either you can have b= (int *)malloc(sizeof(int)) or instead of int *b you can have char *b and then cast the malloced pointer to char *. The code will even may work if you assign a value which is less than 128 (because of signed char) to *b.
EDIT :- I think sometimes even this will work without any hassle. Because the compiler may choose to allocate more than one byte of memory for fast access of data.
The heap corruption indeed happens already at the *b=11000 assignment, but it is not detected until the free(b) call because that is the first point where the integrity of the heap gets checked again.
Checking the heap integrity at every assignment (or even every assignment involving a dereferenced pointer) would slow most programs down too much and it would tie the compiler too tightly to the library implementation. For that reason, the integrity checks are only performed when the heap gets manipulated, which is in the malloc and free functions (and friends).
The code writes more data to the memory block than the space available to it hence corrupting the start of next valid memory block.
Using char * rather than int * and writing a value -128 to 127 to *b should fix it.
Your value is 110000 --> 0x01ADB0 --> 3 bytes. You are writing 3 bytes of data into 1 byte you requested from the heap.
It is important to be aware of what malloc is doing with parameter 1, and what you are putting into this memory.
malloc() allocates size bytes and returns a pointer to the allocated memory.
Also don't forget to test your pointer before using it, and initializing your local variables.

Resources