3

Processes in OS have their own virtual address spaces. Say, I allocate some dynamic memory using malloc() function call in a c program and subtract some positive value(say 1000) from the address returned by it. Now, I try to read what is written on that location which should be fine but what about writing to that location?

virtual address space also has some read only chunk of memory. How does it protect that?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Naman
  • 991
  • 2
  • 10
  • 20
  • It's more about the "may", than about the "*can*". – alk Jul 21 '16 at 14:18
  • 4
    Out-of-bounds access is undefined behavior, regardless of whether it is reading or writing. – EOF Jul 21 '16 at 14:19
  • MMU can map addresses with different attributes. – Eugene Sh. Jul 21 '16 at 14:21
  • This is firmly in the "it depends" category, and it depends on your OS, CPU and couple other things. From the perspective of C and C++, no you cannot, you can only form pointer to memory locations that are allocated by your program (or point exactly 1 "element" behind your allocations). – Xarn Jul 21 '16 at 14:21
  • There's a little ambiguity in your question in how you say "from its virtual memory". Do you mean the *entire* virtual address space that a 64-bit pointer could access? Or do you mean only the portion of that address space that the OS has actually mapped to that process? – Owen Jul 21 '16 at 14:48
  • @Owen I meant "only portion of that address space that the OS has actually mapped to that process". – Naman Jul 21 '16 at 14:52
  • This is a general operating system dependant question. Basically it depends on how your operating system maps and protects non allocated memory. Think on a 64bit operating system in which you have 2^64 addresses: Will all be readable? writable? – Luis Colorado Jul 22 '16 at 10:56
  • @Xarn, I'm afraid your comment was fine, up to the point you mentioned exactly 1 "element" behind. That's also incorrect, you cannot access **one "element" --- even one byte---** outside the `malloc(3)` result pointer. Doing results **in UB always**. – Luis Colorado Jul 22 '16 at 11:44
  • @Naman, the portion of that address space actually mapped does not have to be conex, and all the fragments can have different semantics (so this means different access levels) And what the OS does when you try an access outside these regions is also OS controlled, so everything is OS controlled, and you have not specified what operating system are using. – Luis Colorado Jul 22 '16 at 11:47
  • @LuisColorado Forming a pointer to != accessing. You are allowed to form a pointer, so you can have a half open range of [first, last), but not dereference it. – Xarn Jul 23 '16 at 08:21
  • @Xarn, a semiopen range of `[first, last)` is **never** one place behind the first element. It's indeed **on** it. It's behind the last element. By the way, a pointer behind the last element does not need to be checked, as it only happens when the range is indeed empty... and then, you can check if `first == last` for emptyness. – Luis Colorado Jul 25 '16 at 10:50

8 Answers8

4

TL;DR No, it's not allowed.


In your case, when you got a valid non-NULL pointer to a memory address returned by malloc(), only the requested size of memory is allocated to your process and you're allowed to use (read and / or write) into that much space only.

In general, any allocated memory (compile-time or run-time) has an associated size with it. Either overrunning or underruning the allocated memory area is considered invalid memory access, which invokes undefined behavior.

Even if, the memory is accessible and inside the process address space, there's nothing stopping the OS/ memory manager to return the pointer to that particular address, so, at best, either your previous write will be overwritten or you will be overwriting some other value. The worst case, as mentioned earlier, UB.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • "*... returned by malloc()*" or stack or static allocation. To cut it short: "allocated properly" – alk Jul 21 '16 at 14:27
  • Ok. I was wondering- In "Undefined behavior", process may also crash. This can happen "ONLY" when you try to write at "read only" location. This read only protection is done with one of page directory entry(read/write bit). I am focusing on crash at epoc of writing to read only location. Is it? – Naman Jul 21 '16 at 14:39
4

Say, I allocate some dynamic memory using malloc() function call in a c program and subtract some positive value(say 1000) from the address returned by it. Now, I try to read what is written on that location which should be fine but what about writing to that location?

What addresses you can read/write/execute from are based on a processes current memory map, which is set up by the operating system.

On my linux box, if I run pmap on my current shell, I see something like this:

evaitl@bb /proc/13151 $ pmap 13151
13151:   bash
0000000000400000    976K r-x-- bash
00000000006f3000      4K r---- bash
00000000006f4000     36K rw--- bash
00000000006fd000     24K rw---   [ anon ]
0000000001f25000   1840K rw---   [ anon ]
00007ff7cce36000     44K r-x-- libnss_files-2.23.so
00007ff7cce41000   2044K ----- libnss_files-2.23.so
00007ff7cd040000      4K r---- libnss_files-2.23.so
00007ff7cd041000      4K rw--- libnss_files-2.23.so
00007ff7cd042000     24K rw---   [ anon ]
...
[many more lines here...]

Each line has a base address, a size, and the permissions. These are considered memory segments. The last line either says what is being mapped in. bash is my shell. anon means this is allocated memory, perhaps for bss, maybe heap from malloc, or it could be a stack.

Shared libraries are also mapped in, that is where the the libnns_files lines come from.

When you malloc some memory, it will come from an anonymous program segment. If there isn't enough space in the current anon segment being used for the heap, the OS will increase its size. The permissions in those segments will almost certainly be rw.

If you try to read/write outside of space you allocated, behavior is undefined. In this case that means that you may get lucky and nothing happens, or you may trip over an unmapped address and get a SIGSEGV signal.

evaitl
  • 1,365
  • 8
  • 16
3

Now, I try to read what is written on that location which should be fine

It is not fine. According to the C++ standard, reading uninitialized memory has undefined behaviour.

but what about writing to that location?

Not fine either. Reading or writing unallocated memory also has undefined behaviour.

Sure, the memory address that you ended up in might be allocated - it's possible. But even if it happens to be, the pointer arithmetic outside of bounds of the allocation is already UB.

virtual address space also has some read only chunk of memory. How does it protect that?

This one is out of scope of C++ (and C) since it does not define virtual memory at all. This may differ across operating systems, but at least one approach is that when the process requests memory from the OS, it sends flags that specify the desired protection type. See prot argument in the man page of mmap as an example. The OS in turn sets up the virtual page table accordingly.

Once the protection type is known, the OS can raise an appropriate signal if the protection has been violated, and possibly terminate the process. Just like it does when a process tries to access unmapped memory. The violations are typically detected by the memory management unit of the CPU.

eerorika
  • 232,697
  • 12
  • 197
  • 326
3

Processes in OS have their own virtual address spaces. Say, I allocate some dynamic memory using malloc() function call in a c program and subtract some positive value(say 1000) from the address returned by it. Now, I try to read what is written on that location which should be fine but what about writing to that location?

No, it should not be fine, since only the memory region allocated by malloc() is guaranteed to be accessible. There is no guarantee that the virtual address space is contiguous, and thus the memory addresses before and after your region are accessible (i.e. mapped to virtual address space).

Of course, no one is stopping you from doing so, but the behaviour will be really undefined. If you access non-mapped memory address, it will generate a page fault exception, which is a hardware CPU exception. When it is handled by the operating system, it will send SIGSEGV signal or access violation exception to your application (depending ot the OS).

virtual address space also has some read only chunk of memory. How does it protect that?

First it's important to note that virtual memory mapping is realized partly by an external hardware component, called a memory management unit. It might be integrated in the CPU chip, or not. Additionally to being able to map various virtual memory addresses to physical ones, it supports also marking these addresses with different flags, one of which enables and disables writing protection.

When the CPU tries to write on virtual address, marked as read-only, thus write-protected, (for examble by MOV instruction), the MMU fires a page fault exception on the CPU.

Same goes for trying to access a non-present virtual memory pages.

Anton Angelov
  • 1,223
  • 7
  • 19
1

In the C language, doing arithmetic on a pointer to produce another pointer that does not point into (or one-past-the-end) the same object or array of objects is undefined behavior: from 6.5.6 Additive Operators:

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated

(for the purposes of this clause, a non-array object is treated as an array of length 1)


You could get unlucky and the compiler could produce still produce a pointer you're allowed to do things with and then doing things with them will do things — but precisely what those things are is anybody's guess and will be unreliable and often difficult to debug.

If you're lucky, the compiler produces a pointer into memory that "does not belong to you" and you get a segmentation fault to alert you to the problem as soon as you try to read or write through it.

Community
  • 1
  • 1
0

How the system behaves when you read/write an unmapped memory address depends basically on your operating system implementation. Operating systems normally behave differently when you try to access an unmapped virtual address. What happens when you try one access to an unmapped (or mapped for not-memory ---for example to map a file in memory) the operating system is taking the control (by means of a trap) and what happens then is completely operating system dependant. Suppose you have mapped the video framebuffer somewhere in your virtual address... then, writing there makes the screen change. Suppose you have mapped a file, then reading/writing that memory means reading or writing a file. Suppose you (the process running) try to access a swapped zone (due to physical memory lack your process has been partially swapped) your process is stopped and work for bringing that memory from secondary storage is begun, and then the instruction will be restarted. For example, linux generates a SIGSEGV signal when you try to access memory not allocated. But you can install a signal handler to be called upon receiving this signal and then, trying to access unallocated memory means jumping into a piece of code in your own program to deal with that situation.

But think that trying to access memory that has not been correctly acquired, and more in a modern operating system, normally means that your program is behaving incorrectly, and normally it will crash, letting the system to take the control and it will be killed.

NOTE

malloc(3) is not a system call, but a library function that manages a variable size allocation segment on your RAM, so what happens if you try to access even the first address previous to the returned one or past the last allocated memory cell, means undefined behaviour. It does not mean you have accessed unallocated memory. Probably you will be reading a perfectly allocated piece of memory in your code or in your data (or the stack) without knowing. malloc(3) tends to ask the operating system for continous large amounts of memory to be managed for many malloc calls between costly asking the operating system for more memory. See sbrk(2) or memmap(2) system calls manpages for getting more on this.

For example, either linux or bsd unix allocate an entry in the virtual address space of each process at page 0 (for the NULL address) to make the null pointer invalid access, and if you try to read or write to this address (or all in that page) you'll get a signal (or your process killed) Try this:

int main()
{
    char *p = 0; /* p is pointing to the null address */
    p[0] = '\n'; /* a '\n' is being written to address 0x0000 */
    p[1] = '\0'; /* a '\0' is being written to address 0x0001 */
}

This program should fail at runtime on all modern operating systems (try to compile it without optimization so the compiler doesn't eliminate the code in main, as it does effectively nothing) because you are trying to access an already allocated (for specific purposes) page of memory.

The program on my system (mac OS X, a derivative from BSD unix) just does the following:

$ a.out
Segmentation fault: 11

NOTE 2

Many modern operating systems (mostly unix derived) implement a type of memory access called COPY ON WRITE. This means that you can access that memory and modify it as you like, but the first time you access it for writing, a page fault is generated (normally, this is implemented as you receiving a read only page, letting the fault to happen and making the individual page copy to store your private modifications) This is very effective on fork(2), that normally are followed by an exec(2) syscall (only the pages modified by the program are actually copied before the process throws them all, saving a lot of computer power)

Another case is the stack growing example. Stack grows automatically as you enter/leave stack frames in your program, so the operating system has to deal with the page faults that happen when you PUSH something on the stack and that push crosses a virtual page and goes into the unknown. When this happens, the OS automatically allocates a page and converts that region (the page) into more valid memor (read-write normally).

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
0

Technically, a process has a logical address. However, that often gets conflated into a virtual address space.

The number of virtual addresses that can be mapped into that logical address space can be limited by:

  1. Hardware
  2. System resources (notably page file space)
  3. System Parameters (e.g., limiting page table size)
  4. Process quotas

Your logical address space consists of an array of pages that are mapped to physical page frames. Not every page needs to have such a mapping (or even is likely to).

The logical address space is usually divided into two (or more) areas: system (common to all processes) and user (created for each process).

Theoretically, there is nothing in the user space to being a process with, only the system address space exists.

If the system does not use up its entire range of logical addresses (which is normal), unused addresses cannot be accessed at all.

Now your program starts running. The O/S has mapped some pages into your logical address space. Very little of that address space it likely to be mapped. Your application can map more pages into the unmapped pages of logical address space.

Say, I allocate some dynamic memory using malloc() function call in a c program and subtract some positive value(say 1000) from the address returned by it. Now, I try to read what is written on that location which should be fine but what about writing to that location?

The processor uses a page table to map logical pages to physical page frames. If you do you say a number of things can happen:

  1. There is no page table entry for the address => Access violation. Your system may not set up a page table that can span the entire logical address space.

  2. There is a page table entry for the address but it is marked invalid => Access Violation.

  3. You are attempting to access a page that is not accessible in your current processor mode (e.g., user mode access to a page that only allows kernel mode access) => Access Violation.

virtual address space also has some read only chunk of memory. How does it protect that?

  1. You are attempting to access a page that in a manner not permitted to the page (e.g., write to readonly page, execute to a no execute page) => Access Violation The access allowed to a page is defined in the page table.

[Ignoring page faults]

If you make it though those tests, you can access the random memory address.

user3344003
  • 20,574
  • 3
  • 26
  • 62
-1

It does not. It's actually you duty as a programmer to handle this

King Natsu
  • 461
  • 4
  • 19