Find the size of reserved memory for a character array in C

匿名 (未验证) 提交于 2019-12-03 01:22:02

问题:

I'm trying to learn C and as a start, i set off writing a strcpy for my own practice. As we know, the original strcpy easily allows for security problems so I gave myself the task to write a "safe" strcpy.

The path I've chosen is to check wether the source string (character array) actually fits in the destination memory. As I've understood it, a string in C is nothing more than a pointer to a character array, 0x00 terminated.

So my challenge is how to find how much memory the compiler actually reserved for the destination string?

I tried:

sizeof(dest) 

but that doesn't work, since it will return (as I later found out) the size of dest which is actually a pointer and on my 64 bit machine, will always return 8.

I also tried:

strlen(dest) 

but that doesn't work either because it will just return the length until the first 0x0 is encountered, which doesn't necessarily reflect the actual memory reserved.

So this all sums up to the following question: How to find our how much memory the compiler reserved for my destination "string"???

Example:

char s[80] = ""; int i = someFunction(s); // should return 80 

What is "someFunction"?

Thanks in advance!

回答1:

You can use sizeof to check at compile time:

char s[80] = ""; int i = sizeof s ; // should return 80 

Note that this fails if s is a pointer:

char *s = ""; int j = sizeof s;  /* probably 4 or 8. */ 

Arrays are not pointers. To keep track of the size allocated for a pointer, the program simply must keep track of it. Also, you cannot pass an array to a function. When you use an array as an argument to a function, the compiler converts that to a pointer to the first element, so if you want the size to be avaliable to the called function, it must be passed as a parameter. For example:

char s[ SIZ ] = ""; foo( s, sizeof s ); 


回答2:

Once you pass a char pointer to the function you are writing, you will loose knowledge for how much memory is allocated to s. You will need to pass this size as argument to the function.



回答3:

So this all sums up to the following question: How to find our how much memory the compiler reserved for my destination "string"???

There is no portable way to find out how much memory is allocated. You have to keep track of it yourself.

The implementation must keep track of how much memory was malloced to a pointer, and it may make something available for you to find out. For example, glibc's malloc.h exposes

size_t malloc_usable_size (void *__ptr) 

that gives you access to roughly that information, however, it doesn't tell you how much you requested, but how much is usable. Of course, that only works with pointers you obtained from malloc (and friends). For an array, you can only use sizeof where the array itself is in scope.



回答4:

char s[80] = ""; int i = someFunction(s); // should return 80 

In an expression s is a pointer to the first element of the array s. You cannot deduce the size of an array object with the only information of the value of a pointer to its first element. The only thing you can do is to store the information of the size of the array after you declare the array (here sizeof s) and then pass this information to the functions that need it.



回答5:

There's no portable way to do it. However, the implementation certainly needs to know this information internally. Unix-based OSes, like Linux and OS X, provide functions for this task:

// OS X #include <malloc/malloc.h>  size_t allocated = malloc_size(somePtr);  // Linux #include <malloc.h>  size_t allocated = malloc_usable_size(somePtr);   // Maybe Windows...  size_t allocated = _msize(somePtr); 


回答6:

A way to tag the member returned by malloc is to always malloc an extra sizeof(size_t) bytes. Add that to the address malloc returns, and you have a storage space for storing the actual length. Store the malloced size - the sizeof (size_t) there, and you have the basis for your new set of functions.

When you pass two of these sorts of pointers into your new-special strcpy, you can subtract sizeof(size_t) off the pointers, and access the sizes directly. That lets you decide if the memory can be copied safely.

If you are doing strcat, then the two sizes, along with calculating the strlens means you can do the same sort of check to see if the results of the strcat will overflow the memory.

It's doable. It's probably more trouble than it's worth.

Consider what happens if you pass in a character pointer that was not mallocated. The assumption is that the size is before the pointer. That assumption is false. Attempting to access the size in that case is undefined behavior. If you are lucky, you may get a signal.

One other implication of that sort of implementation is that when you go to free the memory, you have to pass in exactly-the-pointer-that-malloc-returned. If you don't get that right, heap corruption is possible.

Long story short... Don't do it that way.



回答7:

For situations where you are using character buffers in your program, you can do some smoke and mirrors to get the effect that you want. Something like this.

char input[] = "test"; char output[3];  if (sizeof(output) < sizeof(input)) {     memcpy(output,input,sizeof(input) + 1); } else {     printf("Overflow detected value <%s>\n",input); } 

One can improve the error message by wraping the code in a macro.

#define STRCPYX(output,input)                                        \ if (sizeof(output) < sizeof(input))                                  \ {                                                                    \     memcpy(output,input,sizeof(input) + 1);                          \ }                                                                    \ else                                                                 \ {                                                                    \     printf("STRCPYX would overflow %s with value <%s> from %s\n",    \                                    #output,       input,   #input);  \ }                                                                    \  char input[] = "test"; char output[3]; STRCPYX(output,input); 

While this does give you what you want, the same sort of risks apply.

char *input = "testing 123 testing"; char output[9]; STRCPYX(output,input); 

the size of input is 8, and output is 9, the value of output ends up as "Testing "

C was not designed to protect the programmer from doing things incorrectly. It is kind of like you are attempting to paddle upriver :) It is a good exercise to think about.



回答8:

Although arrays and pointers can appear to be interchangeable, they differ in one important aspect; an array has size. However because an array when passed to a function "degrades" to a pointer, the size information is lost.

The point is that at some point you know the size of the object - because you allocated it or declared it to be a certain size. The C language makes it your responsibility to retain and disseminate that information as necessary. So after your example:

char s[80] = "";  // sizeof(s) here is 80, because an array has size int i = someFunction(s, sizeof(s)) ; // You have to tell the function how big the array is. 

There is no "magic" method of determining the size of the array within someFunction(), because that information is discarded (for reasons of performance and efficiency - C is relatively low level in this respect, and does not add code or data that is not explicit); if the information is needed, you must explicitly pass it.

One way in which you can pass a string and retain size information, and even pass the string by copy rather than by reference is to wrap the string in a struct thus:

typedef struct {     char s[80] ;  } charArray_t ; 

then

charArray_t s ; int i = someFunction( &s ) ; 

with a definition of someFunction() like:

int someFunction( charArray_t* s )  {     return sizeof( s->s ) ;  } 

You don't really gain much by doing that however - just avoid the additional parameter; in fact you loose some flexibility because someFunction() now only takes a fixed array length defined by charrArray_t, rather than any array. Sometimes such restrictions are useful. On feature of this approach is that you can pass by copy this:

int i = someFunction( s ) ; 

then

int someFunction( charArray_t s )  {     return sizeof( s.s ) ;  } 

since structures unlike arrays can be passed this way. You can equally return by copy as well. It can be somewhat inefficient however. Sometimes the convenience and safety outweigh the inefficiency however.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!