1

Let's assume that, we have a C API exposed by a C++ library where the C client queries a value by passing a void pointer and a key.

bool GetValue(const char* key, void* val) {
     if(val != NULL) {
         // find the value using the key with some logic of library
         // lets assume found value is a int
         int foundValue = 34;
         *(int*)val = foundValue;
         return true; // or false if key not found
     }
     return false;
}

The C client and the C++ library developers have an agreement about the size of values associated with keys. Let's assume the C++ library only has int and long long int, and the C client also knows it. So the C client is expected to behave as follows when the value associated with "some_key" is an int:

int valueHolder;
if(GetValue("some_key",&valueHolder)) {
     // do something with valueHolder
}

But the C client can cause stack memory corruption by providing pointer which points to less space. For example, if the C client provides a pointer which points to an unsigned char and the value written by the C++ library is int, stack memory corruption can happen.

So the C client should take proper action to avoid such a situation. My question is, how the C++ library can handle such memory corruption/crash situation and be robust to crash or memory corruption? (a small note: a template or function overload is not an option for me because C++ library can not expose such feature to a C client).

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Debashish
  • 1,155
  • 19
  • 34
  • 3
    `GetValue` knows nothing about what type `val` points to. So there is nothing you can do. Probably you should drop `void *val` and have different functions for different types. E.g. `GetValueInt(const char* key, int* val)` and `GetValueLongLöongInt(const char* key, long long int* val)` etc.. Maybe this is an [XY Problem](https://xyproblem.info/). – Jabberwocky Dec 04 '20 at 15:36
  • 3
    Can you change the API to add a “size of buffer” parameter along with the pointer? – Buddy Dec 04 '20 at 15:36
  • 2
    Neither C nor C++ are memory-safe languages. It gives more power to the developer with more responsibility. – Eugene Sh. Dec 04 '20 at 15:37
  • @EugeneSh. I agree :) – Debashish Dec 04 '20 at 15:38
  • Microsoft uses exceptions to solve this problem, returning false when an exception is caught inside the function. – Sprite Dec 04 '20 at 15:39
  • @Sprite, writing outside the buffer is not guaranteed to cause an exception. – HAL9000 Dec 04 '20 at 15:41
  • 1
    @Jabberwocky. `have different functions for different types` that looks like the final resort. We tried to avoid writing functions for all types actually but ended up with this situation. – Debashish Dec 04 '20 at 15:41
  • 1
    @Debashish cramming evreything into the same function by using just a void pointer cannot work, unless you have a 3rd parameter that contains some sort of type information, then you can have a switch/case on this type information and use the correponding cast `(int*)`, `(long long int*)`, `(char*)` or whatever – Jabberwocky Dec 04 '20 at 15:44
  • @Jabberwocky, yes switch case is an open option. – Debashish Dec 04 '20 at 15:45
  • 1
    @Debashish it's either this or my previous comment. If you find anything better, let me know. – Jabberwocky Dec 04 '20 at 15:46
  • But the client can still intensionally cause problems by mentioning the wrong type – Debashish Dec 04 '20 at 15:46
  • 2
    @Debashish yes of course, nobody claimed that C++ and C are safe languages. – Jabberwocky Dec 04 '20 at 15:46

2 Answers2

3

Simply you have to design API in such way that type of the value is clear.

So for example:

bool GetIntValue(const char* key, int* val) {
     if(val != NULL && IsIntValue(key)) {
         // find the value using the key with some logic of library
         // lets assume found value is a int
         int foundValue = 34;
         val = foundValue;
         return true; 
     }
     return false;
}

bool GetSizeValue(const char* key, size_t* val) {
     if(val != NULL && IsSizeValue(key)) {
         // find the value using the key with some logic of library
         // lets assume found value is a int
         size_t foundValue = 12;
         val = foundValue;
         return true; 
     }
     return false;
}

...

Or other way:

enum Types {
   Int,
   Size,
   ....
};

struct Variant {
   enum Types type;
   union {
       int integer;
       size_t size;
       ....
   };
};

bool GetVariantValue(const char* key, struct Variant* val) {
     if(val != NULL) {
         ...
         *val = MakeIntVariant(33);
         return true; 
     }
     return false;
}
Marek R
  • 32,568
  • 6
  • 55
  • 140
-3

Unfortunately it cannot be made robust, see this link: Checking if a pointer is allocated memory or not

You can catch the crash however using something like

try {} catch (...) {}

but there is no guarantee it will crash, and in fact it is very likely to instead overwrite/corrupt adjacent memory.

Sven Nilsson
  • 1,861
  • 10
  • 11
  • 4
    `catch(...)` does not catch crashes. It only catches exceptions. – François Andrieux Dec 04 '20 at 15:38
  • 1
    depending the on setup, invalid access will generate an exception – Sven Nilsson Dec 04 '20 at 15:39
  • @SvenNilsson, No, inavlid access cannot be made to cause an exception in all cases, there is still a risk that your program crashes at the spot, corrupts internal state, it may even go undetected. Protection violations can be made to cause exceptions, but not all invalid access is a protection violation. – HAL9000 Dec 04 '20 at 15:50