I don't know much about hacking, c, assembly, memory and all that stuff so I coudln't resolve my question by my self.
So, buffer overflow overflows into other address of variables and corrupts them. So I tested and it really does. And I thought that if it can overflow constant variable buffer overflow must be super powered and I tested, but it doesn't overflow const variable.
Why is that?
int a;
char buffer[8];
and
const int a;
char buffer[8];
has address of variable 'buffer' in front of address of variable 'a' by size of the 'buffer'. Is there something special in const variable when allocated to a memory?
my sample code:
#include <stdio.h>
#include <string.h>
int main() {
char buffer1[8];
const int a=0; //vs int a=0;
char buffer2[8];
strcpy(buffer1,"one");
strcpy(buffer2,"two");
printf("Buffer 2 [%p]: %s\n",buffer2,buffer2);
printf("a [%p]: %d\n",&a,a);
printf("Buffer 1 [%p]: %s\n",buffer1,buffer1);
printf("\nCopy Buffer\n\n");
strcpy(buffer2,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
printf("Buffer 2 [%p]: %s\n",buffer2,buffer2);
printf("a [%p]: %d\n",&a,a);
printf("Buffer 1 [%p]: %s\n",buffer1,buffer1);
return 0;
}
Three things come to mind:
This is undefined behavior, so all bets are off the table.
The compiler doesn't even have to look at what a is. If I were a compiler, I would just look at that code and say "a is always zero, so I'm just going to go ahead and replace a with 0." The thing is, when you say printf("%p %d", &a, a);, the compiler doesn't even have to get the contents of a. It knows it's going to be zero, always and forever.* So it can just change that code to printf("%p %d", &a, 0);.
Even if a were not const, the compiler would be allowed to "cache" the value of a in a register. It only needs to look up the value once, and then it knows that a never changes*, so it can reuse the value it looked up from before.
*Compilers will make a lot of assumptions, like "this code doesn't invoke undefined behavior." If you invoke undefined behavior, the compiler might make some "wrong" assumptions. Which it probably does, in this case.
Buffer Overflow is UB, so anything can happen.
Also, your compiler could optimize away your const variable, because it has a constant value determined at compile time.
An implementation is allowed to put a const-qualified object in a different memory segment altogether, so that may be why it isn't affected (emphasis on may; since behavior on buffer overflow is undefined, there could be any number of reasons).
Online C 2011 standard, section 6.7.3, footnote 132:
The implementation may place a const object that is not volatile in a read-only region of
storage. Moreover, the implementation need not allocate storage for such an object if its address is
never used.
Related
I am a c++ learner. Others told me "uninitiatied pointer may point to anywhere". How to prove that by code.?I made a little test code but my uninitiatied pointer always point to 0. In which case it does not point to 0? Thanks
#include <iostream>
using namespace std;
int main() {
int* p;
printf("%d\n", p);
char* p1;
printf("%d\n", p1);
return 0;
}
Any uninitialized variable by definition has an indeterminate value until a value is supplied, and even accessing it is undefined. Because this is the grey-area of undefined behaviour, there's no way you can guarantee that an uninitialized pointer will be anything other than 0.
Anything you write to demonstrate this would be dictated by the compiler and system you are running on.
If you really want to, you can try writing a function that fills up a local array with garbage values, and create another function that defines an uninitialized pointer and prints it. Run the second function after the first in your main() and you might see it.
Edit: For you curiosity, I exhibited the behavior with VS2015 on my system with this code:
void f1()
{
// junk
char arr[24];
for (char& c : arr) c = 1;
}
void f2()
{
// uninitialized
int* ptr[4];
std::cout << (std::uintptr_t)ptr[1] << std::endl;
}
int main()
{
f1();
f2();
return 0;
}
Which prints 16843009 (0x01010101). But again, this is all undefined behaviour.
Well, I think it is not worth to prove this question, because a good coding style should be used and this say's: Initialise all variables! One example: If you "free" a pointer, just give them a value like in this example:
char *p=NULL; // yes, this is not needed but do it! later you may change your program an add code beneath this line...
p=(char *)malloc(512);
...
free(p);
p=NULL;
That is a safe and good style. Also if you use free(p) again by accident, it will not crash your program ! In this example - if you don't set NULL to p after doing a free(), your can use the pointer by mistake again and your program would try to address already freed memory - this will crash your program or (more bad) may end in strange results.
So don't waste time on you question about a case where pointers do not point to NULL. Just set values to your variables (pointers) ! :-)
It depends on the compiler. Your code executed on an old MSVC2008 displays in release mode (plain random):
1955116784
1955116784
and in debug mode (after croaking for using unitialized pointer usage):
-858993460
-858993460
because that implementation sets uninitialized pointers to 0xcccccccc in debug mode to detect their usage.
The standard says that using an uninitialized pointer leads to undefined behaviour. That means that from the standard anything can happen. But a particular implementation is free to do whatever it wants:
yours happen to set the pointers to 0 (but you should not rely on it unless it is documented in the implementation documentation)
MSVC in debug mode sets the pointer to 0xcccccccc in debug mode but AFAIK does not document it (*), so we still cannot rely on it
(*) at least I could not find any reference...
I created a C++/CLI wrapper calling a third party code, which happened to end in corrupted memory. So I'm suspecting that maybe the code wasn't legal in C++
below is the code that crashed:
void Init_4bit_tab(unsigned char *dest,unsigned char *source)
{
unsigned char masque,i;
masque=0x08;
for(i=0; i<4; i++) {
dest[i] = (*source & masque)>>(3-i);
masque >>= 1;
}
}
the exact error was:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Update:
After scanning the 3rd party code, It appears to be multidimensional array, because of the way it was passed, but I'm still not sure what's causing the problem:
the source function
unsigned char Data_B[81];
...
S_Box_Calc(&Data_B[33]);
void S_Box_Calc(unsigned char *vect)
{
unsigned char *S_Box[8];
unsigned lig,col,i;
S_Box[0]=S1;
S_Box[1]=S2;
S_Box[2]=S3;
S_Box[3]=S4;
S_Box[4]=S5;
S_Box[5]=S6;
S_Box[6]=S7;
S_Box[7]=S8;
for(i=0;i<8;i++) {
col= 8*vect[1+6*i] + 4*vect[2+6*i] + 2*vect[3+6*i] + vect[4+6*i];
lig= 2*vect[6*i] + vect[5+6*i];
Init_4bit_tab(&vect[4*i],&S_Box[i][col+lig*16]);
}
}
Update 2:
I checked the values on debug mode the dest and source are not null. however if I tried to quick watch (*source & masque) under this code dest[i] = (*source & masque)>>(3-i);
I get this error
(*source & masque) error: & cannot be performed on '*source' and 'masque'
Update 3:
S1...Sn was originally defined on the global scope of the file, but I get an error when I left it as is, so I initialized them in the constructor this way:
unsigned char lS1[64] = {
14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,
0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,
4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,
15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13
};
std::copy(S1, S1 + 64, lS1);
could this be the problem?
There's no problem with the code you show, if it is passed
valid pointers. If it's corrupting memory, it's probably
because the caller didn't pass it valid pointers.
After your edit: if S_Box_Calc is called with vect equal to
Data_B + 33, as you show, the range [vect, vect+48) is
legal, which means that Init_4bit_tab should not be called
with a value superior to 44. In fact, in the code you show, it
is never called with a value greater than 28, so you shouldn't be able
to corrupt memory here. If any of S1 through S8, however, do
not point to valid memory, you'll get the symptoms you state.
This is perfectly legal C++ syntactically, it compiles. Check if it's semantically correct. The only place where you could inadvertently tread in the UB land is in accessing dest pointer i.e. The array it is pointing to should atleast be 4 chars long from where it's pointing to. Also since the error talks about access violation, make sure dest is pointing to a writable memory location.
Where are your overflow checks? You should be passing in sizes to your functions to allow you to restrict writes to memory if there is a chance that it will overflow. It's similar to strcpy() vs. strncpy() or strlcpy() in BSD. Perhaps if you implement something along those lines and generate an error where there is a condition that memory written would otherwise overflow, you might find the cause of memory corruption.
I made a small program that looked like this:
void foo () {
char *str = "+++"; // length of str = 3 bytes
char buffer[1];
strcpy (buffer, str);
cout << buffer;
}
int main () {
foo ();
}
I was expecting that a stack overflow exception would appear because the buffer had smaller size than the str but it printed out +++ successfully... Can someone please explain why would this happened ?
Thank you very much.
Undefined Behavior(UB) happened and you were unlucky it did not crash.
Writing beyond the bounds of allocated memory is Undefined Behavior and UB does not warrant a crash. Anything might happen.
Undefined behavior means that the behavior cannot be defined.
You don't get a stack overflow because it's undefined behaviour, which means anything can happen.
Many compilers today have special flags that tell them to insert code to check some stack problems, but you often need to explicitly tell the compiler to enable that.
Undefined behavior...
In case you actually care about why there's a good chance of getting a "correct" result in this case: there are a couple of contributing factors. Variables with auto storage class (i.e., normal, local variables) will typically be allocated on the stack. In a typical case, all items on the stack will be a multiple of some specific size, most often int -- for example, on a typical 32-bit system, the smallest item you can allocate on the stack will be 32 bits. In other words, on your typical 32-bit system, room for four bytes (of four chars, if you prefer that term).
Now, as it happens, your source string contained only 3 characters, plus the NUL terminator, for a total of 4 characters. By pure bad chance, that just happened to be short enough to fit into the space the compiler was (sort of) forced to allocate for buffer, even though you told it to allocate less.
If, however, you'd copied a longer string to the target (possibly even just a single byte/char longer) chances of major problems would go up substantially (though in 64-bit software, you'd probably need longer still).
There is one other point to consider as well: depending on the system and the direction the stack grows, you might be able to write well the end of the space you allocated, and still have things appear to work. You've allocated buffer in main. The only other thing defined in main is str, but it's just a string literal -- so chances are that no space is actually allocated to store the address of the string literal. You end up with the string literal itself allocated statically (not on the stack) and its address substituted where you've used str. Therefore, if you write past the end of buffer, you may be just writing into whatever space is left at the top of the stack. In a typical case, the stack will be allocated one page at a time. On most systems, a page is 4K or 8K in size, so for a random amount of space used on the stack, you can expect an average of 2K or 4K free respectively.
In reality, since this is in main and nothing else has been called, you can expect the stack to be almost empty, so chances are that there's close to a full page of unused space at the top of the stack, so copying the string into the destination might appear to work until/unless the source string was quite long (e.g., several kilobytes).
As to why it will often fail much sooner than that though: in a typical case, the stack grows downward, but the addresses used by buffer[n] will grow upward. In a typical case, the next item on the stack "above" buffer will be the return address from main to the startup code that called main -- therefore, as soon as you write past the amount of space on the stack for buffer (which, as above, is likely to be larger than you specified) you'll end up overwriting the return address from main. In that case, the code inside main will often appear to work fine, but as soon as execution (tries to) return from main, it'll end up using that data you just wrote as the return address, at which point you're a lot more likely to see visible problems.
Outlining what happens:
Either you are lucky and it crashes at once. Or because it's undefined technically you could end up writing to a memory address used by something else. say that you had two buffers, one buffer[1] and one longbuffer[100] and assume that the memory address at buffer[2] could be the same as longbuffer[0] which would mean that long buffer now terminates at longbuffer[1] (because the null-termination).
char *s = "+++";
char longbuffer[100] = "lorem ipsum dolor sith ameth";
char buffer[1];
strcpy (buffer, str);
/*
buffer[0] = +
buffer[1] = +
buffer[2] = longbuffer[0] = +
buffer[3] = longbuffer[0] = \0 <- since assigning s will null terminate (i.e. add a \0)
*/
std::cout << longbuffer; // will output: +
Hope that helps in clarifying please note it's not very likely that these memory addresses will be the same in the random case, but it could happen, and it doesn't even need to be the same type, anything can be at buffer[2] and buffer[3] addresses before being overwritten by the assignment. Then the next time you try to use your (now destroyed) variable it might well crash, and thats when debugging become a bit tedious since the crash doesn't seem to have much to do with the real problem. (i.e. it crashes when you try to access a variable on your stack while the real problem is that you somewhere else in your code destroyed it).
There is no explicit bounds checking, or exception throwing on strcpy - it's a C function. If you want to use C functions in C++, you're going to have to take on the responsibility of checking for bounds etc. or switch to using std::string.
In this case it did work, but in a critical system, taking this approach might mean that your unit tests pass but in production, your code barfs - not a situation that you want.
Stack corruption is happening, its an undefined behaviour, luckily crash didnt occur. Do the below modifications in your program and run it will crash surely because of stack corruption.
void foo () {
char *str = "+++"; // length of str = 3 bytes
int a = 10;
int *p = NULL;
char buffer[1];
int *q = NULL;
int b = 20;
p = &a;
q = &b;
cout << *p;
cout << *q;
//strcpy (buffer, str);
//Now uncomment the strcpy it will surely crash in any one of the below cout statment.
cout << *p;
cout << *q;
cout << buffer;
}
#include <iostream>
int main()
{
const int i=10;
int *p =(int *) &i;
*p = 5;
cout<<&i<<" "<<p<<"\n";
cout<<i<<" "<<*p;
return 0;
}
Output:
0x22ff44 0x22ff44
10 5
Please Explain.
Well, your code obviously contains undefined behaviour, so anything can happen.
In this case, I believe what happens is this:
In C++, const ints are considered to be compile-time constants. In your example, the compiler basically replaces your "i" with number 10.
You've attempted to modify a const object, so the behavior is
undefined. The compiler has the right to suppose that the const
object's value doesn't change, which probably explains the
symptoms you see. The compiler also has the right to put the
const object in read only memory. It generally won't do so for
a variable with auto lifetime, but a lot will if the const has
static lifetime; in that case, the program will crash (on most
systems).
I'll take a shot at it: since there's no logical reason for that output, the compiler must have optimised that wretched cout<<i<<" " to a simple "cout<<"10 ". But it's just a hunch.
In C++, in which way the stack may get corrupted. One way I guess is to overwriting the stack variables by accessing an array beyond its boundaries. Is there any other way that it can get corrupted?
You could have a random/undefined pointer that ends up pointing to the stack, and write though that.
An assembly function could incorrectly setup/modify/restore the stack
Cosmic waves could flips bits in the stack.
Radioactive elements in the chip's casing could flip bits.
Anything in the kernel could go wrong and accidentally change your stack memory.
But those are not particular to C++, which doesn't have any idea of the stack.
Violations of the One Definition Rule can lead to stack corruption. The following example looks stupid, but I've seen it a couple of times with different libraries compiled in different configurations.
header.h
struct MyStruct
{
int val;
#ifdef LARGEMYSTRUCT
char padding[16];
#endif
}
file1.cpp
#define LARGEMYSTRUCT
#include "header.h"
//Here it looks like MyStruct is 20 bytes in size
void func(MyStruct s)
{
memset(s.padding, 0, 16); //corrupts the stack as below file2.cpp does not have LARGEMYSTRUCT declared and declares Mystruct with 4 bytes
return; //Will probably crash here as the return pointer has been overwritten
}
file2.cpp
#include "header.h"
//Here it looks like MyStruct is only 4 bytes in size.
extern void func(MyStruct s);
void caller()
{
MyStruct s;
func(s); //push four bytes on to the stack
}
Taking pointers to stack variables is a good way:
void foo()
{
my_struct s;
bar(&s);
}
If bar keeps a copy of the pointer then anything can happen in the future.
Summing up: Stack corruption happens when there's stray pointers pointing to the stack.
The C++ standard does not define stack/heap. Further, there are a number of ways to invoke undefined behavior in a program -- all of which may corrupt your stack (it's UB, after all). The short answer is -- your question is too vague to have a meaningful answer.
Calling a function with the wrong calling convention.
(though this is technically compiler-specific, not a question of C++, every C++ compiler has to deal with that.)
Throwing an exception inside a destructor is a good candidate. It would mess up the stack unwinding.