Lowest possible memory address on modern OS

3

I've recently been pointed into one of my C programs that, should the start address of the memory block be low enough, one of my tests would fail as a consequence of wrapping around zero, resulting in a crash.

At first i thought "this is a nasty potential bug", but then, i wondered : can this case happen ? I've never seen that. To be fair, this program has already run millions of times on a myriad of systems, and it never happened so far.

Therefore, my question is : What is the lowest possible memory address that a call to malloc() may return ? To the best of my knowledge, i've never seen addresses such as 0x00000032 for example.

I'm only interested in "modern" environments, such as Linux, BSD and Windows. This code is not meant to run on a C64 nor whatever hobby/research OS.

c
memory
malloc
memory-address
asked on Stack Overflow Dec 8, 2012 by Cyan • edited Dec 9, 2012 by Matt

3 Answers

8

First of all, since that's what you asked for, I'm only going to consider modern systems. That means they're using paged memory and have a faulting page at 0 to handle null pointer dereferences.

Now, the smallest page size I'm aware of on any real system is 4k (4096 bytes). That means you will never have valid addresses below 0x1000; anything lower would be part of the page containing the zero address, and thus would preclude having null pointer dereferences fault.

In the real world, good systems actually keep you from going that low; modern Linux even prevents applications from intentionally mapping pages below a configurable default (64k, I believe). The idea is that you want even moderately large offsets from a null pointer (e.g. p[n] where p happens to be a null pointer) to fault (and in the case of Linux, they want code in kernelspace to fault if it tries to access such addresses to avoid kernel null-pointer-dereference bugs which can lead to privilege elevation vulns).

With that said, it's undefined behavior to perform pointer arithmetic outside of the bounds of the array the pointer points into. Even if the address doesn't wrap, there are all sorts of things a compiler might do (either for hardening your code, or just for optimization) where the undefined behavior could cause your program to break. Good code should follow the rules of the language it's written in, i.e. not invoke undefined behavior, even if you expect the UB to be harmless.

answered on Stack Overflow Dec 8, 2012 by R.. GitHub STOP HELPING ICE • edited Dec 8, 2012 by R.. GitHub STOP HELPING ICE
4

You probably mean that you are computing &a - 1 or something similar.

Please, do not do this, even if pointer comparison is currently implemented as unsigned comparison on most architectures, and you know that (uintptr_t)&a is larger than some arbitrary bound on current systems. Compilers will take advantage of undefined behavior for optimization. They do it now, and if they do not take advantage of it now, they will in the future, regardless of “guarantees” you might expect from the instruction set or platform.

See this well-told anecdote for more.

In a completely different register, you might think that signed overflow is undefined in C because it used to be that there were different hardware choices such as 1's complement and sign-magnitude. Therefore, if you knew that the platform was 2's complement, an expression such as (x+1) > x would detect MAX_INT.

This may be the historical reason, but the reasoning no longer holds. The expression (x+1) > x (with x of type int) is optimized to 1 by modern compilers, because signed overflow is undefined. Compiler authors do not care that the original reason for undefinedness used to be the variety of available architectures. And whatever undefined thing you are doing with pointers is next on their list. Your program will break tomorrow if you invoke undefined behavior, not because the architecture changed, but because compilers are more and more aggressive in their optimizations.

answered on Stack Overflow Dec 8, 2012 by Pascal Cuoq • edited Dec 8, 2012 by Pascal Cuoq
2

Dynamic allocations are performed on heap. Heap resides in a process address space just after the text (the program code), initialized data and uninitialized data sections, see here: http://www.cprogramming.com/tutorial/virtual_memory_and_heaps.html . So the minimal possible address in the heap depends on the size of these 3 segments thus there is no absolute answer since it depends on the particular program.

answered on Stack Overflow Dec 8, 2012 by SomeWittyUsername

User contributions licensed under CC BY-SA 3.0