Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #141721 > unrolled thread
| Started by | Howard Chu <highlandsun@gmail.com> |
|---|---|
| First post | 2019-03-06 03:45 -0800 |
| Last post | 2019-03-20 11:13 +0100 |
| Articles | 20 on this page of 42 — 13 participants |
Back to article view | Back to comp.lang.c
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 →
| From | William Ahern <william@25thandClement.com> |
|---|---|
| Date | 2019-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2019-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]
| From | William Ahern <william@25thandClement.com> |
|---|---|
| Date | 2019-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]
| From | Howard Chu <highlandsun@gmail.com> |
|---|---|
| Date | 2019-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]
| From | "I Set My Girlfriend's Pantyhose on Fire" <cdalten@gmail.com> |
|---|---|
| Date | 2019-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]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2019-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]
| From | mark.bluemel@gmail.com |
|---|---|
| Date | 2019-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]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2019-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]
| From | Rosario19 <Ros@invalid.invalid> |
|---|---|
| Date | 2019-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]
| From | Rosario19 <Ros@invalid.invalid> |
|---|---|
| Date | 2019-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]
| From | mark.bluemel@gmail.com |
|---|---|
| Date | 2019-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]
| From | William Ahern <william@25thandClement.com> |
|---|---|
| Date | 2019-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]
| From | Howard Chu <highlandsun@gmail.com> |
|---|---|
| Date | 2019-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]
| From | eric.sanchis@iut-rodez.fr |
|---|---|
| Date | 2019-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]
| From | nobody@example.org (Scott) |
|---|---|
| Date | 2019-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]
| From | eric.sanchis@iut-rodez.fr |
|---|---|
| Date | 2019-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]
| From | nobody@example.org (Scott) |
|---|---|
| Date | 2019-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]
| From | eric.sanchis@iut-rodez.fr |
|---|---|
| Date | 2019-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]
| From | nobody@example.org (Scott) |
|---|---|
| Date | 2019-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]
| From | eric.sanchis@iut-rodez.fr |
|---|---|
| Date | 2019-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