I've been usi开发者_如何转开发ng pointers more and more in my programs, and while reading up about pointers, every single guide or tutorial I found said that incorrect use of pointers could yield 'disastrous' results.
Now, I've had a few cases of some big memory leaks, and pointers dereferencing a wrong pointer variable, returning an incorrect value, but other than that nothing 'disastrous' has ever occurred; like my computer and/or other programs crashing.
Can someone give me a simple code example that will definitely yield 'disastrous' results, perhaps with some back-story of what happened, in case you've ever accidentally used that piece of code? By 'disastrous' results, I mean code that might interfere with other programs or the OS, and possibly make them crash.
Incorrect pointer arithmetic can lead to disasters too, because getting the bounds wrong leads to buffer overflows, and buffer overflows lead to corrupted data, for example stack smashing:
void test_fun(int i)
int x[5];
for (int *p = x; p < x+10; ++p) { // obvious error, some are more subtle
*p = i;
}
return; // execution may resume at address `i`, with entertaining results
}
Of course, you can make the same mistake just calling strcpy
or memcpy
[*], you don't have to be doing the pointer arithmetic yourself. If an attacker controls the value of i
(perhaps because it's read from an input file, and the attacker crafts a malicious file), then you could have worse than a crash on your hands. In combination with more platform-specific tricks, the attacker might be able to arrange that returning to i
eventually ends up executing code supplied by the attacker.
[*] or strncpy
, or strlcpy
, or strcpy_s
, or std::copy
, before anyone starts. Once you've got a bound wrong somehow, then supplying that wrong bound to a bounds-checking function is still wrong...
There're two main kinds of disasters - dangling pointers and memory leaks.
Dangling pointer is when a pointer stores an address that is not an address of an object:
T* first;
T* second; //somewhere in another piece of code
first = new T();
second = first;
delete first;
first = 0; //second still stores the address of an already deleted object
memory leak is when there're no pointers storing an address of a heap-allocated object:
T* object;
for( int i = 0; i < 10; i++ ) {
object = new T();
}
delete object; // now the first nine objects are unreacheable
Dangling pointers are bad because using them you leads to undefined behavior - program might crash or modify some unrelated data and this will cause problems later. Memory leaks are bad because allocated memory can't be reused and so the program can get short on memory some time later.
A few cases come to mind:
- continuing to access free()/delete()d memory or local variables after they've left scope
- leaking heap memory due to unclear ownership
- unintended shared access to data, where changes to pointed-to values may confuse some of algorithms working on them
- accidental shallow copies
- incomplete/flawed serialisation due to naive binary writing of assumed POD data that actually contains pointers to other data
- multi-process unsafe data in shared memory where pointers (esp. virtual dispatch pointers) needed to be different in the different processes accessing it
- cyclic data linkage that causes code to get stuck in loops indefinitely
- moving the pointers through data in such a way that they accidentally move outside the data (the issue is similar to array indices, but more complex as there's not necessarily any constant reference and consistently safe relative indexing range)
- failing to check for / handle sentinel values correctly
- issues with the type of data to which the pointer points
- use of unsafe/erroneous casts (e.g. a reinterpret cast where a dynamic cast is needed)
- failure to appreciate that indexing from pointers is done in units of the pointer-to-type's size
- invalid conversions from pointers to/from shorter integer types
The most nasty I have seen are "delayed failures", when the wrongly done write access damages a data structure that is used only later, producing incorrect output of the completely irrelevant code. During debugging, you observe "the rise of machines" - the data structure mysteriously obtains the wrong values that have never been assigned, against the programmers will. You may be searching for an error thousands of LOC away from where it really is.
精彩评论