Need to find size of destination buffer for strncpy

S

Simon Biber

jacob said:
Replace all your malloc calls with this two functions.
They will allow you to know immediately the size of an
allocated object, besides giving you more security in handling
pointers, since they can give you a hint if the passed
pointer really points to an allocated buffer.
-------------------------------------------------------------cut here
/* this will be written at the end of the allocated block.
If this is overwritten it means something has written
beyond the block */
#define MAGIC 0xFFFF
/* This signature will be written at the start of the
allocated block to avoid freeing memory that wasn't
allocated at all */
#define SIGNATURE 12345678L
/* This variable counts the size of the memory allocated
so far. This is useful for detecting memory leaks */
static long AllocatedMemory;
/* This allocates a block, writing the signature, then
the size of the block. The useful data starts after
those two ints. At the end of the block, the "MAGIC"
number is written to detect overwrites */
void *allocate(int size)

This function should take a size_t value as argument. Many systems have
smaller int than size_t, and this unnecessarily restricts what can be
allocated. For example most 64-bit systems have 32-bit int and 64-bit
size_t.
{
register char *r;
register int *ip = NULL;

if (size <= 0)
return NULL;
size += 3 * sizeof(int);
r = malloc((size_t)size);
if (r == NULL)
return NULL;
memset(r, 0, (size_t) size);
AllocatedMemory += size;
ip = (int *) r;
*ip++ = SIGNATURE;

On an implementation with int less than 32 bits, you just mangled your
signature. According to the C99 standard, when converting integer values
to a type in which the value is not representable: "either the result is
implementation-defined or an implementation-defined signal is raised".
*ip++ = size;

If you took my advice to make size a size_t object, then the same
problem applies here: you're forcing it into an int.
memset(ip, 0, size - 3*sizeof(int));
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;

If size was not a multiple of sizeof(int) then this will be an unaligned
access. Such accesses will trap on many implementations. For example, on
the Sun SPARC architecture.

In addition, if int is 16 bits then 0xFFFF is outside the range of
signed int, and the implementation-defined result or signal referred to
above may occur.
return (r + 2 * sizeof(int));

It's possible that 2*sizeof(int) is not sufficient alignment for some
larger types. For example, if sizeof(int) is 2 but sizeof(long long) is
8 and long long requires 8-byte alignment then one will not be able to
store long longs into this block. Also, if sizeof(int) is 4 but
sizeof(long double) is 16 and requires 16-byte alignment then one will
not be able to store long doubles into this block.
} [...]

/* This will return the size of a block or -1 if it is not
a valid block
*/
int GetSize(void *block)
{
int *ip = block;

ip -= 2;
if (*ip != SIGNATURE)
return -1;
ip++;
return *ip;
}

If it is not a valid block as allocated by the allocate function above,
then trying to read two ints before the pointed-to location will
probably go outside of the object pointed to. This is of course
undefined behaviour, and may result in a segmentation fault.
 
C

CBFalconer

Simon said:
This function should take a size_t value as argument. Many systems
have smaller int than size_t, and this unnecessarily restricts
what can be allocated. For example most 64-bit systems have 32-bit
int and 64-bit size_t.
.... snip code ...

On an implementation with int less than 32 bits, you just mangled
your signature. According to the C99 standard, when converting
integer values to a type in which the value is not representable:
"either the result is implementation-defined or an
implementation-defined signal is raised".

Rather than such faulty code, Jacob might be well advised to look
at my nmalloc module and its associated malldbg module. This is
available under the terms of DJGPP copying, i.e. essentially
unrestricted, although I retain copyright. Malldbg implements
various things that are often available under *IX systems, and is
well decoupled from the malloc module proper. It even includes a
..txh documentation module, ready for inclusion in other
documentation and capable of generating virtually any form of
documentation.

In his environment the only question is whether sbrk or the
equivalent is available as a system call. The calls to the
debugging macros can all be stripped to utilize a non-gcc compiler.

It would be trivial to add a function to malldbg.c/h which returns
the actual allocation size (which may have been rounded up from the
original malloc call). This should return zero for any pointer
that is not malloced, and the detection is possible, although not
absolutely positive.

size_t mallsize(void *p); /* highly non-standard */

<http://cbfalconer.home.att.net/download>
 
J

jacob navia

Simon Biber a écrit :
This function should take a size_t value as argument. Many systems have
smaller int than size_t, and this unnecessarily restricts what can be
allocated. For example most 64-bit systems have 32-bit int and 64-bit
size_t.
The problem with size_t is that if you give it -4 it will try to
allocate 4GB...

Note the test for size <= 0 further below
On an implementation with int less than 32 bits, you just mangled your
signature. According to the C99 standard, when converting integer values
to a type in which the value is not representable: "either the result is
implementation-defined or an implementation-defined signal is raised".

Yes, you may use another signature in another environment. Anyway
since it is an arbitrary number, the result is the same, since we are
not interested in the value but in, the fact that we find it again

If you took my advice to make size a size_t object, then the same
problem applies here: you're forcing it into an int.

Yes, As I explained above.
memset(ip, 0, size - 3*sizeof(int));
ip = (int *) (&r[size - sizeof(int)]);
*ip = MAGIC;


If size was not a multiple of sizeof(int) then this will be an unaligned
access. Such accesses will trap on many implementations. For example, on
the Sun SPARC architecture.

In addition, if int is 16 bits then 0xFFFF is outside the range of
signed int, and the implementation-defined result or signal referred to
above may occur.
return (r + 2 * sizeof(int));


It's possible that 2*sizeof(int) is not sufficient alignment for some
larger types. For example, if sizeof(int) is 2 but sizeof(long long) is
8 and long long requires 8-byte alignment then one will not be able to
store long longs into this block. Also, if sizeof(int) is 4 but
sizeof(long double) is 16 and requires 16-byte alignment then one will
not be able to store long doubles into this block.

Can you tell me which implementation has int with 2 bytes and
long long with 8? I would like to know...
}
[...]

/* This will return the size of a block or -1 if it is not
a valid block
*/
int GetSize(void *block)
{
int *ip = block;

ip -= 2;
if (*ip != SIGNATURE)
return -1;
ip++;
return *ip;
}


If it is not a valid block as allocated by the allocate function above,
then trying to read two ints before the pointed-to location will
probably go outside of the object pointed to. This is of course
undefined behaviour, and may result in a segmentation fault.

Yes, a good thing. It will crash if you do pas it a nonsense
pointer. In most cases it will return -1.

Perfectly OK as behavior.
 
J

jacob navia

CBFalconer a écrit :
Rather than such faulty code, Jacob might be well advised to look
at my nmalloc module and its associated malldbg module.

OK Chuck, I will look into it but my code is not "faulty".
It *could* crash in some machines because the size is
not aligned, that's all.
 
M

Mark McIntyre

CBFalconer a écrit :

OK Chuck, I will look into it but my code is not "faulty".
It *could* crash in some machines because the size is
not aligned, that's all.

I think Simon pointed out a few other issues, for what its worth.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
M

Mark McIntyre

Simon Biber a écrit :
The problem with size_t is that if you give it -4 it will try to
allocate 4GB...

Simon's point was that many systems can allocate more memory than an
int's worth. The proposed function would be defective on such systems.
The negative is easily catered for (as indeed you did), plus quite
frankly if the documentation says "must be a positive value" then
anyone passing a -ve number deserves what they get - tell them to
RTFM.
Note the test for size <= 0 further below

Yes, though returning NULL is possibly not the ideal fix. It would get
implementation dependent, but I'd consider some means to notify the
user.
Yes, you may use another signature in another environment. Anyway
since it is an arbitrary number, the result is the same, since we are
not interested in the value but in, the fact that we find it again

Now that we know about this issue, this code is now *by design*
nonportable. That seems a peculiar decision, especially since the fix
is trivial.
Yes, As I explained above.

Again, code is now "broken" by design.
Yes, a good thing. It will crash if you do pas it a nonsense
pointer. In most cases it will return -1.

Perfectly OK as behavior.

Et tu, brute?

Remind me who it was in a different thread that was advocating maximum
safety and automation? Could it be the same person now saying its ok
to crash if getting unexpected inputs?

But as long as its documented, I agree its ok, though not ideal.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
R

Randy Howard

Simon Biber a écrit :
The problem with size_t is that if you give it -4 it will try to
allocate 4GB...

You eschew proper data types for faulty ones for a given problem space
because someone might feed it garbage input, rather than putting in
place a method to ensure reasonable input.

Nevermind that people could easily want to request more memory than
possible under a 32-bit int on 64-bit systems. I guess they don't
matter, because you don't think it's likely.

One look at the prototype for malloc() should be enough to tell us who
is correct, and who is wrong. Or, are you suggesting that the
parameter to malloc() should be of type int instead and that the
standard is flawed in that regard?
 
J

jacob navia

Mark McIntyre a écrit :
Simon's point was that many systems can allocate more memory than an
int's worth. The proposed function would be defective on such systems.

Maybe. I really do not care. I proposed this
code to be helpful to somebody that asked a question.

Now you want me to develop a general allocator
for all machines in existence?

In any case you can only talk nonsense without ever
proposing some code to actually solve a problem.

There is NO way in C to know if a pointer is valid
or not. You didn't know that?
Et tu, brute?

Remind me who it was in a different thread that was advocating maximum
safety and automation? Could it be the same person now saying its ok
to crash if getting unexpected inputs?

There is NO WAY I repeat, to find out if a pointer is valid
or not, at least under standard C. Under windows you can
do it but I am sure you would have cried like mad because
I use the windows API.

There is NO WAY that I can say something here that you have to
put your nose and triy to find something wrong with it.

Please stop this. I do not care about your opinion
 
R

Richard Heathfield

jacob navia said:
CBFalconer a écrit :

OK Chuck, I will look into it but my code is not "faulty".
It *could* crash in some machines because the size is
not aligned, that's all.

....and you didn't document the fact when you presented the code. How is that
"not faulty"?
 
R

Richard Heathfield

jacob navia said:

There is NO way in C to know if a pointer is valid
or not. You didn't know that?

But there is. Didn't you know that? I'll tell you about it.

1) whenever you define a pointer, either point it to an object (or function,
if it's a function pointer) or set it to NULL. Make sure that, from now on,
you only ever give it a determinate value.

2) When a pointer value becomes indeterminate (e.g. free(ptr)), set the
pointer value to NULL immediately.

3) Before using a pointer, check to see whether it's NULL. If not, you know
it's valid, provided you followed the above rules.

There is NO WAY that I can say something here that you have to
put your nose and triy to find something wrong with it.

We try to find stuff wrong with what Chris Torek writes, too. And Keith
Thompson. And Richard Bos. And everyone, basically. The differences between
them and you are as follows:

1) it's rather harder to find errors in their articles (but we still try);
2) when we do so, they're glad of the correction (or, if they disagree with
the error report, they are at least glad of the opportunity to clarify the
matter);
3) they care about the opinions of other language experts with regard to the
C language - they might not agree, but they're ready to listen.
Please stop this. I do not care about your opinion

If you don't want people to write replies to your Usenet articles, don't
write Usenet articles. If you are not interested in other people's
opinions, why bother with Usenet at all?
 
C

CBFalconer

jacob said:
CBFalconer a écrit :


OK Chuck, I will look into it but my code is not "faulty". It
*could* crash in some machines because the size is not aligned,
that's all.

Code that crashes is not faulty? This is a new one.
 
C

CBFalconer

jacob said:
Simon Biber a écrit :
The problem with size_t is that if you give it -4 it will try to
allocate 4GB...

and then get rejected by malloc. At least nmalloc. So what?
 
C

CBFalconer

jacob said:
Mark McIntyre a écrit :


Maybe. I really do not care. I proposed this
code to be helpful to somebody that asked a question.

Now you want me to develop a general allocator
for all machines in existence?

That is inherently impossible. That's why malloc etc. is a system
provided mechanism. If you know what ISO C provides, you can
document the non-standard assumptions needed for a particular code
module to function. Having done so you have a good idea whether or
not a port to another system is easy, feasible, hard, or
impossible.
 
C

CBFalconer

jacob said:
Mark McIntyre a écrit :

.... snip ...

There is NO WAY that I can say something here that you have to
put your nose and triy to find something wrong with it.

Please stop this. I do not care about your opinion

But WE care that other readers do not receive bad advice. Thus
anything erroneous requires correction from somebody. The
criterion for erroneous involves the C standard. With luck all the
errors will be picked up.

Newbies in general should not take newsgroup advice too seriously
without waiting at least a few days for criticisms to appear.
Regulars would do well to learn something from their exposed
mistakes.
 
M

Mark McIntyre

Mark McIntyre a écrit :

Maybe. I really do not care.

Yes, thats the problem. You don't care to listen to the opinions or
advice of others.
I proposed this
code to be helpful to somebody that asked a question.

Thats absolutely fine, but you should know CLC well enough to expect
others to comment on and correct any errors in posts. Such corrections
are posted in the same spirit as your post, ie to be helpful, and it
is extremely important not to take offense at or respond defensively
to such additions.
There is NO way in C to know if a pointer is valid
or not. You didn't know that?

You could always initialise it to point to a valid object.
There is NO WAY I repeat, to find out if a pointer is valid

Quite. Its impossible to tell if some random value stored in a pointer
type is usable. Many people advocate only using valid pointers to
avoid this issue...
There is NO WAY that I can say something here that you have to
put your nose and triy to find something wrong with it.

For what its worth, most of my post was positive, as was another post
I made in response to one of yours earlier. I have no grudge against
you, no beef with you or your application, no axe to grind, no
favorites to play. I do however have a beef against people posting
incorrect, inapplicable or nonsensical advice.
Please stop this.

I will continue to comment on all and any post that I want to. As and
when I notice something I feel I can contribute to, I will do so. If
someone corrects me, I'll do my best to accept it with grace. If I
notice something thats wrong I'll do my best to correct it politely. I
don't promise to remain civil if I get gratuitously flamed in
response.
I do not care about your opinion

Then stop reading my posts. Killfile me. Its that simple.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
R

Randy Howard

and then get rejected by malloc. At least nmalloc. So what?

Why would it automatically be rejected? On some platforms, a request
for 4GB of RAM could be reasonable. I've worked on systems with 8
physical CPUs and 64GB of RAM. That's not a totally unreasonable
number.
 
C

CBFalconer

Randy said:
and then get rejected by malloc. At least nmalloc. So what?

Why would it automatically be rejected? On some platforms, a request
for 4GB of RAM could be reasonable. I've worked on systems with 8
physical CPUs and 64GB of RAM. That's not a totally unreasonable
number.[/QUOTE]

Then it doesn't get rejected. But nothing says it has to be
accepted. In the case of nmalloc restriction to ((2 exp 31) -
65536) prevents ever confusing sbrk with a request to decrease
allocation. Among the reasons malloc packages are non-portable.
 
J

jaysome

#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)

The value of an undefined id is 0.

That's a bug in C.

If a preprocessor comparison operator is used with an undefined "id",
the preprocessor should consider that an error.

I ran across a similar problem recently. The solution was to insure
the "id" was defined, before using it. For example:

#ifndef __STDC_VERSION__
#error "__STDC_VERSION__ not defined ... try again"
#endif
#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)

It's sad one has to do these types of things to make things work with
Standard C. Especially when these types of things may cost you one or
more sleepless nights.

Happy New Year
 
M

Mark McIntyre

That's a bug in C.

no, its a feature :)

Personally I think its correct behaviour. Your example below of
__STDC_VERSION__ is using the macro wrongly, IMHO. If the value isn't
defined then it actually tells you something about the environment.
It's sad one has to do these types of things to make things work with
Standard C. Especially when these types of things may cost you one or
more sleepless nights.

It makes one use macros more carefully, sure. Thats probably a good
thing too.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,609
Members
45,253
Latest member
BlytheFant

Latest Threads

Top