How can I make sure that a caller passes a malloc'd pointer?
Asked Answered
P

11

6

I have a function which reallocs a pointer given as an argument to a new size. Now, the problem is that - according to the man page - realloc needs a pointer which has been returned by malloc or calloc before.

How can I make sure that the caller passes a pointer that meets those requirements? There seem to be no build-in C mechanics (like type qualifiers or something) to do so.

Now, before I restructure my API (as I consider the function as it is now not to be robust enough) - can you please verify that I haven't missed something?

Thanks in advance.

Edit: One solution would obviously be to malloc in the function. The problem with that is that the caller does not "see" the allocation. Thus I would need to explictly say in the docs that he has to free the pointer. That's even worse than to expect them to provide a malloc'd pointer (which would imply that the caller has to free it).

What I really want is something that blocks abuse at compile time. That, and a pony. ;-)

Projectile answered 23/7, 2010 at 14:5 Comment(3)
Do you need to make it really that fool-proof?Schoof
at least I would want to, yesProjectile
Since you can't, you should probably stop worrying and do something more interesting. People can subvert any and all checking or prevention you care to try. Don't waste time preventing. Invest time in crashing loudly and clearly so they can read the error message and realize their mistake.Birt
B
10

How can I make sure that the caller passes a pointer that meets those requirements?

Documentation.

Define the API.

If the person writing the caller refuses to follow the API, things crash. They refused to play by your rules and things crashed. What did they expect?

Birt answered 23/7, 2010 at 14:7 Comment(0)
R
4

At the end of the day, pointers in C are nothing but a machine word, which may or may not point to maningful data inside the process's memory, which may or may not be allocated by malloc. There is no such information attached to any pointer.

Just add a big, fat warning to the API. If the documentation clearly says "never ever pass this function pointer which wasn't malloc'd", and someone does it nonetheless and gets bugs/crashs, it's not your fault - there's no way you can program defensively here.

Reproval answered 23/7, 2010 at 14:12 Comment(0)
M
2

You cannot determine whether the area a pointer points to has been [m|c]alloc();'d. You can only define your API and hope your API users follow it.

Is allocating memory yourself (callee) prohibitive? You can still do the initial allocation with realloc(NULL, size); if you're bound by that.

More information on your problem would be nice.

Mump answered 23/7, 2010 at 14:10 Comment(0)
S
2

One possible way. Abstract it.

foo.c
#include "foo.h"
struct foo{
   ...
} ;

foo *newFoo(...)          
void resize(foo *f)      

foo.h

struct foo;
typedef struct foo;

caller.c

foo *myfoo;
myfoo = newFoo(..)
resize(myfoo);
Schoof answered 23/7, 2010 at 14:41 Comment(0)
T
2

How is the user supposed to obtain this malloc'ed pointer in first place? Isn't that possible that it is obtained by another function in your API? Otherwise it sounds like boilerplate-code to me, having to allocate a chunk of memory with malloc before calling your function.

In your situation I would try that. Having the caller obtain a valid pointer trough a function I provide and releasing it trough another function. Even better, wrap it all together in an opaque structure.

my_param_type my_param = init_param(....);
...
my_function_that_wants_malloc(.....,my_param);
...
free_param(my_param);

Does that make sense in your API?

Having its own type it will be clear even to the most lame user that he has to obtain it through the expected init_param() function.

Timmy answered 23/7, 2010 at 15:18 Comment(0)
W
0

You can maintain a static pointer within your function and call the realloc yourself. Return the static pointer. This way the user does not have worry with allocation or freeing. The variable will be freed automatically when the process dies.

Be aware that if realloc fails, it returns a null pointer but the original malloc'ed memory is left untouched, this is a common source of memory leaks.

Waxwork answered 23/7, 2010 at 14:12 Comment(2)
This doesn't answer the question.Deep
No, but it an important note that is likely applicable to any answer, so I upvoted it even though I'd prefer to say "if (tmp) a=tmp; else {note the failure but leave a as-is}". If the realloc fails, nobody should try to use the "expanded" area of the allocated space, but whoever owned the pointer before should be the one to delete it.Condottiere
F
0

Change your implementation so you don't use realloc. If the buffer passed in is too small, just malloc a new bigger one and memcpy the content of the passed in buffer to it. This will improve the usability of your API at the expense of a marginal loss of performance.

Felizio answered 23/7, 2010 at 14:40 Comment(2)
That's even worse. Now you have to rely on the caller to free() the new buffer. If they don't, it won't crash noisily; it will just leak memory. If you want to guard against users who don't read API docs, don't do it in a way that will fail silently.Seam
@Nathon: If you realloc a pointer in an API call that's potentially far worse than mallocing new memory. The original pointer may now be invalid but if the programmer hasn't read the API documentation he may not know that and decide to write more data to it. That could easily lead to subtle data corruption bugs, if for example the old pointer is now part way through a new different buffer.Felizio
M
0

If all you have is the pointer value, there is no (standard) way to determine if that pointer value was returned by malloc(). If you have deeply intimate knowledge of how dynamic memory allocation works on your platform, then you can make an educated guess based on the pointer value, but even that's not a guarantee you're looking at something that was actually a return value from malloc() as opposed to a value offset from that (e.g., *ptr = malloc(10); foo(&ptr[5]);).

One strategy that may or may not be worth the effort (IME, it isn't) is to hide malloc(), realloc(), and free() behind some kind of memory manager that keeps track of allocated pointers and sizes. Instead of calling realloc(), your code would call the resize function in the memory manager, which would return an error code if the pointer passed to it isn't in the list of managed pointers.

Me answered 23/7, 2010 at 14:52 Comment(0)
I
0

Create a custom data type that is a renamed void pointer.

typedef void* myPointerType;

Write an allocate function (that can be a simple wrapper around malloc) that the user must use to allocate memory.

myPointerType myAllocateFunction (size_t size) {
    return (myPointerType)(malloc(size));
}

You would also want to make a matching "free" function.

Now, have your function (the one that performs the realloc) take a myPointerType object as a parameter instead of a generic pointer. You can cast it back down to a void* to use with realloc. To get around this, the user would have to manually cast a non-malloced pointer to a myPointerType, but you can specify in your documentation that casting to and from myPointerType is not allowed (so if they do this and their app crashes, it's because they misused the API).

There are even stronger ways that you can enforce this, but I'm not sure if it would be worth the trouble. You can't completely fool-proof an API (you'd be amazed at how capable fools are these days). Whatever you end up implementing, the best way to ensure that your API is used correctly is to provide clear, comprehensive documentation.

You're on your own for the pony, sorry.

Ian answered 23/7, 2010 at 15:50 Comment(2)
Does that typedef go in a *.h file when building as static/shared lib? Sorry newb question.Minstrel
@TristonJ.Taylor- If this is part of the public interface to your shared library then yes, it needs to go in the header file. Otherwise, you'll have functions that reference types that haven't been defined and your users will get compile-time errors.Ian
C
0

If you're looking for a compile-time check, the only thing you can really do is declare some new type which is returned by "approved" allocation calls. I would suggest something like:

typedef struct MEM_INFO {void *ptr; int allocsize; struct *privateMemInfo priv;} ALLOCMEM[0];

The privateMemInfo would be defined in your .c file, rather than the header, thus somewhat protecting fields in it from mischief. Note that since ALLOCMEM would be an array type, ptr and allocsize would always be accessed using the arrow operator rather than the dot. A routine which takes an ALLOCMEM as a parameter would naturally be given a pointer to the ALLOCMEM, allowing it to perform relocation "nicely". Thus, for example, one would use:

  ALLOCMEM foo = {0};
  if (reallocate(foo,12345) >= 0) /* It worked */
Condottiere answered 23/7, 2010 at 15:54 Comment(2)
Great idea here but: typedef char * strptr; defeats you royally! strptr can be cast to a char * and the CPP will catch the error at compile/design time. In the case someone tries to subvert that, oh yeah, realloc WILL fail noisily on its own. Great Idea. Thanks. I think its what myself and the op are really looking for.Minstrel
My declaration is faulty, in that it needs to be ALLOCMEM[1], but the basic idea is that all of the allocation routines require a reference to a "struct MEM_INFO". While nothing would prevent code from messing with the contents of a "struct MEM_INFO", compile-time checking would ensure that nothing but a reference to such a structure could be passed to the allocation/deallocation routines.Condottiere
P
0

Thus I would need to explictly say in the docs that he has to free the pointer. That's even worse than to expect them to provide a malloc'd pointer (which would imply that the caller has to free it).

In C libraries it's a common practice:

Purely answered 23/7, 2010 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.