Can we access dangling pointer /access other process memory similar to anti-virus? Would memory leak be possible for such inter-process access?

StackOverflow https://stackoverflow.com/questions/22514579

Question

I was always under the impression that trying to access a dynamically freed (first allocated and later deleted/freed) memory would end up with a coredump. However when I executed the below code it went through successfully.

 int* ptr = new int(3);
delete ptr;

cout << "value : " << *ptr << "  " << ptr;

So I went ahead and created a dangling pointer and explicitly tried to access the memory, but now it dumped.

int* aptr;
aptr = (int*)0x561778;
cout << "value : " << *aptr << "  " << aptr;

1) If we cannot access a memory beyond a given process space, then how is it that I was able to access the memory after I freed/released it? ( First example )

Also, If that's the case then how do anti-viruses scan the memory allocated by other processes?

2) If I dynamically allocate but don't free the memory, then it would cause memory leak. But what if my entire process was killed? or completed execution, so got closed. Then wouldn't OS ensure to clean up all the resources allocated to this process? So would memory leak occur only for processes which are on a long run?

If this is true, then when I explicitly try to access the contents of another process how would an OS ensure that it doesn't free up this memory?

Was it helpful?

Solution

1) If we cannot access a memory beyond a given process space, then how is it that I was able to access the memory after I freed/released it? ( First example )

Because the C or C++ runtime keeps a "heap" of memory, and when you call free or delete, the memory is not actually rendered unusable to the process, it is simply put back into the "free memory" region of the heap, so it will be re-used. This is done because it's very common for a process to allocate some memory, free it, allocate some again, free it, etc. For example

void readfile(const std::string& fname)
{
    std::ifstream f(fname.c_str());
    std::string* content = new std::string;
    while(cin.getline(content))
    {
       ...
    }
    delete content;
}

This function (stupidly, because we should not allocate a std::string) will first allocate space for std::string, and then space for the content [possibly in several portions] inside std:string when the getline call is reading the file. There may be other memory allocations in for example `std::ifstream too.

The heap is designed to minimise the number of times it asks the OS to map/unmap memory from the global physical memory to a particular process, since it's quite "expensive" in terms of performance to map and unmap virtual memory in nearly all processors (in particular, unloading the now defunct memory pages from other cores will involve sending a message to the other processor, the other processor stopping what it's currently doing, updating it's virtual mappings, and then answering back "I've done that", before continuing where it was). And of course, if the OS doesn't unmap the memory of the process when the process stops using it, that same process could indeed "use" that memory address to find content of other processes - which would be a bad thing, so the OS will force all processor cores to give up it's memory mappings before that bit of memory can be used again for another process [at least].

Edit: To clarify, the heap will sometimes release memory back to the OS. For example, if you make a LARGE allocation, and then free that same allocation, it may well unmap that immediately, and thus you wouldn't be able to access the memory after it has been freed. Any access of memory after it has been freed is undefined behaviour, the runtime can (and quite often will) do anything it likes with the memory at that point. The most common scenarios are:

  1. Just keep it as it is, but put it in the "freed" pile.
  2. Keep it around as freed memory, but fill it with some "magic" pattern to detect when it has been written to, so "use after free" can be detected (very good thing to detect!)
  3. The memory is unmapped, and no longer available to the current process.
  4. The memory is almost immediately allocated for another purpose, and used again.

The same OS can at different times use any of these scenarios, in almost any order.

Also, If that's the case then how do anti-viruses scan the memory allocated by other processes?

Completely different question. They use either debug interfaces to read another process's memory, or their own kernel driver functionality that uses kernel functions that allow any process to read any other process's memory - after all, the kernel can do anything. [Actually, fairly often, the anti-virus software is more interested in what is being loaded into memory from a file, so apply file access filters that examine reads/writes of data to/from files, rather than scanning what is in memory]

2) If I dynamically allocate but don't free the memory, then it would cause memory leak. But what if my entire process was killed? or completed execution, so got closed. Then wouldn't OS ensure to clean up all the resources allocated to this process? So would memory leak occur only for processes which are on a long run?

A process' memory is freed when the process dies. Always, every time. Or you could cause a system to fail by starting a process that allocates a fair amount of memory, kill it on purpose, run it again, kill it, run, kill, etc. That would be a bad thing, right?

You can of course have a very quick memory leak. Try:

 std:string str;

 str.append(100000, 'x');
 std::vector<std::string> v;

 int i = 0;
 while(1)
 {
     std::cout << "i=" << i << std::endl;
     v.push_back(str);

It won't take that many seconds before the system starts swappng, and a little while later it will be killed (if you don't get bored and kill it first). Expect a linux system to get fairly unresponsive if you do this...

If this is true, then when I explicitly try to access the contents of another process how would an OS ensure that it doesn't free up this memory?

A normal process will not be able to access memory that belongs to another process - only through limited interfaces such as debug interfaces or specially written kernel drivers can you do this. Or by using OS-supported shared memory, where the same memory is mapped into two different processes with the permission of the OS, of course.

These methods of accessing memory of another process will involve some form of "reference counting" [in fact the same applies for example if the process is currently in a system call trying to save a 1000MB file, and the process is for one reason or another killed - say another thread causes a unrecoverable fault] - the OS keeps track of how many "users" there are of a given piece of memory, so that it doesn't pull the rug from under the feet of some process [or itself].

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top