1

I have a C++ API with a C wrapper. A C client can get a handle to an underlying C++ object and then use that to get other information about the object, e.g.

PersonHandle handle = createPerson("NisseIHult");
char* name = getPersonName(handle); //Get person takes a void* pointer

In the code above, the handle is casted to a C++ Person class object.

Question is, how can I check inside getPersonName that the argument, handle, is a valid handle? For example, if a client does this:

char* name = getPersonName(1234);

it will cause an access violation inside getPersonName. I need a way to check and validate the handle, and in the case above, return NULL?

Totte Karlsson
  • 1,261
  • 1
  • 20
  • 55
  • 2
    How do users of the `C` API create `PersonHandle` objects? Some type of `createPerson()` API? Track the valid persons that are actually created from there. – Chad Jan 23 '14 at 21:50
  • 1
    How do you resolve the handle? If you implemented some kind of handle-object mapping registry then you can validate a handle. – tiguchi Jan 23 '14 at 21:50
  • I like the idea of having an internal list of created handles. Then I can check and validate addresses. Good idea! – Totte Karlsson Jan 23 '14 at 21:53
  • Really Bad Idea. It was done before, IsBadReadPtr() is still in the winapi, they can't remove it anymore. Google it to see what's wrong with it. An access violation exception is obvious to any client programmer. Telling him that the handle is wrong, with a corrupted heap that destroyed your std::map is not. – Hans Passant Jan 23 '14 at 23:04
  • But an AV exception don't give a client much information. A BadHandle exception, that I can create seem to be nicer to a client. If I have a corrupted heap, then that IS a problem, but that would be unrelated. – Totte Karlsson Jan 23 '14 at 23:40

2 Answers2

3

Since handles are pointers to C++ objects, there is no reliable way to check their validity without triggering some undefined behavior.

I have seen two solutions to this problem:

  • Make handles integers for full control - rather than giving out pointers, keep pointers internally - say, in an unordered map from int to a pointer, along with some metadata, and give users integers to use as handles. This introduces an additional hash lookup in the process of accessing a person, but you can make this perfectly reliable, because all ints are under your control. For example, if you give out handles to objects of different types, you could produce a detailed error message, e.g. "a handle to a Horse object has been used where a Person handle is required". This solution has an additional advantage that you don't have dangling references: a user can pass a handle that you have invalidated, but you can quickly tell that the object is deleted.
  • Derive all objects to which you give handles from a common base class, and put a "magic number" into the first member of that class - A "magic number" is a bit pattern that you put, say, in an int, and set it in each Person object. After the cast you can check if (personFromHandle->magic != 0xBEEFBEEF) ... to see if the pattern is there. This solution is common, but I would recommend against it, because it has undefined behavior when an invalid handle is passed. It's not OK to use it if the operation is to continue after a failed attempt to use a handle. This solution would also break if passed a reference to a deallocated object.
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • I like both approaches. I may endup having an internal map that holds the handle (pointeraddress as a long) and the type of handle. I can then easily check both if a handle is a valid handle, and if its type pointed too is proper for the actual API function. – Totte Karlsson Jan 23 '14 at 22:06
  • @TotteKarlsson "would also break if passed a reference to a deallocated object" - you may just wipe that magic number upon object destruction. It still doesn't secure from passing invalid handle pointing to inaccessible memory. But you can check if memory address if readable before accessing it. Say, for Linux it could be done this way: http://stackoverflow.com/a/7138421/1259152 – olegst Apr 30 '15 at 11:25
  • @TotteKarlsson And magic number for every new object may be calculated with some fancy algorithm so that you would be sure magic number is really unique and doesn't coincide with some garbage. – olegst Apr 30 '15 at 11:28
1

Similar to the first part of the answer above, put the address of every object you hand out into a std::set (or similar container) and test for existence in the set before casting.

woolstar
  • 5,063
  • 20
  • 31