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


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

Safer and more convenient strcpy()

Started byHoward Chu <highlandsun@gmail.com>
First post2019-03-06 03:45 -0800
Last post2019-03-20 11:13 +0100
Articles 20 on this page of 42 — 13 participants

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


Contents

  Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-06 03:45 -0800
    Re: Safer and more convenient strcpy() Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-03-06 03:58 -0800
      Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-06 04:31 -0800
        Re: Safer and more convenient strcpy() "I Set My Girlfriend's Pantyhose on Fire" <cdalten@gmail.com> - 2019-03-06 05:18 -0800
          Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-06 13:03 -0800
            Re: Safer and more convenient strcpy() William Ahern <william@25thandClement.com> - 2019-03-06 16:51 -0800
              Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-07 07:40 -0800
                Re: Safer and more convenient strcpy() scott@slp53.sl.home (Scott Lurndal) - 2019-03-07 16:12 +0000
                  Re: Safer and more convenient strcpy() Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-03-07 08:28 -0800
                    Re: Safer and more convenient strcpy() scott@slp53.sl.home (Scott Lurndal) - 2019-03-07 16:51 +0000
                      Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-07 09:31 -0800
                      Re: Safer and more convenient strcpy() Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-03-07 11:17 -0800
                    Re: Safer and more convenient strcpy() Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-03-07 22:35 +0000
                      Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-07 15:32 -0800
                        Re: Safer and more convenient strcpy() scott@slp53.sl.home (Scott Lurndal) - 2019-03-08 14:29 +0000
                          Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-08 07:09 -0800
                        Re: Safer and more convenient strcpy() Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-03-08 15:39 +0000
                          Re: Safer and more convenient strcpy() Malcolm McLean <malcolm.arthur.mclean@gmail.com> - 2019-03-08 07:56 -0800
                            Re: Safer and more convenient strcpy() Jorgen Grahn <grahn+nntp@snipabacken.se> - 2019-03-09 08:12 +0000
                      Re: Safer and more convenient strcpy() William Ahern <william@25thandClement.com> - 2019-03-07 15:51 -0800
                        Re: Safer and more convenient strcpy() William Ahern <william@25thandClement.com> - 2019-03-07 16:38 -0800
                          Re: Safer and more convenient strcpy() Keith Thompson <kst-u@mib.org> - 2019-03-07 18:32 -0800
                            Re: Safer and more convenient strcpy() William Ahern <william@25thandClement.com> - 2019-03-07 20:00 -0800
                        Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-08 04:15 -0800
              Re: Safer and more convenient strcpy() "I Set My Girlfriend's Pantyhose on Fire" <cdalten@gmail.com> - 2019-03-07 12:15 -0800
        Re: Safer and more convenient strcpy() Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-03-06 09:12 -0800
    Re: Safer and more convenient strcpy() mark.bluemel@gmail.com - 2019-03-06 08:27 -0800
    Re: Safer and more convenient strcpy() Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-03-06 09:22 -0800
    Re: Safer and more convenient strcpy() Rosario19 <Ros@invalid.invalid> - 2019-03-07 09:10 +0100
      Re: Safer and more convenient strcpy() Rosario19 <Ros@invalid.invalid> - 2019-03-07 09:12 +0100
        Re: Safer and more convenient strcpy() mark.bluemel@gmail.com - 2019-03-07 00:55 -0800
    Re: Safer and more convenient strcpy() William Ahern <william@25thandClement.com> - 2019-03-07 16:09 -0800
      Re: Safer and more convenient strcpy() Howard Chu <highlandsun@gmail.com> - 2019-03-08 04:04 -0800
      Re: Safer and more convenient strcpy() eric.sanchis@iut-rodez.fr - 2019-04-15 04:40 -0700
        Re: Safer and more convenient strcpy() nobody@example.org (Scott) - 2019-04-16 03:33 +0000
          Re: Safer and more convenient strcpy() eric.sanchis@iut-rodez.fr - 2019-04-16 05:35 -0700
            Re: Safer and more convenient strcpy() nobody@example.org (Scott) - 2019-04-17 01:19 +0000
              Re: Safer and more convenient strcpy() eric.sanchis@iut-rodez.fr - 2019-04-17 05:35 -0700
                Re: Safer and more convenient strcpy() nobody@example.org (Scott) - 2019-04-17 16:59 +0000
                  Re: Safer and more convenient strcpy() eric.sanchis@iut-rodez.fr - 2019-04-17 23:40 -0700
    Re: Safer and more convenient strcpy() Philipp Klaus Krause <pkk@spth.de> - 2019-03-20 11:10 +0100
      Re: Safer and more convenient strcpy() Philipp Klaus Krause <pkk@spth.de> - 2019-03-20 11:13 +0100

Page 2 of 3 — ← Prev page 1 [2] 3  Next page →


#141803

FromWilliam Ahern <william@25thandClement.com>
Date2019-03-07 16:38 -0800
Message-ID<v5q8lf-saq.ln1@wilbur.25thandClement.com>
In reply to#141801
William Ahern <william@25thandclement.com> wrote:
> Jorgen Grahn <grahn+nntp@snipabacken.se> wrote:
>> On Thu, 2019-03-07, Malcolm McLean wrote:
>>> On Thursday, 7 March 2019 16:12:28 UTC, Scott Lurndal  wrote:
>> ...
>>>> How is that better than
>>>> 
>>>>    size_t numbytes = snprintf(dst, sizeof(dst), "%s", src);
>>>> 
>>>> safe, nul-byte terminated and returns an indication of whether
>>>> src was larger than dst.
>>> ?
>>> Because if you want to make more than one call, to concatenate strings,
>>> you have to recalcuate the size parameter, which is where errors 
>>> could creep in.
>> 
>> On the other hand, snprintf() itself has good support for
>> concatenating strings.
> 
> snprintf has more complex error conditions, though. For example, in glibc
> snprintf is implemented using the stdio subsystem, effectively invoking
> something like fprintf through a specialized FILE handle. Initialization of
> the internal data structures can require dynamic allocation and so even a
> simple format string like "%s" can return ENOMEM. AFAIU, floating point
> specifiers can require dynamic allocation in most (all?) snprintf
> implementations.
> 
> And then there's the matter that snprintf takes an int parameter for the
> buffer size as well as returning an int, raising the spectre of unexpected
> and undefined conversions and overflows. Indeed, the two issues
> combined--integer conversions and managing error conditions--makes the above
> example snprintf usage poorly written code, IMO.

Correction: it only returns an int; it takes size_t. However, there's the
issue of the treatment of a buffer size value greater than INT_MAX.

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


#141804

FromKeith Thompson <kst-u@mib.org>
Date2019-03-07 18:32 -0800
Message-ID<lnmum6ksdi.fsf@kst-u.example.com>
In reply to#141803
William Ahern <william@25thandClement.com> writes:
> William Ahern <william@25thandclement.com> wrote:
[...]
>> And then there's the matter that snprintf takes an int parameter for the
>> buffer size as well as returning an int, raising the spectre of unexpected
>> and undefined conversions and overflows. Indeed, the two issues
>> combined--integer conversions and managing error conditions--makes the above
>> example snprintf usage poorly written code, IMO.
>
> Correction: it only returns an int; it takes size_t. However, there's the
> issue of the treatment of a buffer size value greater than INT_MAX.

I think that's a matter of historical precedence, and likely not that
much of a problem in practice.

The *printf() functions have been defined to return int since before
size_t even existed.  size_t would make more sense, since the value is a
size in bytes.

But on a modern system with 16-bit int you aren't likely to need
character arrays longer than 32767 bytes (in fact it's not likely to be
a hosted implementation), and 2**31 bytes should be enough for anybody.

Yes, the irony in that last claim is deliberate, but you won't often see
buffers that big treated as strings.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

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


#141805

FromWilliam Ahern <william@25thandClement.com>
Date2019-03-07 20:00 -0800
Message-ID<9069lf-q2j.ln1@wilbur.25thandClement.com>
In reply to#141804
Keith Thompson <kst-u@mib.org> wrote:
> William Ahern <william@25thandClement.com> writes:
>> William Ahern <william@25thandclement.com> wrote:
> [...]
>>> And then there's the matter that snprintf takes an int parameter for the
>>> buffer size as well as returning an int, raising the spectre of unexpected
>>> and undefined conversions and overflows. Indeed, the two issues
>>> combined--integer conversions and managing error conditions--makes the above
>>> example snprintf usage poorly written code, IMO.
>>
>> Correction: it only returns an int; it takes size_t. However, there's the
>> issue of the treatment of a buffer size value greater than INT_MAX.
> 
> I think that's a matter of historical precedence, and likely not that
> much of a problem in practice.
> 
> The *printf() functions have been defined to return int since before
> size_t even existed.  size_t would make more sense, since the value is a
> size in bytes.
> 
> But on a modern system with 16-bit int you aren't likely to need
> character arrays longer than 32767 bytes (in fact it's not likely to be
> a hosted implementation), and 2**31 bytes should be enough for anybody.
> 
> Yes, the irony in that last claim is deliberate, but you won't often see
> buffers that big treated as strings.

In the intended, normal course of things I agree such a situation would be
rare or non-existent in most contexts. However, one must always be mindful
of malicious inputs. For example, the recent (January 2019) systemd journald
exploits rely on the ability send to build long, multi-gigabyte strings:

  If we send a large "native" message to /run/systemd/journal/socket:
  since the maximum size of a "native" entry is 768MB, and the minimum
  length of a "native" item is 3 ("A=\n"), and the size of an EntryItem
  structure is 16 (a 64-bit offset and a 64-bit hash), the maximum size of
  the attacker-controlled alloca() in journal_file_append_entry() is 768MB
  / 3 * 16 = 4GB, large enough to jump from journald's main stack into the
  mmap region, even on amd64.

  -- https://www.qualys.com/2019/01/09/system-down/system-down.txt

String concatenation is often performed over a set of strings, providing an
attacker a multiplier. The maximum size of generated strings from user input
can grow very quickly.

On Linux/x86_64 one can potentially pass roughly 281474976579584
(MAX_ARG_STRLEN * MAX_ARG_STRINGS) bytes of string data through main().

  https://github.com/torvalds/linux/blob/master/include/uapi/linux/binfmts.h

A program could easily blow INT_MAX just by concatenating argv to generate a
simple status or log message. These limits can even be hit unintentionally
in the wild.

  https://github.com/ninja-build/ninja/issues/1261

(In that case only hitting MAX_ARG_STRLEN.)

Even if one were inclined to exclude the possibility of malicious inputs,
such a decision can require more consideration and effort than simply
writing robust code in the first place. Most programmers (the defenders)
don't and can't think like attackers, including myself. We're horrible at
assessing real risk, particularly when it comes to the mechanics of
exploits. The famous last words of the authors of many vulnerabilities are
doubtless, "I can't imagine how this could be exploited." (I know this
because I regularly see programmers literally recite this on forums.)
Defenders have poor imaginations. Conversely, judging by their code most
attackers are horrible at writing good code. But that's not their
responsibility. All an attacker needs is a good imagination, tenacity, and a
little luck.

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


#141808

FromHoward Chu <highlandsun@gmail.com>
Date2019-03-08 04:15 -0800
Message-ID<8b33eb32-388c-44dc-9cdb-bf3673b1a1b7@googlegroups.com>
In reply to#141801
On Friday, March 8, 2019 at 12:00:15 AM UTC, William Ahern wrote:
> One can spend an eternity refining string interfaces trying to fill the gap,
> as exemplified in this and countless other threads. (How many times has C++
> tweaked the semantics of std::string?) But why bother? Most of the C library
> is suboptimal (as are most standard libraries in most languages, for that
> matter), yet most of us understand that consistency and uniformity make it
> cost effective to continue using those interfaces.

If the library isn't useful why continue carrying it around? The fact that
strcpy_s() was added to the standard library proves that safety, at least,
is considered more important than consistency and uniformity. Its usage
pattern is completely inconsistent with the pattern for strcpy().

On the side of consistency, however, the proposed stecpy() is semantically more consistent - it still allows constructions like

strcat(strcat(strcat(strcpy(buf, "This "),"is "),"a long "),"string.");

via

stecpy(stecpy(stecpy(stecpy(buf, "This ", bufend),"is ", bufend),"a long ", bufend),"string.", bufend);

I.e., it supports all of the original use cases of strcpy() and strcat() without needing to change any surrounding logic. The other newer interfaces all sacrifice original usecases to obtain safety, and that sacrifice was unnecessary.

> 
> String management, logging, configuration parsing--these are abstraction
> blackholes. The only way to win is to not play.

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


#141793

From"I Set My Girlfriend's Pantyhose on Fire" <cdalten@gmail.com>
Date2019-03-07 12:15 -0800
Message-ID<789d17d0-804f-4235-8406-8275cfcc4b51@googlegroups.com>
In reply to#141755
On Wednesday, March 6, 2019 at 4:52:01 PM UTC-8, William Ahern wrote:
> Howard Chu <highlandsun@gmail.com> wrote:
> > On Wednesday, March 6, 2019 at 1:19:15 PM UTC, I Set My Girlfriend's Pantyhose on Fire wrote:
> >> On Wednesday, March 6, 2019 at 4:31:28 AM UTC-8, Howard Chu wrote:
> >> > On Wednesday, March 6, 2019 at 11:58:29 AM UTC, Malcolm McLean wrote:
> >> > > On Wednesday, 6 March 2019 11:45:24 UTC, Howard Chu  wrote:
> >> > > > I was recently reminded of an old rant of mine, which reminded me of the even older discussion about better versions of strcpy().
> >> > > > 
> >> > > > https://www.openldap.org/lists/openldap-software/200303/msg00560.html
> >> > > > 
> >> > > > The function I advocated there eventually became more common, known as
> >> > > > stpcpy(), but it's still not a safer function. It's easy to write a safer
> >> > > > function that's still convenient to use though, like so:
> >> > > > 
> >> > > > char *stecpy(char *dst, const char *src, const char *end)
> >> > > > {
> >> > > >   while (*src && dst < end)
> >> > > >    *dst++ = *src++;
> >> > > >   if (dst < end)
> >> > > >    *dst = '\0';
> >> > > >   return dst;
> >> > > > }
> >> > > > 
> >> > > If the buffer is too small to fit the string, it is not nul-terminated.
> >> > > So you'll likely get a crash when you try to use the string. Better
> >> > > to lose a character and nul-terminate.
> >> > 
> >> > I suppose. Easily fixed:
> >> > 
> >> > char *stecpy(char *dst, const char *src, const char *end)
> >> > {
> >> >   end--;
> >> >   while (*src && dst < end)
> >> >    *dst++ = *src++;
> >> >   *dst = '\0';
> >> >   return dst;
> >> > }
> >> 
> >> 
> >> Wouldn't that fix introduce memory overflow error? Just curious because this looks like something similar that I encountered at my job recently.
> > 
> > Look again. No.
> 
> Yes, it does, if end - dst == 0:
> 
>   char buf[100], *p = buf, *pe = &buf[sizeof buf];
>   ... // series of operations that increments p to pe
>   stecpy(p, src, pe);
> 
> Also, end-- invokes undefined behavior if end is the start of an array, such
> as with:
> 
>   char buf[100];
>   size_t limit = 1000;
>   ... // series of operations that decrements limit to 0
>   stecpy(buf, src, &buf[MIN(sizeof buf, limit)]);
> 
> Both situations are rather common, IME.

Yeah. This is why I work as a programmer for a living while people like fir don't.

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


#141737

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-03-06 09:12 -0800
Message-ID<86sgw0sz9c.fsf@mailhub.linuxsc.com>
In reply to#141724
Howard Chu <highlandsun@gmail.com> writes:

> On Wednesday, March 6, 2019 at 11:58:29 AM UTC, Malcolm McLean wrote:
>
>> On Wednesday, 6 March 2019 11:45:24 UTC, Howard Chu  wrote:
>>
>>> I was recently reminded of an old rant of mine, which reminded me of
>>> the even older discussion about better versions of strcpy().
>>>
>>> https://www.openldap.org/lists/openldap-software/200303/msg00560.html
>>>
>>> The function I advocated there eventually became more common, known as
>>> stpcpy(), but it's still not a safer function.  It's easy to write a safer
>>> function that's still convenient to use though, like so:
>>>
>>> char *stecpy(char *dst, const char *src, const char *end)
>>> {
>>>   while (*src && dst < end)
>>>    *dst++ = *src++;
>>>   if (dst < end)
>>>    *dst = '\0';
>>>   return dst;
>>> }
>>
>> If the buffer is too small to fit the string, it is not nul-terminated.
>> So you'll likely get a crash when you try to use the string.  Better
>> to lose a character and nul-terminate.
>
> I suppose. [...]

I would say not.  If it's important to the caller to
that null termination be guaranteed, this is easily
done by putting a null byte at the end of the buffer
and giving an 'end' pointer one less.

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


#141729

Frommark.bluemel@gmail.com
Date2019-03-06 08:27 -0800
Message-ID<96a0f85d-9cc4-4cc0-8b1a-ef17d21f9981@googlegroups.com>
In reply to#141721
On Wednesday, 6 March 2019 11:45:24 UTC, Howard Chu  wrote:
> I was recently reminded of an old rant of mine, which reminded me of the even older discussion about better versions of strcpy().
> 
> https://www.openldap.org/lists/openldap-software/200303/msg00560.html
> 
> The function I advocated there eventually became more common, known as
> stpcpy(), but it's still not a safer function. It's easy to write a safer
> function that's still convenient to use though, like so:
> 
> char *stecpy(char *dst, const char *src, const char *end)
> {
>   while (*src && dst < end)
>    *dst++ = *src++;
>   if (dst < end)
>    *dst = '\0';
>   return dst;
> }
> 
> main() {
>   char buf[64];
>   char *ptr, *end = buf+sizeof(buf);
>   ptr = stecpy(buf, "hello", end);
>   ptr = stecpy(ptr, " world", end);
> }

Of course this version suffers from the traditional problem that it
simply truncates results with no indication that it's doing so.

It all depends on your use-case, I guess. I doubt that there is a single
"correct" solution for string copying in C.

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


#141738

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-03-06 09:22 -0800
Message-ID<86o96nudd5.fsf@mailhub.linuxsc.com>
In reply to#141721
Howard Chu <highlandsun@gmail.com> writes:

> I was recently reminded of an old rant of mine, which reminded me of
> the even older discussion about better versions of strcpy().
>
> https://www.openldap.org/lists/openldap-software/200303/msg00560.html
>
> The function I advocated there eventually became more common, known as
> stpcpy(), but it's still not a safer function.  It's easy to write a safer
> function that's still convenient to use though, like so:
>
> char *stecpy(char *dst, const char *src, const char *end)
> {
>   while (*src && dst < end)
>    *dst++ = *src++;
>   if (dst < end)
>    *dst = '\0';
>   return dst;
> }

An alternative writing:

    char *
    stecpy( char *const d, const char *const s, const char *const e ){
        return  d < e ?   (*d = *s) ? stecpy( d+1, s+1, e ) : d   : d;
    }

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


#141767

FromRosario19 <Ros@invalid.invalid>
Date2019-03-07 09:10 +0100
Message-ID<9vj18epn5rivqrkjcfgrnq79rl4mpn2hue@4ax.com>
In reply to#141721
On Wed, 6 Mar 2019 03:45:07 -0800 (PST), Howard Chu
<highlandsun@gmail.com> wrote:

>char *stecpy(char *dst, const char *src, const char *end)
>{
>  while (*src && dst < end)
>   *dst++ = *src++;
>  if (dst < end)
>   *dst = '\0';
>  return dst;
>}

int *stxcpy(char *dst, const char *src, const char *end)
{char *ini=dst;
 if(dst==0||src==0||end==0) 
          retunr -1;
  while(*src&& dst<end) *dst++=*src++;
  if(dst<end) *dst='\0';
  else {if(dst!=ini) *(dst-1)='\0'else return -1;}
  return 0;
}

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


#141768

FromRosario19 <Ros@invalid.invalid>
Date2019-03-07 09:12 +0100
Message-ID<jik18e1hdf65g1c0kire71ka49cvcq48r9@4ax.com>
In reply to#141767
On Thu, 07 Mar 2019 09:10:01 +0100, Rosario19 <Ros@invalid.invalid>
wrote:

>int *stxcpy(char *dst, const char *src, const char *end)

int stxcpy(char *dst, const char *src, const char *end)

>{char *ini=dst;
> if(dst==0||src==0||end==0) 
>          retunr -1;
>  while(*src&& dst<end) *dst++=*src++;
>  if(dst<end) *dst='\0';
>  else {if(dst!=ini) *(dst-1)='\0'else return -1;}
>  return 0;
>}

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


#141770

Frommark.bluemel@gmail.com
Date2019-03-07 00:55 -0800
Message-ID<a68fdb8e-6ee2-4701-8a57-566e54d23c32@googlegroups.com>
In reply to#141768
On Thursday, 7 March 2019 08:11:05 UTC, Rosario19  wrote:
> On Thu, 07 Mar 2019 09:10:01 +0100, Rosario19 <Ros@invalid.invalid>
> wrote:
> 
> >int *stxcpy(char *dst, const char *src, const char *end)
> 
> int stxcpy(char *dst, const char *src, const char *end)
> 
> >{char *ini=dst;
> > if(dst==0||src==0||end==0) 
> >          retunr -1;
> >  while(*src&& dst<end) *dst++=*src++;
> >  if(dst<end) *dst='\0';
> >  else {if(dst!=ini) *(dst-1)='\0'else return -1;}
> >  return 0;
> >}

But you still didn't correct the mispelt "retunr"...

How hard is it to write something that actually compiles and then
cut-and-paste it into the post?
(and your code formatting is, IMHO, foul).

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


#141802

FromWilliam Ahern <william@25thandClement.com>
Date2019-03-07 16:09 -0800
Message-ID<reo8lf-8vd.ln1@wilbur.25thandClement.com>
In reply to#141721
Howard Chu <highlandsun@gmail.com> wrote:
> I was recently reminded of an old rant of mine, which reminded me of the even older discussion about better versions of strcpy().
> 
> https://www.openldap.org/lists/openldap-software/200303/msg00560.html
> 
> The function I advocated there eventually became more common, known as
> stpcpy(), but it's still not a safer function. It's easy to write a safer
> function that's still convenient to use though, like so:
> 
> char *stecpy(char *dst, const char *src, const char *end)
> {
>   while (*src && dst < end)
>    *dst++ = *src++;
>   if (dst < end)
>    *dst = '\0';
>   return dst;
> }
> 
> main() {
>   char buf[64];
>   char *ptr, *end = buf+sizeof(buf);
>   ptr = stecpy(buf, "hello", end);
>   ptr = stecpy(ptr, " world", end);
> }
> 
> strlcpy() and strcpy_s() both suffer from being inconvenient to use
> successively when constructing a longer string out of component parts: you
> have to continually recalculate the remaining buffer space after each
> call.

strlcpy is invariably implemented as a pair with strlcat. strlcat makes it
trivial to safely concatenate. Yes, it's suboptimal in that it must
recalculate the length of dst on each invocation, but the important thing is
that the interface does this for--you're not writing any additional code.
When strlcat's Big-O characteristics are problematic, there are typically
other considerations that make any single generic C string routine
ill-suited for a particular task.

There isn't a better interface than strlcpy/strlcat. At best there are only
equivalently useful interfaces, or rather equivalently devoted proponents.
There are only better interfaces--plural--according to the context.

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


#141807

FromHoward Chu <highlandsun@gmail.com>
Date2019-03-08 04:04 -0800
Message-ID<9669b272-15fb-4ff6-91dc-bb3ea2b1254d@googlegroups.com>
In reply to#141802
On Friday, March 8, 2019 at 12:15:14 AM UTC, William Ahern wrote:
> Howard Chu <highlandsun@gmail.com> wrote:
> > I was recently reminded of an old rant of mine, which reminded me of the even older discussion about better versions of strcpy().
> > 
> > https://www.openldap.org/lists/openldap-software/200303/msg00560.html
> > 
> > The function I advocated there eventually became more common, known as
> > stpcpy(), but it's still not a safer function. It's easy to write a safer
> > function that's still convenient to use though, like so:
> > 
> > char *stecpy(char *dst, const char *src, const char *end)
> > {
> >   while (*src && dst < end)
> >    *dst++ = *src++;
> >   if (dst < end)
> >    *dst = '\0';
> >   return dst;
> > }
> > 
> > main() {
> >   char buf[64];
> >   char *ptr, *end = buf+sizeof(buf);
> >   ptr = stecpy(buf, "hello", end);
> >   ptr = stecpy(ptr, " world", end);
> > }
> > 
> > strlcpy() and strcpy_s() both suffer from being inconvenient to use
> > successively when constructing a longer string out of component parts: you
> > have to continually recalculate the remaining buffer space after each
> > call.
> 
> strlcpy is invariably implemented as a pair with strlcat. strlcat makes it
> trivial to safely concatenate. Yes, it's suboptimal in that it must
> recalculate the length of dst on each invocation, but the important thing is
> that the interface does this for--you're not writing any additional code.
> When strlcat's Big-O characteristics are problematic, there are typically
> other considerations that make any single generic C string routine
> ill-suited for a particular task.
> 
> There isn't a better interface than strlcpy/strlcat. At best there are only
> equivalently useful interfaces, or rather equivalently devoted proponents.
> There are only better interfaces--plural--according to the context.

Your posts have been well reasoned and on-target and for that I thank you.
But on this we'll disagree. If two interfaces and implementations are
equally correct and equally safe, the one that executes more slowly is
still inferior.

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


#143083

Fromeric.sanchis@iut-rodez.fr
Date2019-04-15 04:40 -0700
Message-ID<ad1523ff-28ba-45bd-9b5c-f15f8d75802d@googlegroups.com>
In reply to#141802
Le vendredi 8 mars 2019 01:15:14 UTC+1, William Ahern a écrit :
> Howard Chu <highlandsun@gmail.com> wrote:
> > I was recently reminded of an old rant of mine, which reminded me of the even older discussion about better versions of strcpy().
> > 
> > https://www.openldap.org/lists/openldap-software/200303/msg00560.html
> > 
> > The function I advocated there eventually became more common, known as
> > stpcpy(), but it's still not a safer function. It's easy to write a safer
> > function that's still convenient to use though, like so:
> > 
> > char *stecpy(char *dst, const char *src, const char *end)
> > {
> >   while (*src && dst < end)
> >    *dst++ = *src++;
> >   if (dst < end)
> >    *dst = '\0';
> >   return dst;
> > }
> > 
> > main() {
> >   char buf[64];
> >   char *ptr, *end = buf+sizeof(buf);
> >   ptr = stecpy(buf, "hello", end);
> >   ptr = stecpy(ptr, " world", end);
> > }
> > 
> > strlcpy() and strcpy_s() both suffer from being inconvenient to use
> > successively when constructing a longer string out of component parts: you
> > have to continually recalculate the remaining buffer space after each
> > call.
> 
> strlcpy is invariably implemented as a pair with strlcat. strlcat makes it
> trivial to safely concatenate. Yes, it's suboptimal in that it must
> recalculate the length of dst on each invocation, but the important thing is
> that the interface does this for--you're not writing any additional code.
> When strlcat's Big-O characteristics are problematic, there are typically
> other considerations that make any single generic C string routine
> ill-suited for a particular task.
> 
> There isn't a better interface than strlcpy/strlcat. At best there are only
> equivalently useful interfaces, or rather equivalently devoted proponents.
> There are only better interfaces--plural--according to the context.

Having had to deal with C strings I disagree with: "There isn't a better interface than strlcpy/strlcat."

size_t  strlcpy( char *dst, const char *src, size_t  dstsize);
size_t  strlcat(  char *dst, const char *src, size_t  dstsize);

These functions offer a weak robustness: 
          + if dst or src is a NULL pointer, strlcpy and strlcat crash.
          + if strlcat scans dstsize characters without finding a null character, the destination string will not be null terminated (bad-formed string).

These functions make truncation prevention complicated. 

They are not transparent (transparency means after an error input data are not modified).

I would suggest using strntcpy/strntcat functions:

int strntcpy(char *dst, size_t dstsize, const char *src);
int strntcat(char *dst, size_t dstsize, const char *src);

They are free (LGPL).

They are robust (functions perform correctly under normal and unexpected conditions). 
          + if dst or src is a NULL pointer, they do not crash and an error is returned.
          + if strntcat scans dstsize characters without finding a null character, an error is returned without producing a bad-formed string.

They never produce bad-formed string or buffer overflow. 

They are transparent.

Programmers
  + do not have to manage the terminating null byte.
  + can allow truncation (strtcpy/strtcat) or not allow truncation (strntcpy/strntcat). It is easy to detect or prevent truncations.

Example:

#include "strtnt.h"

#define	DSTSIZE	7
char dst[DSTSIZE] = "1234";

char * src = "hello world";
int ret;
. . .	
if ( (ret = strntcpy(dst,sizeof(dst),src)) < 0 )
{    /*  truncation prevention (error ETRUNC)
           dst="1234" (transparency)   */  }
. . .

These functions are freely downloadable here :
http://aral.iut-rodez.fr/en/sanchis/miscellaneous/strtnt/strtnt.tar

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


#143097

Fromnobody@example.org (Scott)
Date2019-04-16 03:33 +0000
Message-ID<5cb54ab1.2459943736@core>
In reply to#143083
On Mon, 15 Apr 2019 04:40:03 -0700 (PDT), eric.sanchis@iut-rodez.fr
wrote:

>Having had to deal with C strings I disagree with: "There isn't a better in=
>terface than strlcpy/strlcat."
>
>size_t  strlcpy( char *dst, const char *src, size_t  dstsize);
>size_t  strlcat(  char *dst, const char *src, size_t  dstsize);
>
>These functions offer a weak robustness:=20
>          + if dst or src is a NULL pointer, strlcpy and strlcat crash.

No reason they have to crash, of course. The ones I wrote don't. It's
not mandatory.

>          + if strlcat scans dstsize characters without finding a null char=
>acter, the destination string will not be null terminated (bad-formed strin=
>g).
>          + if strntcat scans dstsize characters without finding a null cha=
>racter, an error is returned without producing a bad-formed string.

So...given the same unterminated dst, both functions leave it
untouched, and in strlcat this is bad, and in strntcat this is good.
Have I got that right?

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


#143109

Fromeric.sanchis@iut-rodez.fr
Date2019-04-16 05:35 -0700
Message-ID<38440a99-738b-4436-9c53-6b96716cdb87@googlegroups.com>
In reply to#143097
Le mardi 16 avril 2019 05:32:40 UTC+2, Scott a écrit :
> On Mon, 15 Apr 2019 04:40:03 -0700 (PDT), eric.sanchis@iut-rodez.fr
> wrote:
> 
> >Having had to deal with C strings I disagree with: "There isn't a better in=
> >terface than strlcpy/strlcat."
> >
> >size_t  strlcpy( char *dst, const char *src, size_t  dstsize);
> >size_t  strlcat(  char *dst, const char *src, size_t  dstsize);
> >
> >These functions offer a weak robustness:=20
> >          + if dst or src is a NULL pointer, strlcpy and strlcat crash.
> 
> No reason they have to crash, of course. The ones I wrote don't. It's
> not mandatory.
> 
> >          + if strlcat scans dstsize characters without finding a null char=
> >acter, the destination string will not be null terminated (bad-formed strin=
> >g).
> >          + if strntcat scans dstsize characters without finding a null cha=
> >racter, an error is returned without producing a bad-formed string.
> 
> So...given the same unterminated dst, both functions leave it
> untouched, and in strlcat this is bad, and in strntcat this is good.
> Have I got that right?


>>>These functions offer a weak robustness:=20
>>>          + if dst or src is a NULL pointer, strlcpy and strlcat crash.

>No reason they have to crash, of course. The ones I wrote don't. It's
not mandatory.

Under Ubuntu 18, they crash.

Example :
/*      @(#)    dstNULL_lcpy.c  */

#include        <bsd/string.h>
#include        <stdio.h>

#define DSTSIZE 20

int main(void)
{
   char * dst = NULL; /* for example, after a problem with malloc */

strlcpy(dst,"hello",DSTSIZE) ;          

printf("%s\n", dst) ;

return 0 ;
}

$ gcc -o dstNULL_lcpy  dstNULL_lcpy.c  -lbsd
$ 
$ dstNULL_lcpy 
Erreur de segmentation (core dumped)
$


>          + if strlcat scans dstsize characters without finding a null char=
>acter, the destination string will not be null terminated (bad-formed strin=
>g).
>          + if strntcat scans dstsize characters without finding a null cha=
>racter, an error is returned without producing a bad-formed string.

So...given the same unterminated dst, both functions leave it
untouched, and in strlcat this is bad, and in strntcat this is good.
Have I got that right?

Right.

With strlcat:
/*      @(#)    dstBadformed_lcat.c     */

#include        <bsd/string.h>
#include        <stdio.h>

#define DSTSIZE 4

int main(void)
{
   char dst[DSTSIZE] ={'1','2','3','4'} ;  /* bad formed string */
   char * p = dst + DSTSIZE ;   /* dummy */

*p = 'Z' ;

strlcat(dst,"hello",sizeof(dst)) ;  /* truncation is supposed allowed */

/* here, dst is supposed to be a well formed string */

printf("dst='%s'\n", dst) ;  

return 0 ;
}


$ gcc -o dstBadFormed_lcat -fno-stack-protector  dstBadFormed_lcat.c -lbsd
$ dstBadFormed_lcat 
dst='1234Zz
           ��'
$


With strtcat (truncation is supposed allowed):

/*      @(#)    dstBadformed_tcat.c     */

#include        <stdio.h>

#include        "strtnt.h"

#define DSTSIZE 4

int main(void)
{
   int ret ;
   char dst[DSTSIZE] ={'1','2','3','4'} ;  /* bad formed string */
   char * p = dst + DSTSIZE ;   /* dummy */

*p = 'Z' ;

if ( (ret=strtcat(dst,sizeof(dst),"hello")) < 0 )
{ 
  fprintf(stderr,"Error (ret=%d)\n", ret) ;  /* EDSTPAR */
  /* process error ... */
}

return 0 ;
}

$ gcc -o dstBadFormed_tcat dstBadFormed_tcat.c strtnt.o -fno-stack-protector
$ dstBadFormed_tcat 
Error (ret=-1)
$

With strlcat, the program uses a wrong string.
With strtcat, an error is detected (and can be processed).

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


#143157

Fromnobody@example.org (Scott)
Date2019-04-17 01:19 +0000
Message-ID<5cb67e2b.2538657329@core>
In reply to#143109
On Tue, 16 Apr 2019 05:35:07 -0700 (PDT), eric.sanchis@iut-rodez.fr
wrote:

>strlcat(dst,"hello",sizeof(dst)) ;  /* truncation is supposed allowed */
>if ( (ret=3Dstrtcat(dst,sizeof(dst),"hello")) < 0 )

Well, your approach is flawed, for one. I'll assume that you didn't
read the strlcat man page far enough to get to the part where strlcat
returns a value that can be used to detect potential overflows.

if (strlcat(dst,"hello",sizeof(dst)) >= sizeof(dst))
  /* problem*/ ;

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


#143169

Fromeric.sanchis@iut-rodez.fr
Date2019-04-17 05:35 -0700
Message-ID<797ff415-70ed-4321-a2ee-d707dd14245a@googlegroups.com>
In reply to#143157
Le mercredi 17 avril 2019 03:18:33 UTC+2, Scott a écrit :
> On Tue, 16 Apr 2019 05:35:07 -0700 (PDT), eric.sanchis@iut-rodez.fr
> wrote:
> 
> >strlcat(dst,"hello",sizeof(dst)) ;  /* truncation is supposed allowed */
> >if ( (ret=3Dstrtcat(dst,sizeof(dst),"hello")) < 0 )
> 
> Well, your approach is flawed, for one. I'll assume that you didn't
> read the strlcat man page far enough to get to the part where strlcat
> returns a value that can be used to detect potential overflows.
> 
> if (strlcat(dst,"hello",sizeof(dst)) >= sizeof(dst))
>   /* problem*/ ;


The strlcat manpage says:

"Also note that strlcpy() and strlcat() only operate on true “C” strings. This means that for strlcpy() src must be NUL-terminated and for strlcat() both src and dst must be NUL-terminated. "

So, you must not use: 
      if (strlcat(dst,"hello",sizeof(dst)) >= sizeof(dst))
           /* problem*/ ;
when dst is not NUL-terminated.

But you can use strtcat/strntcat.

I think I provided decisive evidence showing that strntcpy/strntcat functions are more robust than strlcpy/strlcat.

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


#143181

Fromnobody@example.org (Scott)
Date2019-04-17 16:59 +0000
Message-ID<5cb75ad9.2595151548@core>
In reply to#143169
On Wed, 17 Apr 2019 05:35:59 -0700 (PDT), eric.sanchis@iut-rodez.fr
wrote:

>Le mercredi 17 avril 2019 03:18:33 UTC+2, Scott a =C3=A9crit=C2=A0:
>> if (strlcat(dst,"hello",sizeof(dst)) >=3D sizeof(dst))
>>   /* problem*/ ;
>
>
>The strlcat manpage says:
>
>"Also note that strlcpy() and strlcat() only operate on true =E2=80=9CC=E2=
>=80=9D strings. This means that for strlcpy() src must be NUL-terminated an=
>d for strlcat() both src and dst must be NUL-terminated. "

"Note however, that if strlcat() traverses size characters without
finding a NUL, the length of the string is considered to be size and
the destination string will not be NUL-terminated..."

>I think I provided decisive evidence showing that strntcpy/strntcat functio=
>ns are more robust than strlcpy/strlcat.

"No manual entry for strntcat"

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


#143201

Fromeric.sanchis@iut-rodez.fr
Date2019-04-17 23:40 -0700
Message-ID<02c6685e-e185-4906-8fdd-115513a1ba65@googlegroups.com>
In reply to#143181
Le mercredi 17 avril 2019 18:59:06 UTC+2, Scott a écrit :
> On Wed, 17 Apr 2019 05:35:59 -0700 (PDT), eric.sanchis@iut-rodez.fr
> wrote:
> 
> >Le mercredi 17 avril 2019 03:18:33 UTC+2, Scott a =C3=A9crit=C2=A0:
> >> if (strlcat(dst,"hello",sizeof(dst)) >=3D sizeof(dst))
> >>   /* problem*/ ;
> >
> >
> >The strlcat manpage says:
> >
> >"Also note that strlcpy() and strlcat() only operate on true =E2=80=9CC=E2=
> >=80=9D strings. This means that for strlcpy() src must be NUL-terminated an=
> >d for strlcat() both src and dst must be NUL-terminated. "
> 
> "Note however, that if strlcat() traverses size characters without
> finding a NUL, the length of the string is considered to be size and
> the destination string will not be NUL-terminated..."
> 
> >I think I provided decisive evidence showing that strntcpy/strntcat functio=
> >ns are more robust than strlcpy/strlcat.
> 
> "No manual entry for strntcat"

You will find source code and man pages of strntcpy/strntcat functions in the tarball:
http://aral.iut-rodez.fr/en/sanchis/miscellaneous/strtnt/strtnt.tar

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


Page 2 of 3 — ← Prev page 1 [2] 3  Next page →

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


csiph-web