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


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

strncpy and 'n'

Started bynroberts <roberts.noah@gmail.com>
First post2012-02-16 09:05 -0800
Last post2012-02-17 00:50 -0800
Articles 20 on this page of 47 — 20 participants

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


Contents

  strncpy and 'n' nroberts <roberts.noah@gmail.com> - 2012-02-16 09:05 -0800
    Re: strncpy and 'n' Anders Wegge Keller <wegge@wegge.dk> - 2012-02-16 18:57 +0100
      Re: strncpy and 'n' "A. K." <akk@nospam.org> - 2012-02-16 19:27 +0100
        Re: strncpy and 'n' Anders Wegge Keller <wegge@wegge.dk> - 2012-02-16 19:34 +0100
          Re: strncpy and 'n' "A. K." <akk@nospam.org> - 2012-02-16 20:58 +0100
    Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-16 10:01 -0800
      Re: strncpy and 'n' Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-16 11:10 -0800
        Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-16 14:20 -0800
          Re: strncpy and 'n' Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-17 04:19 -0800
            Re: strncpy and 'n' James Kuyper <jameskuyper@verizon.net> - 2012-02-17 10:04 -0500
              Re: strncpy and 'n' Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-17 10:27 -0800
              Re: strncpy and 'n' Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-17 11:22 -0800
                Re: strncpy and 'n' James Kuyper <jameskuyper@verizon.net> - 2012-02-17 14:40 -0500
                  Re: strncpy and 'n' Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-18 02:17 -0800
      Re: strncpy and 'n' jgk@panix.com (Joe keane) - 2012-02-19 20:04 +0000
        Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-19 12:33 -0800
          Re: strncpy and 'n' jgk@panix.com (Joe keane) - 2012-02-20 21:25 +0000
            Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-20 14:46 -0800
              Re: strncpy and 'n' Jorgen Grahn <grahn+nntp@snipabacken.se> - 2012-02-23 23:13 +0000
                Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-23 16:20 -0800
                Re: strncpy and 'n' Philip Lantz <prl@canterey.us> - 2012-02-25 00:26 -0800
                Re: strncpy and 'n' pacman@kosh.dhis.org (Alan Curry) - 2012-02-26 01:34 +0000
                  Re: strncpy and 'n' Jorgen Grahn <grahn+nntp@snipabacken.se> - 2012-02-28 22:46 +0000
                    Re: strncpy and 'n' jgk@panix.com (Joe keane) - 2012-03-01 00:03 +0000
                      Re: strncpy and 'n' Jorgen Grahn <grahn+nntp@snipabacken.se> - 2012-03-01 12:03 +0000
            Re: strncpy and 'n' James Kuyper <jameskuyper@verizon.net> - 2012-02-20 17:51 -0500
          Re: strncpy and 'n' Shao Miller <sha0.miller@gmail.com> - 2012-02-20 18:14 -0500
            Re: strncpy and 'n' pete <pfiland@mindspring.com> - 2012-02-20 23:16 -0500
              Re: strncpy and 'n' Shao Miller <sha0.miller@gmail.com> - 2012-02-25 01:51 -0500
                Re: strncpy and 'n' pete <pfiland@mindspring.com> - 2012-02-25 10:34 -0500
          Re: strncpy and 'n' Rich Webb <bbew.ar@mapson.nozirev.ten> - 2012-02-20 19:09 -0500
            Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-20 16:46 -0800
              Re: strncpy and 'n' Rich Webb <bbew.ar@mapson.nozirev.ten> - 2012-02-20 20:48 -0500
            Re: strncpy and 'n' Shao Miller <sha0.miller@gmail.com> - 2012-02-20 19:46 -0500
          Re: strncpy and 'n' jgk@panix.com (Joe keane) - 2012-02-21 21:46 +0000
            Re: strncpy and 'n' James Kuyper <jameskuyper@verizon.net> - 2012-02-21 17:15 -0500
            Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-21 14:29 -0800
              Re: strncpy and 'n' Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-02-22 01:15 -0800
              Re: strncpy and 'n' jgk@panix.com (Joe keane) - 2012-02-23 00:02 +0000
                Re: strncpy and 'n' Keith Thompson <kst-u@mib.org> - 2012-02-22 17:02 -0800
    Re: strncpy and 'n' Markus Schaub <usenet@markus-schaub.de> - 2012-02-16 18:53 +0000
    Re: strncpy and 'n' lawrence.jones@siemens.com - 2012-02-16 13:25 -0500
    Re: strncpy and 'n' blp@cs.stanford.edu (Ben Pfaff) - 2012-02-16 11:18 -0800
    Re: strncpy and 'n' Stephen Sprunk <stephen@sprunk.org> - 2012-02-16 13:29 -0600
      Re: strncpy and 'n' Ike Naar <ike@iceland.freeshell.org> - 2012-02-16 22:45 +0000
    Re: strncpy and 'n' ImpalerCore <jadill33@gmail.com> - 2012-02-16 11:32 -0800
    Re: strncpy and 'n' Nick Keighley <nick_keighley_nospam@hotmail.com> - 2012-02-17 00:50 -0800

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


#17915

FromPhilip Lantz <prl@canterey.us>
Date2012-02-25 00:26 -0800
Message-ID<MPG.29b0e5e2f30a738098968d@news.eternal-september.org>
In reply to#17858
Jorgen Grahn wrote:
> On Mon, 2012-02-20, Keith Thompson wrote:
> > The point is that strncpy is a very different function from strcpy.
> > It is not intended to work with a *string* in the target array;
> > it works with a specialized data structure (used to store file
> > names in very early Unix systems).
> 
> Malcolm McLean wrote something similar upthread. Do you have any
> references for this?
> 
> It would explain the function's weird semantics, but I haven't seen
> anything before which says this is its background.  (There's also
> wcsncpy() for wchar_t -- that one is certainly newer, and useless in
> the data structures you mention.)

I've never seen a reference for that either, but I guessed that this was 
its purpose when I first learned the sematics of the function, oh, about 
30 years ago, and I've assumed that ever since. Fixed-length Unix file 
names are the only place I know of where a string buffer had to be 
padded out to its full length with nulls.

> > I think you're understating the differences between strcpy and
> > strncpy.  The strncpy function is radically different from strcpy,
> > and there are very few legitimate uses for it.  On the other hand,
> > the deceptive name has led many C programmers to use it incorrectly,
> > and I strongly suspect that it's  used incorrectly far more often
> > than it's used correctly.
> 
> The static analysis tool we use at work screams bloody murder every
> time I use strcpy() and tells me to use strncpy() instead. Argh ...

Aargh! Well, presumably a non-broken fix will shut it up just as well.

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


#17959

Frompacman@kosh.dhis.org (Alan Curry)
Date2012-02-26 01:34 +0000
Message-ID<jic282$t4p$1@speranza.aioe.org>
In reply to#17858
In article <slrnjkdi0u.1ls.grahn+nntp@frailea.sa.invalid>,
Jorgen Grahn  <grahn+nntp@snipabacken.se> wrote:
>On Mon, 2012-02-20, Keith Thompson wrote:
>>
>> The point is that strncpy is a very different function from strcpy.
>> It is not intended to work with a *string* in the target array;
>> it works with a specialized data structure (used to store file
>> names in very early Unix systems).
>
>Malcolm McLean wrote something similar upthread. Do you have any
>references for this?
>

Ancient Unix source code is available. And it's not too big to grep. Let's go
mythbusting.

First observation: strncpy is present only in V7, not any earlier versions.

The item most commonly accused of being the original reason for strncpy is
the directory entry, which is defined like this:

#ifndef	DIRSIZ
#define	DIRSIZ	14
#endif
struct	direct
{
	ino_t	d_ino;
	char	d_name[DIRSIZ];
};

That definition appears in 2 different header files, one which is used in the
kernel and one which is used in userspace. The headers are identical. In
fact, the above 8 lines are the entire contents.

This struct represents the format of a directory entry. There is no
distinction between the external format on permanent storage and the internal
format used by the system. There was no need for such a distinction because
there was only one supported filesystem type.

If the strncpy hypothesis is true, the original use of strncpy should have
been to copy from a \0-terminated string into a d_name field. There are 10
uses of strncpy in V7. (11 if you also include lpr from the "Addenda" tape.
But let's not, because it came later.)

Results of grepping for strncpy, after removing instances that were not
actually calls to strncpy:

usr/src/cmd/atrun.c:		strncpy(file, dirent.d_name, DIRSIZ);
usr/src/cmd/crypt.c:	strncpy(buf, pw, 8);
usr/src/cmd/ed.c:	strncpy(buf, keyp, 8);
usr/src/cmd/expr.y:			strncpy(Mstring[0], p, num);
usr/src/cmd/login.c:#define SCPYN(a, b)	strncpy(a, b, sizeof(a))
usr/src/cmd/login.c:	SCPYN(utmp.ut_name, "");
usr/src/cmd/login.c:		SCPYN(utmp.ut_name, argv[1]);
usr/src/cmd/login.c:		SCPYN(utmp.ut_line, index(ttyn+1, '/')+1);
usr/src/cmd/mkdir.c:		strncpy(pname, d, slash);
usr/src/cmd/ranlib.c:		strncpy(firstname, arp.ar_name, 14);
usr/src/cmd/xsend/lib.c:	strncpy(buf, s, 10);

Some of the calls were through the SCPYN macro so I also included that as a
grep target.

First notice that all the matches are in usr/src/cmd, not in usr/sys where
the kernel source is. I expected the primeval strncpy to be in the kernel,
perhaps in the creat call where a \0-terminated string from userspace must be
used to populate a new directory entry. Nope.

Well, at least the first match (atrun.c) is working on a d_name. Yay! A
confirmation! Wait a minute, what's the argument order for strncpy again?
Destination first. Crap! A non-confirmation. With additional context, this
looks like exactly the kind of sloppy usage of strncpy that we now try to
avoid. The destination, "file", is declared like this:

	char file[DIRSIZ+1];

And after the strncpy we find the usual fixup:

		strncpy(file, dirent.d_name, DIRSIZ);
		file[DIRSIZ] = '\0';

And then "file" is used as a \0-terminated string. It didn't need the extra
padding. strncpy is doing something useful here through, protecting against
an unterminated source buffer. That's probably as close as we're going to get
to confirming the strncpy hypothesis, since none of the rest of the uses
involve d_name.

In crypt.c we have
	char buf[13];
and
	strncpy(buf, pw, 8);
In this case, the source string is a user-supplied password, which is
\0-terminated, so strncpy is not protecting against an unterminated source.
It is, however, truncating the source if it is longer than 8 characters. And
if the password is less than 8 bytes long, the padding will actually be
relevant. The buffer is required to contain an 8-byte "key". This use of
strncpy actually needs all of its features.

In ed.c there is basically a copy of the same function from crypt.c, for
editing encrypted files.

In expr.y we have what looks like another sloppy strncpy. It has the fixup:

			strncpy(Mstring[0], p, num);
			Mstring[0][num] = '\0';

But the length "num" is not related to the size of the destination buffer.
It's the length of the portion of the source buffer that matched the \(...\)
subexpression of a regexp, which will be saved into Mstring[0]. Mstring is
declared like this:

char Mstring[1][128];

so there's a potential buffer overflow here if you use \(...\) to match a
string longer than 128 bytes. Maybe it's impossible to pass a string that
long to the program; I don't know. Moving on...

login.c looks good. The SCPYN macro ties the strncpy length limit to the
destination buffer size, and the destinations are ut_name and ut_line, both
of which are fixed-size buffers that need to be padded. This might actually
be a better case than the atrun.c usage. It fits everything we expected to
find except that it's utmp, not a directory.

mkdir.c is next, and it's an exciting candidate, isn't it? Especially if you
remember that mkdir wasn't a syscall yet, so the userspace mkdir program was
actually setuid root and worked at a low level. Not quite low enough to
operate on a struct direct though. What's happening here

		strncpy(pname, d, slash);

is, like the expr.y usage, copying a substring of the source string, which is
taken directly from main's argv, so it's \0-terminated. strncpy is not
protecting against an unterminated source, and it's not truncating a long
source, so its only possibly useful feature is padding. Nope. After the
strncpy, pname is treated as a \0-terminated string. The fixup is hidden in a
strcat this time, but it's still there:

	if(slash)
		strncpy(pname, d, slash);
	strcpy(pname+slash, ".");

Again there's a potential buffer overflow if the source string is longer than
128 bytes.

ranlib.c looks strange. I don't know exactly what it's doing, since I don't
know anything about the ar format. But the strncpy destination is

char	firstname[17];

and the strncpy is

		strncpy(firstname, arp.ar_name, 14);

seem a bit weird. Later, firstname is used as a \0-terminated string. So if
the source string arp.ar_name was shorter than 14 bytes, the first padding
byte added by strncpy will be the terminator and the rest will be
unnecessary. If the source string was 14 bytes, the terminator will be the \0
found at firstname[14]... not courtesy of strncpy or any post-strncpy fixup,
but just because it's in the bss and it never gets modified.

xsend/lib.c looks like another copy of the encryption key setup code found in
crypt.c and ed.c using a slightly different key generation method that uses
up to 10 characters of the user-supplied password.

I've looked in the V6 source for the code corresponding to each strncpy call
in V7. Most of them (at, expr, ranlib, xsend, and the encryption ability of
ed) don't exist in V6. mkdir was pure assembly in V6. The crypt program looks
like a total rewrite. login is the only one that had a direct equivalent.

struct utmp in V6 was different, using only a single char to identify the
tty, but ut_name was there (called simply "name") and it's appears plausible
that strncpy and SCPYN were added specifically to simplify the existing code,
which copied and padded the array with manual loops.

And one last thing... if strncpy wasn't used to populate the d_name field,
how was it done? Well, the kernel creates directory entries in response to
user requests like creat and mknod and link. All of those eventually end up
calling wdir() in usr/sys/sys/iget.c which does this:

	bcopy((caddr_t)u.u_dbuf, (caddr_t)u.u_dent.d_name, DIRSIZ);

So a d_name is created as an exact copy of a u_dbuf, which is declared like
this:

	char	u_dbuf[DIRSIZ];		/* current pathname component */

and must already be properly padded. That was done before the wdir() call by
namei() in usr/sys/sys/nami.c and here's the answer:

(at this point, c is either the first character of a pathname or a non-slash
character that was found after a slash)

	cp = &u.u_dbuf[0];
	while (c != '/' && c != '\0' && u.u_error == 0 ) {
		if (mpxip!=NULL && c=='!')
			break;
		if(cp < &u.u_dbuf[DIRSIZ])
			*cp++ = c;
		c = (*func)();
	}
	while(cp < &u.u_dbuf[DIRSIZ])
		*cp++ = '\0';

Characters are read one at a time from the source string by calling a
callback function (*func). It does that because the source string may or may
not be in userspace, and userspace strings can't be directly addressed.
Afterward, the padding is done with a loop.

-- 
Alan Curry

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


#18048

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2012-02-28 22:46 +0000
Message-ID<slrnjkqma0.1ls.grahn+nntp@frailea.sa.invalid>
In reply to#17959
On Sun, 2012-02-26, Alan Curry wrote:
> In article <slrnjkdi0u.1ls.grahn+nntp@frailea.sa.invalid>,
> Jorgen Grahn  <grahn+nntp@snipabacken.se> wrote:
>>On Mon, 2012-02-20, Keith Thompson wrote:
>>>
>>> The point is that strncpy is a very different function from strcpy.
>>> It is not intended to work with a *string* in the target array;
>>> it works with a specialized data structure (used to store file
>>> names in very early Unix systems).
>>
>>Malcolm McLean wrote something similar upthread. Do you have any
>>references for this?
>>
>
> Ancient Unix source code is available. And it's not too big to grep. Let's go
> mythbusting.
>
> First observation: strncpy is present only in V7, not any earlier versions.
[huge snip]

Interesting, and at the same time I'm ashamed to admit that I haven't
read it in detail yet.

That it came from V7 was news to me. In a way, that's almost enough
information. V7 means the late 1970s; programmers were fewer and on
average smarter, and the conventions weren't the same as today.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

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


#18073

Fromjgk@panix.com (Joe keane)
Date2012-03-01 00:03 +0000
Message-ID<jimeco$poo$1@reader1.panix.com>
In reply to#18048
In article <slrnjkqma0.1ls.grahn+nntp@frailea.sa.invalid>,
Jorgen Grahn  <grahn+nntp@snipabacken.se> wrote:
>That it came from V7 was news to me. In a way, that's almost enough
>information. V7 means the late 1970s; programmers were fewer and on
>average smarter, and the conventions weren't the same as today.

Fixed-width members [or variable with fixed maximum] are old as dirt.
Think punched cards and 'tabulating machines'.

Used as a key in a B-tree or hash table, you really do want to clear out
the 'junk' (no need to go looking for the terminator).

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


#18084

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2012-03-01 12:03 +0000
Message-ID<slrnjkupc1.1ls.grahn+nntp@frailea.sa.invalid>
In reply to#18073
On Thu, 2012-03-01, Joe keane wrote:
> In article <slrnjkqma0.1ls.grahn+nntp@frailea.sa.invalid>,
> Jorgen Grahn  <grahn+nntp@snipabacken.se> wrote:
>>That it came from V7 was news to me. In a way, that's almost enough
>>information. V7 means the late 1970s; programmers were fewer and on
>>average smarter, and the conventions weren't the same as today.
>
> Fixed-width members [or variable with fixed maximum] are old as dirt.
> Think punched cards and 'tabulating machines'.
>
> Used as a key in a B-tree or hash table, you really do want to clear out
> the 'junk' (no need to go looking for the terminator).

I wasn't referring to that ... I was more thinking of things like
function naming conventions, i.e. did people in 1979 see a function
named strsomething() and immediately think of normal nul-terminated C
strings?

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

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


#17708

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-02-20 17:51 -0500
Message-ID<jhuipv$ffg$1@dont-email.me>
In reply to#17704
On 02/20/2012 04:25 PM, Joe keane wrote:
...
> Why can i use the same format for float/double in 'fprintf', but not in
> 'fscanf'?

Because float gets promoted to double when passed to fprintf(), while
the concept of "promoted type" doesn't even apply to the pointer
arguments passed to fscanf(). float* and double* are incompatible types,
which means they must be treated differently, and fscanf() needs to know
about that face.

> Why is it that multiply long and long gives long, but short and short
> gives int?

Integer types that are smaller than 'int' get promoted to 'int' on the
principle that 'int' should be the natural integer type for a given
platform. Integer types smaller than 'int' should be used only when
needed to save space - types larger than 'int' should be used only when
needed to represent large numbers.

...
> Why is it that 'int' means signed, but 'char' can be either one?

Because the normal character type on many of the machines that C was
first ported to was signed, while on many others it was unsigned,
whereas the normal integer type was (almost?) always signed.
-- 
James Kuyper

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


#17714

FromShao Miller <sha0.miller@gmail.com>
Date2012-02-20 18:14 -0500
Message-ID<jhujta$m7f$1@dont-email.me>
In reply to#17623
On 2/19/2012 15:33, Keith Thompson wrote:
> jgk@panix.com (Joe keane) writes:
>> In article<lnvcn6fyi2.fsf@nuthaus.mib.org>,
>> Keith Thompson<kst-u@mib.org>  wrote:
>>> There's rarely *any* reason to use strncpy().  It's not a "safer"
>>> version of strcpy(); it's a quite different function.
>>
>> It is a safer version of 'strcpy'.  There is the issue of what to do if
>> the full copy can't be done, but that's program logic and the library
>> function can't read people's minds.  Even if the program just calls
>> 'abort', that's a huge improvement.
>
> Did you not read my description of what strncpy actually does, or do you
> disagree with it?
>
> strncat is a "safer" version of strcat.  It takes an extra argument
> "n" that specifies the maximum number of characters to be copied.  If
> the source is longer than n characters, it appends just n characters.
> It properly zero-terminates the destination in all cases.
>
> strncpy *looks* like it should be to strcpy as strncat is to strcat,
> but it isn't.  If the source string is shorter than n characters,
> it will pad the destination with multiple null characters, something
> that strcpy never does.  If the source string is longer than n
> characters, it will leave the destination unterminated (i.e.,
> not a string).
>
> If it had been defined something like this:
>
>      char *better_strncpy(char *dest, const char *src, size_t n) {
>          dest[0] = '\0';
>          return strncat(dest, src, n);
>      }
>
> then it would be reasonable to call it a "safer" version of strcpy.
>
> (It's possible I have an off-by-one error in the above code;
> I haven't taken the time to check.)
>

My impression is that in:

   char foo[] = "foo";
   char bar[3] = "bar";
   char baz[10] = "baz";

each of these could be roughly equivalent to:

   char foo[sizeof "foo"];
   char bar[3];
   char baz[10];

   strncat(foo, "foo", sizeof "foo");
   strncat(bar, "bar", 3);
   strncat(foo, "baz", 10);

And that in:

   char blah[40] = { 0 };

this is roughly equivalent to:

   char blah[40];

   memset(blah, 0, 40);


And that in:

   int blee[2][2] = { { 13, 42 } };

this is roughly equivalent to:

   static const int hidden_blee_initializer[2][2] =
     { { 13, 42 }, { 0, 0 } };
   int blee[2][2];

   memcpy(blee, hidden_blee_initializer, sizeof blee);

Where each of these standard functions might be highly optimized and 
where an implementation might actually choose to implement the 
initializations using just the same logic.

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


#17726

Frompete <pfiland@mindspring.com>
Date2012-02-20 23:16 -0500
Message-ID<4F431A89.7DDD@mindspring.com>
In reply to#17714
Shao Miller wrote:
 
> My impression is that in:
> 
>    char foo[] = "foo";
>    char bar[3] = "bar";
>    char baz[10] = "baz";
> 
> each of these could be roughly equivalent to:
> 
>    char foo[sizeof "foo"];
>    char bar[3];
>    char baz[10];
> 
>    strncat(foo, "foo", sizeof "foo");
>    strncat(bar, "bar", 3);
>    strncat(foo, "baz", 10);

ITYM strncat(baz, "baz", 10);

foo, bar and baz 
would each have to be initialized with a null byte 
in order to properly use strncat on them like that.

But, even if bar[3] had static duration
and so was initialized with a null byte,
    strncat(bar, "bar", 3);
writes 4 characters and overruns the array.


n1570
7.24.3.2 The strncat function
Synopsis
    1 #include <string.h>
      char *strncat(char * restrict s1, 
              const char * restrict s2, size_t n);
Description
    2 The strncat function appends not more than n characters
      (a null character and characters that follow it are not appended) 
      from the array pointed to by s2 to the end of the string
      pointed to by s1. 
      The initial character of s2 overwrites the null character 
      at the end of s1. 
      A terminating null character is always appended to the result.

-- 
pete

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


#17909

FromShao Miller <sha0.miller@gmail.com>
Date2012-02-25 01:51 -0500
Message-ID<jia0di$3g9$1@dont-email.me>
In reply to#17726
On 2/20/2012 23:16, pete wrote:
> Shao Miller wrote:
>
>> My impression is that in:
>>
>>     char foo[] = "foo";
>>     char bar[3] = "bar";
>>     char baz[10] = "baz";
>>
>> each of these could be roughly equivalent to:
>>
>>     char foo[sizeof "foo"];
>>     char bar[3];
>>     char baz[10];
>>
>>     strncat(foo, "foo", sizeof "foo");
>>     strncat(bar, "bar", 3);
>>     strncat(foo, "baz", 10);
>
> ITYM strncat(baz, "baz", 10);
>

Right.  Except...

> foo, bar and baz
> would each have to be initialized with a null byte
> in order to properly use strncat on them like that.
>
> But, even if bar[3] had static duration
> and so was initialized with a null byte,
>      strncat(bar, "bar", 3);
> writes 4 characters and overruns the array.
>
>
> n1570
> 7.24.3.2 The strncat function
> Synopsis
>      1 #include<string.h>
>        char *strncat(char * restrict s1,
>                const char * restrict s2, size_t n);
> Description
>      2 The strncat function appends not more than n characters
>        (a null character and characters that follow it are not appended)
>        from the array pointed to by s2 to the end of the string
>        pointed to by s1.
>        The initial character of s2 overwrites the null character
>        at the end of s1.
>        A terminating null character is always appended to the result.
>

Somehow, I completely was confusing 'strncpy' with 'strncat', here. :( 
Please substitute 'strncpy' in place of 'strncat' in my post. :(  Of 
course, that makes it irrelevant to Keith's immediately-preceding post.

Thank you for the correction, pete!

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


#17929

Frompete <pfiland@mindspring.com>
Date2012-02-25 10:34 -0500
Message-ID<4F48FF8A.6666@mindspring.com>
In reply to#17909
Shao Miller wrote:
> 
> On 2/20/2012 23:16, pete wrote:
> > Shao Miller wrote:
> >
> >> My impression is that in:
> >>
> >>     char foo[] = "foo";
> >>     char bar[3] = "bar";
> >>     char baz[10] = "baz";
> >>
> >> each of these could be roughly equivalent to:
> >>
> >>     char foo[sizeof "foo"];
> >>     char bar[3];
> >>     char baz[10];
> >>
> >>     strncat(foo, "foo", sizeof "foo");
> >>     strncat(bar, "bar", 3);
 
> Somehow, I completely was confusing 'strncpy' with 'strncat', here. :(
> Please substitute 'strncpy' in place of 'strncat' in my post. :(  Of
> course, that makes it irrelevant
> to Keith's immediately-preceding post.
> 
> Thank you for the correction, pete!

You're welcome.

What I don't about the idea of using strncpy instead of strcpy,
is that learning to use strncpy instead of strcpy,
seems to me to be more complicated 
than learning to use strcpy properly.

The n parameter of strncpy makes it harder to forget
that the size of something must be taken into account.

But anyone who would have trouble remembering
how to use strcpy correctly, 
might also have trouble remembering 
that if n is less than (1 + source string length) 
then the resulting strncpy write, will not be a string.

I've always been comfortable using strcpy.

-- 
pete

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


#17716

FromRich Webb <bbew.ar@mapson.nozirev.ten>
Date2012-02-20 19:09 -0500
Message-ID<3on5k791lujmorfiuidd1rg747as5rkjoa@4ax.com>
In reply to#17623
On Sun, 19 Feb 2012 12:33:01 -0800, Keith Thompson <kst-u@mib.org>
wrote:

>jgk@panix.com (Joe keane) writes:
>> In article <lnvcn6fyi2.fsf@nuthaus.mib.org>,
>> Keith Thompson  <kst-u@mib.org> wrote:
>>>There's rarely *any* reason to use strncpy().  It's not a "safer"
>>>version of strcpy(); it's a quite different function.
>>
>> It is a safer version of 'strcpy'.  There is the issue of what to do if
>> the full copy can't be done, but that's program logic and the library
>> function can't read people's minds.  Even if the program just calls
>> 'abort', that's a huge improvement.
>
>Did you not read my description of what strncpy actually does, or do you
>disagree with it?
>
>strncat is a "safer" version of strcat.  It takes an extra argument
>"n" that specifies the maximum number of characters to be copied.  If
>the source is longer than n characters, it appends just n characters.
>It properly zero-terminates the destination in all cases.
>
>strncpy *looks* like it should be to strcpy as strncat is to strcat,
>but it isn't.  If the source string is shorter than n characters,
>it will pad the destination with multiple null characters, something
>that strcpy never does.  If the source string is longer than n
>characters, it will leave the destination unterminated (i.e.,
>not a string).
>
>If it had been defined something like this:
>
>    char *better_strncpy(char *dest, const char *src, size_t n) {
>        dest[0] = '\0';
>        return strncat(dest, src, n);
>    }
>
>then it would be reasonable to call it a "safer" version of strcpy.
>
>(It's possible I have an off-by-one error in the above code;
>I haven't taken the time to check.)

I'm surprised that the construct

   strncpy(dest, source, BUF_LEN)[BUF_LEN - 1] = '\0';

hasn't been mentioned. It seems to be a reasonably compact way of
dealing with uncontrolled input.

-- 
Rich Webb     Norfolk, VA

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


#17719

FromKeith Thompson <kst-u@mib.org>
Date2012-02-20 16:46 -0800
Message-ID<ln39a5c8ty.fsf@nuthaus.mib.org>
In reply to#17716
Rich Webb <bbew.ar@mapson.nozirev.ten> writes:
[...]
> I'm surprised that the construct
>
>    strncpy(dest, source, BUF_LEN)[BUF_LEN - 1] = '\0';
>
> hasn't been mentioned. It seems to be a reasonably compact way of
> dealing with uncontrolled input.

Yes, that should work.  (I've never seen anyone actually use that
idiom; have you?)

Note that if dest is, say, 1000 bytes, and strlen(source)==3,
then it will write 997 null characters into dest, when 1 would do.
If the source string is from user or file input, that's probably
not going to be significant.

This avoids that problem:

    dest[0] = '\0';
    strncat(dest, source, BUF_LEN - 1);

Another problem is that it *silently* truncates overly long input.
(That might be just what you want.)

The problems of strncat can be worked around if you're aware of them,
but I'm skeptical that it's worth the effort.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

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


#17723

FromRich Webb <bbew.ar@mapson.nozirev.ten>
Date2012-02-20 20:48 -0500
Message-ID<2qq5k75slcfdm2rahh6dihqir2ir5javai@4ax.com>
In reply to#17719
On Mon, 20 Feb 2012 16:46:01 -0800, Keith Thompson <kst-u@mib.org>
wrote:

>Rich Webb <bbew.ar@mapson.nozirev.ten> writes:
>[...]
>> I'm surprised that the construct
>>
>>    strncpy(dest, source, BUF_LEN)[BUF_LEN - 1] = '\0';
>>
>> hasn't been mentioned. It seems to be a reasonably compact way of
>> dealing with uncontrolled input.
>
>Yes, that should work.  (I've never seen anyone actually use that
>idiom; have you?)

Ermmm, me, actually. Where I use it constantly is in parsing data inputs
that can have real-world issues, such as a noise burst that over-filled
a field or a separator character between two fields that got dropped. I
deal with the correctness of the buffer subsequently but first I need to
ensure that the buffer is safely filled and terminated so that I can
look at it.

There will typically be separate checks for overall format (e.g.,
framing words, number of fields) and a checksum/CRC/hash but some day
the planets will be in a bad alignment where everything else is okay
and, as the saying goes, in a field where "foo" was expected
"supercalifragilisticexpialidocious" was read.

>Note that if dest is, say, 1000 bytes, and strlen(source)==3,
>then it will write 997 null characters into dest, when 1 would do.
>If the source string is from user or file input, that's probably
>not going to be significant.

Which is fine, although the difference is usually on the order of an
input length of eight into a buffer with room for, say, twelve.

>This avoids that problem:
>
>    dest[0] = '\0';
>    strncat(dest, source, BUF_LEN - 1);

True. A more complete solution would probably be a strlen() test
followed by truncation with \0 and an application diagnostic if the
input is too long. But I'm often dealing with embedded systems where
it's just a black box that can not fail from a data line hiccup.

>Another problem is that it *silently* truncates overly long input.
>(That might be just what you want.)

Pretty much, yes.

-- 
Rich Webb     Norfolk, VA

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


#17720

FromShao Miller <sha0.miller@gmail.com>
Date2012-02-20 19:46 -0500
Message-ID<jhupgm$li7$1@dont-email.me>
In reply to#17716
On 2/20/2012 19:09, Rich Webb wrote:
> On Sun, 19 Feb 2012 12:33:01 -0800, Keith Thompson<kst-u@mib.org>
> wrote:
>
>> jgk@panix.com (Joe keane) writes:
>>> In article<lnvcn6fyi2.fsf@nuthaus.mib.org>,
>>> Keith Thompson<kst-u@mib.org>  wrote:
>>>> There's rarely *any* reason to use strncpy().  It's not a "safer"
>>>> version of strcpy(); it's a quite different function.
>>>
>>> It is a safer version of 'strcpy'.  There is the issue of what to do if
>>> the full copy can't be done, but that's program logic and the library
>>> function can't read people's minds.  Even if the program just calls
>>> 'abort', that's a huge improvement.
>>
>> Did you not read my description of what strncpy actually does, or do you
>> disagree with it?
>>
>> strncat is a "safer" version of strcat.  It takes an extra argument
>> "n" that specifies the maximum number of characters to be copied.  If
>> the source is longer than n characters, it appends just n characters.
>> It properly zero-terminates the destination in all cases.
>>
>> strncpy *looks* like it should be to strcpy as strncat is to strcat,
>> but it isn't.  If the source string is shorter than n characters,
>> it will pad the destination with multiple null characters, something
>> that strcpy never does.  If the source string is longer than n
>> characters, it will leave the destination unterminated (i.e.,
>> not a string).
>>
>> If it had been defined something like this:
>>
>>     char *better_strncpy(char *dest, const char *src, size_t n) {
>>         dest[0] = '\0';
>>         return strncat(dest, src, n);
>>     }
>>
>> then it would be reasonable to call it a "safer" version of strcpy.
>>
>> (It's possible I have an off-by-one error in the above code;
>> I haven't taken the time to check.)
>
> I'm surprised that the construct
>
>     strncpy(dest, source, BUF_LEN)[BUF_LEN - 1] = '\0';
>
> hasn't been mentioned. It seems to be a reasonably compact way of
> dealing with uncontrolled input.
>

Or maybe:

   strncpy(dest, source, BUF_LEN - 1)[BUF_LEN - 1] = '\0';

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


#17750

Fromjgk@panix.com (Joe keane)
Date2012-02-21 21:46 +0000
Message-ID<ji13bl$1ip$1@reader1.panix.com>
In reply to#17623
In article <lnboouef7m.fsf@nuthaus.mib.org>,
Keith Thompson  <kst-u@mib.org> wrote:
>If it had been defined something like this:
>
>    char *better_strncpy(char *dest, const char *src, size_t n) {
>        dest[0] = '\0';
>        return strncat(dest, src, n);
>    }
>
>then it would be reasonable to call it a "safer" version of strcpy.

Well there you go.  You pretty much solved your complaint.  It you are
catenating several strings, it's almost more natural to zero out at the
beginning, then use 'strcat' from there.

You can understand why you might want to zero out the whole array at the
start; from there 'strcat' is fine.

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


#17753

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-02-21 17:15 -0500
Message-ID<4F441773.3050905@verizon.net>
In reply to#17750
On 02/21/2012 04:46 PM, Joe keane wrote:
> In article <lnboouef7m.fsf@nuthaus.mib.org>,
> Keith Thompson  <kst-u@mib.org> wrote:
>> If it had been defined something like this:
>>
>>    char *better_strncpy(char *dest, const char *src, size_t n) {
>>        dest[0] = '\0';
>>        return strncat(dest, src, n);
>>    }
>>
>> then it would be reasonable to call it a "safer" version of strcpy.
> 
> Well there you go.  You pretty much solved your complaint.

I don't see how the ability to define such a function changes any of the
issues he complained about - there's still a function named strncpy() in
standard library, and it's name still creates well-justified but false
expectations in newbies about what it might do. Careful reading of the
standard or good documentation  will correct those mistaken expectations
- but that shouldn't have been necessary.

I'm not saying that newbies shouldn't need to read the documentation, or
that the standard should be written so that reading it is unnecessary.
I'm merely saying that good naming conventions can make it easier to
guess what the functions do, and easier to remember what they do once
you have learned what it is. Either the behavior should have matched
those expectations, or the name should have been changed to make them
not well-justified.

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


#17755

FromKeith Thompson <kst-u@mib.org>
Date2012-02-21 14:29 -0800
Message-ID<lnhayjbz2a.fsf@nuthaus.mib.org>
In reply to#17750
jgk@panix.com (Joe keane) writes:
> In article <lnboouef7m.fsf@nuthaus.mib.org>,
> Keith Thompson  <kst-u@mib.org> wrote:
>>If it had been defined something like this:
>>
>>    char *better_strncpy(char *dest, const char *src, size_t n) {
>>        dest[0] = '\0';
>>        return strncat(dest, src, n);
>>    }
>>
>>then it would be reasonable to call it a "safer" version of strcpy.
>
> Well there you go.  You pretty much solved your complaint.  It you are
> catenating several strings, it's almost more natural to zero out at the
> beginning, then use 'strcat' from there.

Not really.  My complaint is that strncpy() is in the standard library
with the name "strncpy", and that too many programmers use it
incorrectly.

> You can understand why you might want to zero out the whole array at the
> start; from there 'strcat' is fine.

Note that multiple strcat() calls can be quite inefficient, since
each one has to scan the destination to find the terminating '\0'
before it appends the new data.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

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


#17779

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2012-02-22 01:15 -0800
Message-ID<e24c1402-0efb-4943-80c7-a45a8da513f0@t30g2000vbx.googlegroups.com>
In reply to#17755
On Feb 21, 10:29 pm, Keith Thompson <ks...@mib.org> wrote:
>
> Not really.  My complaint is that strncpy() is in the standard library
> with the name "strncpy", and that too many programmers use it
> incorrectly.
>
What's worse is that often the wrong use won't be detected.

strncpy() appears to eb a safe strcpy() if the buffer length is never
exceeded. Since normally the buffer will be larger than any string you
expect, this often won't be tested. Who's going to pass a string of
more than FILE_MAX to a program?

Then even if it is tested, there's a reasonable chance that the
character immediately folowing the buffer is a byte of value zero. So
it might well appear to a casual tester that the fucntin has worked as
expected - he might not notice the extra character.

--
Malcolm's website
http://www.malcolmmclean.site11.com/www

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


#17804

Fromjgk@panix.com (Joe keane)
Date2012-02-23 00:02 +0000
Message-ID<ji3vn9$q5o$1@reader1.panix.com>
In reply to#17755
In article <lnhayjbz2a.fsf@nuthaus.mib.org>,
Keith Thompson  <kst-u@mib.org> wrote:
>My complaint is that strncpy() is in the standard library
>with the name "strncpy", and that too many programmers use it
>incorrectly.

I think i agree on naming.  For example, 'strncpyz' zeros out the
buffer, 'strncpyu' can leave it without a terminator (the default is
less surprising), 'strncpyzu' does both.  The zero seems harmless, at
worst it runs slower, at best it runs faster.

But 'strcpy' doesn't give us much guidance here.

It *can't* zero out the buffer, because it doesn't know the buffer size.
It *can't* decide to leave out the terminator, because it doesn't know
the buffer size.  It can't avoid trashing your memory, for same reason.

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


#17808

FromKeith Thompson <kst-u@mib.org>
Date2012-02-22 17:02 -0800
Message-ID<ln4nuibbuz.fsf@nuthaus.mib.org>
In reply to#17804
jgk@panix.com (Joe keane) writes:
> In article <lnhayjbz2a.fsf@nuthaus.mib.org>,
> Keith Thompson  <kst-u@mib.org> wrote:
>>My complaint is that strncpy() is in the standard library
>>with the name "strncpy", and that too many programmers use it
>>incorrectly.
>
> I think i agree on naming.  For example, 'strncpyz' zeros out the
> buffer, 'strncpyu' can leave it without a terminator (the default is
> less surprising), 'strncpyzu' does both.  The zero seems harmless, at
> worst it runs slower, at best it runs faster.
>
> But 'strcpy' doesn't give us much guidance here.
>
> It *can't* zero out the buffer, because it doesn't know the buffer size.

It doesn't bother to zero out the buffer, because that's rarely a useful
thing to do.  If you want to zero a buffer, use memset().

> It *can't* decide to leave out the terminator, because it doesn't know
> the buffer size.

It doesn't leave out the terminator because it *needs* to store the
terminator in order for the destination to be a valid string.

>                   It can't avoid trashing your memory, for same reason.

It's up to the caller to avoid trashing memory by ensuring that the
destination is big enough to hold the data to be copied into it.
(Admittedly strcpy() doesn't make this easy.)

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[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