tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Usage of strncpy in the kernel



At Sat, 4 Jan 2025 18:42:08 -0500 (EST), Mouse <mouse%Rodents-Montreal.ORG@localhost> wrote:
Subject: Re: Usage of strncpy in the kernel
>
> > strncpy() has the advantage of working nicely and predictably
> > regardless of whether the known buffer's length is that of the source
> > or that of the destination.  I.e. one can use it to copy a known
> > number of possibly (or definitely) unterminated chars from an array
> > of a given length into an array of the same or larger size;
>
> Huh?
>
> If I have an array [N1], NUL-terminated unless length is N1, and I want
> to copy that quasi-string into an array [N2], with truncation if src
> length > N2 and NUL-termination if src length < N2...I can't see how
> strncpy can do that.

That's not what I described.  Especially not in just that part quoted
above.  I'm not sure what you're trying to get at.  I didn't mention
"NUL-termination" once in the above quoted portion.

strncpy() itself does not do NUL-termination, unless one conflates that
with the filling of the destination with zeros in the case where the
source "ends" before 'len' characters have been copied.  The zero-
filling should not be conflated with NUL-termination, and definitely
never be relied upon as an alternative to explicit separate NUL-
termination.  strncpy() must always copy 'len' characters, and so if the
source "ends" before that many have been copied, it continues copying
zeros until it is finished

This is described arguably better in the POSIX spec than most manual
pages (here with NetBSD strncpy(1) parameter names in square brackets):

    If the array pointed to by s2 [[src]] is a string that is shorter
    than n [[len]] bytes, NUL characters shall be appended to the copy
    in the array pointed to by s1 [[dst]], until n [[len]] bytes in all
    are written.

POSIX clarifies the meaning of "a string that is shorter than 'n' bytes"
in the paragraph above the quoted one:

	(bytes that follow a NUL character are not copied)

As I said, if one wishes to use the destination array as a C string then
one must _always_ manually NUL terminate it explicitly.

So as a concrete example of this first scenario I described:

	struct foo {
		char name[8];	/* an array with an up-to-8-char name */
	} src = { { '1', '2', '3', '4', '5', /* '6', '7', '8' */ } };

	char *dst = malloc(sizeof(src.name) + 1);

	strncpy(dst, src.name, sizeof(src.name));
	dst[sizeof(src.name)] = '\0';

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

Note how the 'len' is specified as the size of the source array, and the
programmer makes sure the destination is larger than that.

Now a concrete example for the alternate scenario I described:

	size_t srclen;
	char *longsrc = fgetln(stdin, &srclen);
	char *nln = NULL;
	struct foo {
		char name[8];	/* an array with an up-to-8-char name */
	} dst;

	if (longsrc[srclen - 1] == '\n') {
		longsrc[srclen - 1] = '\0';
	} else if (srclen > 0) {
		nln = malloc(srclen + 1);

		/* pedantically add a NUL in case input is < 8 chars */
		memcpy(nln, longsrc, srclen);
		nln[srclen] = '\0';
		longsrc = nln;
	} else {
		exit(1);	/* EOF on input */
	}

	strncpy(dst.name, longsrc, sizeof(dst.name));

	if (nln) free(nln);

Note how 'len' here is now specified as the size of the destination
array, which is not going to be used as a string so it is not
NUL-terminated (but it may end up being a string shorter than 8, though
the whole of the array will have been filled with valid data).

--
					Greg A. Woods <gwoods%acm.org@localhost>

Kelowna, BC     +1 250 762-7675           RoboHack <woods%robohack.ca@localhost>
Planix, Inc. <woods%planix.com@localhost>     Avoncote Farms <woods%avoncote.ca@localhost>

Attachment: pgpvvmdpnXnPY.pgp
Description: OpenPGP Digital Signature



Home | Main Index | Thread Index | Old Index