Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #151069 > unrolled thread

memcpy can't take null.

Started byMalcolm McLean <malcolm.arthur.mclean@gmail.com>
First post2020-04-05 06:47 -0700
Last post2020-04-08 01:32 +0200
Articles 9 on this page of 29 — 13 participants

Back to article view | Back to comp.lang.c


Contents

  memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-05 06:47 -0700
    Re: memcpy can't take null. Öö Tiib <ootiib@hot.ee> - 2020-04-05 07:30 -0700
      Re: memcpy can't take null. Richard Damon <Richard@Damon-Family.org> - 2020-04-05 14:39 -0400
        Re: memcpy can't take null. Philipp Klaus Krause <pkk@spth.de> - 2020-04-06 18:46 +0200
          Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-06 12:45 -0700
            Re: memcpy can't take null. Philipp Klaus Krause <pkk@spth.de> - 2020-04-07 07:28 +0200
    Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-05 16:00 -0700
      Re: memcpy can't take null. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2020-04-05 18:29 -0600
        Re: memcpy can't take null. scott@slp53.sl.home (Scott Lurndal) - 2020-04-06 18:08 +0000
          Re: memcpy can't take null. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2020-04-06 15:44 -0600
      Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-06 13:54 -0700
        Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-06 14:30 -0700
    Re: memcpy can't take null. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2020-04-06 01:20 +0100
    Re: memcpy can't take null. Siri Cruise <chine.bleu@yahoo.com> - 2020-04-05 19:09 -0700
    Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-07 16:50 +0200
      Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-07 09:19 -0700
      Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-07 09:21 -0700
        Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-08 01:41 +0200
          Re: memcpy can't take null. scott@slp53.sl.home (Scott Lurndal) - 2020-04-08 00:30 +0000
            Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-08 02:06 -0700
              Re: memcpy can't take null. Richard Damon <Richard@Damon-Family.org> - 2020-04-08 09:17 -0400
                Re: memcpy can't take null. Tim Rentsch <tr.17687@z991.linuxsc.com> - 2020-04-09 07:16 -0700
              Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-08 13:33 -0700
                Re: memcpy can't take null. Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2020-04-09 09:05 -0700
                  Re: memcpy can't take null. Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2020-04-09 13:28 -0700
                    Re: memcpy can't take null. richard@cogsci.ed.ac.uk (Richard Tobin) - 2020-04-09 23:41 +0000
            Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-08 16:17 +0200
      Re: memcpy can't take null. Ike Naar <ike@sdf.lonestar.org> - 2020-04-07 22:01 +0000
        Re: memcpy can't take null. Guillaume <message@bottle.org> - 2020-04-08 01:32 +0200

Page 2 of 2 — ← Prev page 1 [2]


#151230

FromRichard Damon <Richard@Damon-Family.org>
Date2020-04-08 09:17 -0400
Message-ID<C9kjG.316344$gW1.16632@fx03.iad>
In reply to#151228
On 4/8/20 5:06 AM, Malcolm McLean wrote:
>>
> If Nemployees is non-zero whilst the pointer employees points to invalid
> memory, it's likely that the bug will be discovered soon enough.
> But if Nemployees is zero and employees is NULL, then the loops through
> the employees array will all be no-ops and have defined, correct
> behaviour. It's only if you call a library function like memcpy that
> the code becomes incorrect.
> 

The issue is that since memcpy is a very basic function, you want it as
efficient as possible. On some machines, it may be quicker to load the
pointers into registers that might object to having a null pointer
before processing the loop, and thus the machine might trap on a null
pointer.

Also, if you really want this safe version, you can make a memcpy_safe0
function defined as:

void* memcpy_safe0(void* dest, const void* src, size_t num) {
  if(num) return memcpy(dest, src, num);
  else return dest;
}

Note, that if the library function includes the check, there is no way
to wrap it to remove that check, thus it make sense to make the standard
version the quick one.

Note that in the above, with a decent optimizing compiler, the call to
memcpy above is tail-recursive, so becomes just a jump (or an inline
expansion of the memcpy code), and the above definition could be made
inline so perhaps the compiler could know if num was zero or not an
optimize the code.

[toc] | [prev] | [next] | [standalone]


#151258

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2020-04-09 07:16 -0700
Message-ID<86d08gn3rx.fsf@linuxsc.com>
In reply to#151230
Richard Damon <Richard@Damon-Family.org> writes:

> On 4/8/20 5:06 AM, Malcolm McLean wrote:
>
>> If Nemployees is non-zero while the pointer employees points to invalid
>> memory, it's likely that the bug will be discovered soon enough.
>> But if Nemployees is zero and employees is NULL, then the loops through
>> the employees array will all be no-ops and have defined, correct
>> behaviour.  It's only if you call a library function like memcpy that
>> the code becomes incorrect.
>
> The issue is that since memcpy is a very basic function, you want it as
> efficient as possible.  On some machines, it may be quicker to load the
> pointers into registers that might object to having a null pointer
> before processing the loop, and thus the machine might trap on a null
> pointer.
>
> Also, if you really want this safe version, you can make a memcpy_safe0
> function defined as:
>
> void* memcpy_safe0(void* dest, const void* src, size_t num) {
>   if(num) return memcpy(dest, src, num);
>   else return dest;
> }
>
> Note, that if the library function includes the check, there is no way
> to wrap it to remove that check, thus it make sense to make the standard
> version the quick one.
>
> Note that in the above, with a decent optimizing compiler, the call to
> memcpy above is tail-recursive,

It is a tail call, but not a recursive tail call.

[toc] | [prev] | [next] | [standalone]


#151241

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2020-04-08 13:33 -0700
Message-ID<87a73llnu4.fsf@nosuchdomain.example.com>
In reply to#151228
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
[...]
> If Nemployees is non-zero whilst the pointer employees points to invalid
> memory, it's likely that the bug will be discovered soon enough.
> But if Nemployees is zero and employees is NULL, then the loops through
> the employees array will all be no-ops and have defined, correct
> behaviour. It's only if you call a library function like memcpy that
> the code becomes incorrect.

Your function that takes a pointer to an employee structure and
a count indicating the number of elements in the array should be
accompanied by documentation (at least a comment) that tells the
user what combinations are valid.  If you write a call that violates
those requirements, that call is incorrect -- even if the function
happens not to misbehave.

If that documentation says that func(NULL, 0) is valid and
func(NULL, 1) is not, that's fine -- but then you need to guarantee
that func(NULL, 0) won't blow up in future versions unless you want
to risk breaking existing code.

See memcpy for an example of this.  Ideally, your function should
be documented as clearly.  If it isn't, it might be impossible to
tell whether a given call is correct or not.

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#151267

FromMalcolm McLean <malcolm.arthur.mclean@gmail.com>
Date2020-04-09 09:05 -0700
Message-ID<3abbeb80-4c62-410e-a133-82d03e72b113@googlegroups.com>
In reply to#151241
On Wednesday, 8 April 2020 21:33:36 UTC+1, Keith Thompson  wrote:
> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
> [...]
> > If Nemployees is non-zero whilst the pointer employees points to invalid
> > memory, it's likely that the bug will be discovered soon enough.
> > But if Nemployees is zero and employees is NULL, then the loops through
> > the employees array will all be no-ops and have defined, correct
> > behaviour. It's only if you call a library function like memcpy that
> > the code becomes incorrect.
> 
> Your function that takes a pointer to an employee structure and
> a count indicating the number of elements in the array should be
> accompanied by documentation (at least a comment) that tells the
> user what combinations are valid.  If you write a call that violates
> those requirements, that call is incorrect -- even if the function
> happens not to misbehave.
>
> If that documentation says that func(NULL, 0) is valid and
> func(NULL, 1) is not, that's fine -- but then you need to guarantee
> that func(NULL, 0) won't blow up in future versions unless you want
> to risk breaking existing code.
> 
> See memcpy for an example of this.  Ideally, your function should
> be documented as clearly.  If it isn't, it might be impossible to
> tell whether a given call is correct or not.
> 
The standard is clear, though you have to refer back to the
preamble on library functions to read it. An array argument may
not be null unless otherwise specified, as happens for snprintf().

However if you look in the Apple man page for memcpy, the issue
is nowhere to be seen. 

[toc] | [prev] | [next] | [standalone]


#151280

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2020-04-09 13:28 -0700
Message-ID<87tv1sjtet.fsf@nosuchdomain.example.com>
In reply to#151267
Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
[...]
> The standard is clear, though you have to refer back to the
> preamble on library functions to read it. An array argument may
> not be null unless otherwise specified, as happens for snprintf().
>
> However if you look in the Apple man page for memcpy, the issue
> is nowhere to be seen. 

Perhaps not directly.  The Apple man page for memcpy (at least the
one I found in a quick Google search) says:

    The memcpy() function copies n bytes from memory area s2 to memory
    area s1.  If s1 and s2 overlap, behavior is undefined.  Applications
    in which s1 and s2 might overlap should use memmove(3) instead.

The phrase "memory area s2" seems a bit vague.  Perhaps Apple's
documentation defines it elsewhere.  But surely if s2 is a null pointer
there is no "memory area s2" (and likewise for s1, of course).

But it also says:

    The memcpy() function conforms to ISO/IEC 9899:1990 (``ISO C90'').

which does completely describe it.

The Linux man page for memcpy is similar.

In any case the definitive documentation for memcpy is the standard.
And when you document your own functions, I don't suggest following
the example of those man pages.

-- 
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#151283

Fromrichard@cogsci.ed.ac.uk (Richard Tobin)
Date2020-04-09 23:41 +0000
Message-ID<r6obrl$2rpf$1@macpro.inf.ed.ac.uk>
In reply to#151280
In article <87tv1sjtet.fsf@nosuchdomain.example.com>,
Keith Thompson  <Keith.S.Thompson+u@gmail.com> wrote:

>Perhaps not directly.  The Apple man page for memcpy (at least the
>one I found in a quick Google search) says:
>
>    The memcpy() function copies n bytes from memory area s2 to memory
>    area s1.  If s1 and s2 overlap, behavior is undefined.  Applications
>    in which s1 and s2 might overlap should use memmove(3) instead.
>
>The phrase "memory area s2" seems a bit vague.

That text, and the term "memory area", appear to derive ultimately
from the description of memcpy() in the System V manual page
MEMORY(3C), which starts:

  These functions operate efficiently on memory areas (arrays of
  characters bounded by a count, not terminated by a null character).

-- Richard

[toc] | [prev] | [next] | [standalone]


#151231

FromGuillaume <message@bottle.org>
Date2020-04-08 16:17 +0200
Message-ID<r6kmeo$1bg2$1@gioia.aioe.org>
In reply to#151221
Le 08/04/2020 à 02:30, Scott Lurndal a écrit :
> How would you validate any of the parameters to memcpy?  Particularly
> considering that from a hardware point of view, zero is a legal virtual
> address.

If you read my whole post, note that I exactly said that. You can't in 
the general case, but you could just for a null pointer. Whether that 
would be useful is also questionable, which I also said.

As to zero (null pointers) being valid, even for memcpy(), it's a bit 
confusing. Whereas 0 can be a "valid" address on some targets at a very 
low level, according to the standard (at least C99):

"If a null pointer constant is converted to a pointer type, the 
resulting pointer, called a null pointer, is guaranteed to compare 
unequal to a pointer to any object or function."

So by that definition, a null pointer can't point to any valid object.

And whatever happens with non-valid objects, is, you guessed... 
undefined behavior. What it all means is that the whole spirit of C (at 
least for a large chunk of the standard) doesn't really make any 
parameter invalid per se. It just states "undefined behavior" for some 
parameter values, and the cases in which some values are illegal are 
very few.

A corollary here is that you can perfectly pass pointers to non-valid 
objects to memcpy(). But: this triggers undefined behavior. We're 
running in circles. ;)

I'll have to admit that parameters triggering undefined behavior are not 
necessarily "invalid" per se by the standard. This kind of makes sense, 
but it is certainly confusing for many people.

Anyway, as I said in previous posts, this is really a non-issue: if you 
need a specific behavior for undefined behavior cases, just implement 
functions on top (or in replacement of) the existing ones. I'm fine with 
this.

One wrong approach though IMO would be *assuming* any specific behavior 
for undefined behavior cases.

[toc] | [prev] | [next] | [standalone]


#151213

FromIke Naar <ike@sdf.lonestar.org>
Date2020-04-07 22:01 +0000
Message-ID<slrn3tukr8pu12.cn3.ike@sdf.lonestar.org>
In reply to#151186
On 2020-04-07, Guillaume <message@bottle.org> wrote:
> void * mymemcpy(void *destination, const void *source, size_t size)
> {
>      if (destination == NULL)
>          return NULL;
>
>      memcpy(destination, source, size);

What does the function return here?

> }

[toc] | [prev] | [next] | [standalone]


#151215

FromGuillaume <message@bottle.org>
Date2020-04-08 01:32 +0200
Message-ID<r6j2hg$seb$1@gioia.aioe.org>
In reply to#151213
Le 08/04/2020 à 00:01, Ike Naar a écrit :
> On 2020-04-07, Guillaume <message@bottle.org> wrote:
>> void * mymemcpy(void *destination, const void *source, size_t size)
>> {
>>       if (destination == NULL)
>>           return NULL;
>>
>>       memcpy(destination, source, size);
> 
> What does the function return here?
> 
>> }

sorry, it should have been: return memcpy(destination, source, size);
of course.

[toc] | [prev] | [standalone]


Page 2 of 2 — ← Prev page 1 [2]

Back to top | Article view | comp.lang.c


csiph-web