How to know if a memory address is valid for current process or not ?

Pages: 12
Hi,

This is an interview question where in I was given the following scenario :

1
2
3
4
5
int main() {
  int* p = (int*) 0x1322 // This can be any random hex value.  [1]
  *p = 20;                                                     [2]
  return 0;                                                    [3]
}


How will you ensure graceful exit in the above program. Constraint : you can't have to delete lines [1], [2] & [3]; but can add conditions/code around it.

The solution which I thought of is :

1
2
3
4
5
6
7
int main() {
  int* p = (int*) 0x1322 // This can be any random hex value.  
  if (IsValidMemoryAddressForInt(p)) { // Assuming that there is some function IsValidMemoryAddressForInt() which can tell
    *p = 20;                           // if the pointer is valid for the current process or not.                          
  }
  return 0;                                                    
}


But the interviewer seemed unconvinced. And also I was not able to find any such function in c++.

How do we handle such scenarios keeping in mind the constraints given ? Is it even possible ?

Thanks for any help :)
Last edited on
Here's a way that (often) works on the Linux system I have to hand:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <sys/mman.h>
#include <stdio.h>
int main()
{
  int* p = (int*) 0x1322 // This can be any random hex value.
  long int ptr_as_int = (long int)p;
  printf("Ptr as int: %ld \n", (long int)p);
  long int boundary = ptr_as_int - (ptr_as_int % 4096);
  printf("Boundary: %ld \n", boundary);
  if (mmap
            ((void*)boundary, 4096, PROT_READ | PROT_WRITE,
             MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED) {
                perror("Unable to mmap(NULL)");
                fprintf(stderr, "Is /proc/sys/vm/mmap_min_addr non-zero?\n");
                return 1;
        }
  printf("Pointer is pointing at location: %ld \n", (long int)p);
  printf("Dereferencing pointer yields: %d\n", *p);
  *p = 20;                                                     [2]
  printf("Now it's: %d\n", *p);
  return 0;                                                   [3]
}


However, it won't work for all possible values of that hex number, and there's no guarantee that the mmap function will actually take the hint on where to map the memory.
Last edited on
Your interviewer was a jerk.


Unfortunately, there is no standard C++ way to protect against an access violation.

Some compilers (such as MSVC) have special constructs (__try blocks) that will allow you to catch things like access violations. In the *nix world, you will probably wind up playing with signals (or, preferably, POSIX’s sigaction).

Here is a blog I found that goes into some details (and frustration):
(NullPointerException in C++|https://cristianadam.eu/20160914/nullpointerexception-in-c-plus-plus/).


If you want to validate that a pointer addresses memory that belongs to you, again, that can be done, but not with C++: you need OS-specific libraries for that.


tl;dr 
C++ cannot help you (much), but it can be done with OS-specific code. Again, refer to the blog link for what C++ is capable of doing for you.



Oh, I might as well tackle the question you were given. The correct answer is:

Don’t do that!

Always maintain ownership of your pointers. (And, unless requirements forbid, use C++ facilities to avoid using pointers at all, like std::unique_ptr and std::vector and, yes, references.)

Unless your pointer is known to be initialized from:
  • an extant, in-scope object
  • a memory manager (ie, using new or malloc() or even OS-specific utilities like GlobalAlloc())

... then you should not dereference it.

Part of the way you prevent invalid accesses is to carefully manage your pointer’s scope. Initialize it when created; destroy it when you are done with it. In other words, make it impossible to access something that is bad.

Hope this helps.
The following "solution" only works on Windows, and specifically the Visual Studio C++ compiler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <windows.h>

int filterException(int code, PEXCEPTION_POINTERS ex) {
	std::cout << "Filtering " << std::hex << code << std::endl;
	return EXCEPTION_EXECUTE_HANDLER;
}

int main()
{
	__try {
		int* p = (int*)0x1322; // This can be any random hex value.  [1]
		*p = 20;
	}
	__except (filterException(GetExceptionCode(), GetExceptionInformation())) {
		std::cout << "Caught. Gracefully handled... for some definition of \"graceful\"\n";
	}
}


Stolen from: https://stackoverflow.com/questions/7049502/c-try-and-try-catch-finally
https://stackoverflow.com/a/7049836/8690169

Structured Exception Handling (SEH) is supported by the OS on Windows, and is accessible through Visual C++ extensions.

I could be funny and say "add a #if 0 and #endif" but that probably isn't what the interviewer wants...

This is a bull**** interview question, by the way. (Edit: After thinking about it more, I will give the benefit of the doubt that it's meant to be this way as a trick question. Or maybe the job you're applying for does very low-level OS tricks, so the question actually applied.) I also have no clue how to do this with GCC or *nix systems.
Last edited on
A snarky answer, by the way, might be:

1
2
3
4
5
6
int main() {
  return 0; // THis is the inserted line
  int* p = (int*) 0x1322 // This can be any random hex value.  [1]
  *p = 20;                                                     [2]
  return 0;                                                    [3]
}
can you wrap a try/throw/catch around it to ensure grace?
it would take some doing -- you would have to craft something to do the throw yourself, which may be back in OS land. Tricksy... I was hoping trying to delete the pointer might throw if it were out of bounds and you tried to delete it... but that isnt quite right either.
Last edited on
@jonnin, no, a memory access violation doesn't throw a C++ exception. It will only throw an OS "exception" (if that's even the right term for it, see what Duthomhas and I wrote).

@Repeater:
If we're being REALLY snarky, neither that nor the OP's code will compile, because of the missing semi-colon.

The ONLY solution is a #if 0 ... #endif (assuming lines 1-3 must stay contiguous).
1
2
3
4
5
6
7
int main() {
  #if 0
  int* p = (int*) 0x1322 // This can be any random hex value.  [1]
  *p = 20;                                                     [2]
  return 0;                                                    [3]
  #endif
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main() {
  return 0;
  int* p = (int*) 0x1322 // This can be any random hex value.  [1]
  *p = 20;                                                     [2]
  return 0;                                                    [3]
}

int main() {
  int* p = (int*) 0x1322 // This can be any random hex value.  [1]
  if (0)
    *p = 20;                                                     [2]
  return 0;                                                    [3]
}

int main() {
  int* p = (int*) 0x1322 // This can be any random hex value.  [1]
  int foo;
  p = &foo;
  *p = 20;                                                     [2]
  return 0;                                                    [3]
}
¿what kind of software they develop?
Being charitable, the interviewer may have been hoping you would refuse to do it and explain why it was stupid, then explain what you would do to make sure it doesn’t happen in code you write.

Then, to butter up the answer, you say that for mission-critical code you would make sure to use the OS facilities to prevent anything that manages to escape attempts at well-crafted code from terminating an application.

That, and your application would be running from a controller process (launcher), that would recognize when the process died an unexpected death, and relaunch it with the as much undamaged state as possible.
I know there isnt a standard thrown error, but I was looking for a way to trick it / get from a sideways approach. They all seem to come back to talking to the OS though, as you guys already knew.
Hey, jonnin, click the link I shared. It has some interesting reading, plus examples of where the C++ Standards Committee has managed to agree on something touching the issue.
closed account (E0p9LyTq)
How will you ensure graceful exit in the above program.

If'n it were my interview I might say, "only hire C++ programmers who know C++ alone can't do that, it requires using OS routine(s)"

neither that nor the OP's code will compile, because of the missing semi-colon.

That might be the real interview test, noticing the missing semi-colon.
I suspect the interviewer had limited understanding of C++/coding himself, which is, sadly, not an uncommon experience.

Not for the missing semicolon, but the comment being specific to say that it could be any random hex value — unnecessary detail. The reality is it could be any random integer value no matter what base it is expressed in. No one would care to make a point of it unless:
  • he were trying to teach something
  • he didn’t know that using hex is just a C/C++ convention, and has nothing to do with a pointer’s value
  • he was just really pressed for time and wrote it on a napkin on the way into the interview

Could be the last one, but... Occam’s razor, and all.
I don't think it's necessarily a bad question. Someone who doesn't know enough will most likely be lured into thinking there is some way to safely execute those lines of code, which is probably what the interviewer wants because that tells him the person is not suitable for the job.
Apologies for missing semi-colon in statement [1], it is ending with a semi-colon.
Thanks
Thanks alot to all for the inputs. Many new things to learn.
On the systems I've worked on, a bad memory access raises a signal, usually either SIGBUS or SIGSEGV. So just catch the signal. This is probably what the interviewer was looking for.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <csignal>

using std::signal;
using std::cout;

void
handler(int sig)
{
    cout << "Address is not valid\n" << std::flush;
    exit(0);
}

int
main()
{
    signal(SIGBUS, handler);
    signal(SIGSEGV, handler);
    int *p = (int *) 0x1322;	// This can be any random hex value.
    * p = 20;
    cout << "Address is valid\n";
    return 0;
}

> On the systems I've worked on, a bad memory access raises a signal

Gross violation of the type system just engenders undefined behaviour; a really bad memory access may be 'good' from the limited perspective that the operating system has.
On the systems I've worked on, a bad memory access raises a signal, usually either SIGBUS or SIGSEGV. So just catch the signal.

And what are you going to do if you have no OS to rely on? That code in the OP is basically C code so it is really dangerous to assume that you have an OS. Even if the code was C++ you still can't really depend on having an OS.

jlb wrote:
you still can't really depend on having an OS.

Nah, all development targets specific systems.

Hence the advice to be aware of your target systems when designing system-critical code.
Pages: 12