
Strncpy() is not a “safer” strcpy() - luu
http://the-flat-trantor-society.blogspot.com/2012/03/no-strncpy-is-not-safer-strcpy.html
======
angry_octet
For someone being so pedantic it is puzzling that he doesn't address the
actual title of his post. No one claims that strncpy is 'safe' they just say
'safer', that is, less likely than normal strcpy to have catastrophic bugs.
And I think that claim stands up: make sure your n in strncpy is the length of
your buffer, do a strncpy, write a null at the end of the buffer. Voila, all
those strcpyies from unexpectedly large strings writing in your heap are gone.
No need to constantly strlen source strings to check the length -- which in
itself can cause a segfault if they are not null terminated.

So yes, it is safer. You would be mad not to use it.

~~~
kazinator
Starting in C99, you can do this:

    
    
      snprintf(buffer, size, "%s", source);
    

This can be put into a #define macro or inline function.

If what you want is a null terminated, truncated copy that fits into a given
buffer size, and snprintf is available, you are mad to still use strncpy.

Also, strncpy pads the destination with nulls, if there is remaining space.
Because of this, the evident purpose of strncpy is to fill fixed-width text
fields in "old school" fixed-field database records. Writing the extra nulls
is wasteful if the data type in question is nothing but a C string.

~~~
jedbrown
With fixed-width records, writing the extra nulls may be important for data
privacy reasons and in any case, it allows comparison using memcpy(3),
diff(1), and checksums.

~~~
_kst_
Sure, but then you have to make sure that you _always_ maintain the extra
nulls.

diff(1) doesn't seem relevant; it operates on text files, which normally don't
contain _any_ null characters.

~~~
jedbrown
diff -q is often used in scripts to determine whether two files are identical.

~~~
_kst_
I would think that "cmp" or "cmp -s" would be better, particularly if the
files might be binary.

------
useerup
Microsoft has a number of dangerous c functions that are on the Secure
Development Lifecycle "banned" list. And yes, strncpy is one of them.

Some insights here: [https://msdn.microsoft.com/en-
us/library/bb288454.aspx](https://msdn.microsoft.com/en-
us/library/bb288454.aspx)

strncpy example vulnerability experience linked to from that article "Buffer
Overflow in Apache 1.3.xx fixed on Bugtraq - the evils of strncpy and
strncat!":
[http://blogs.msdn.com/b/michael_howard/archive/2004/10/29/24...](http://blogs.msdn.com/b/michael_howard/archive/2004/10/29/249713.aspx)

Source code for MS products are automatically (since SDL) screened for use of
the banned functions. You'll have to use the "safer" alternatives.

------
JulianMorrison
C people, isn't it perhaps time to quietly pasture the existing standard
library and create a new one, from scratch, that isn't chock full of booby
traps in the small print?

~~~
belorn
There is one, and it exist inside the gnu libc.

 _strndup_ performs identical to strncpy except that it always terminates the
destination string.
([https://www.gnu.org/software/libc/manual/html_node/Copying-a...](https://www.gnu.org/software/libc/manual/html_node/Copying-
and-Concatenation.html))

~~~
JulianMorrison
You remember the Holy Grail scene from Indiana Jones? The problem is not the
absence of the grail, somewhere on the shelf. The problem is all the others
and the fact that if you pick the wrong one, your program desiccates, crumbles
to dust, and is blown away by a conveniently dramatic wind.

A grail shelf that wasn't intended as a booby trap would contain _one_ cup,
the right one.

~~~
pjc50
But this is the great advantage of C! It's one Holy Grail for every platform!
Choice is wonderful!

/sarcasm

------
lawl
tl;dr: strncpy() doesn't null terminate, but doesn't mention strlcpy()

[http://www.openbsd.org/cgi-
bin/man.cgi?query=strlcpy](http://www.openbsd.org/cgi-
bin/man.cgi?query=strlcpy)

~~~
masklinn
Because, much like reallocarray, strlcpy is not part of the C standard
library.

The correct solution, assuming you can't just import libbsd or include its
functions, is probably to use snprintf. It goes guarantee null-termination of
the target. So

    
    
        strncpy(dst, src, size_dst);
    

becomes

    
    
        snprintf(dst, size_dst, "%s", src);
    

Checking for partial copy is not ideal though, snprintf returns the number of
bytes it _would_ have copied had the destination buffer been infinite
_excluding the final NUL byte_. So a copy is only complete if `ret <
size_dst`.

snprintf is not available in C89 though...

~~~
jjnoakes
snprintf is also slower. It has to parse the format string.

Just use strncpy and NUL-terminate the buffer. It's not hard. Write a two line
function to do it for you, and use that function in place of strncpy.

~~~
masklinn
> Just use strncpy and NUL-terminate the buffer. It's not hard.

Just as it's not hard to not dereference null pointers, not double-free
allocations and a truckload of other mindless drudgery which history shows
_humans won 't reliably get right all the time_. Not to mention checking for
copy truncation with strncpy is error-prone, you've got to check whether it
points to a `\0` _before_ you manually null-terminate it, providing more
chances to get it wrong.

> snprintf is also slower. It has to parse the format string.

How about worrying about that when snprintf actually shows up in profiles
_and_ there's no algorithmic way to improve the situation _and_ strncpy is a
significant improvement?

~~~
jjnoakes
But if you read my actual comment, I suggest not using strncpy everywhere.

Use it once. In a 2-line wrapper. And use the wrapper everywhere.

No human reliability to worry about.

------
JustSomeNobody
What do people get out of pedantic rants such as this? Anyone who's written
anything in C is quite aware of the issue here.

And then there's this gem:

"...If the source string is 5 characters long, and the target is a 1024-byte
buffer, and you set n to the size of the target, strncpy will copy those 5
characters and then fill all 1019 remaining bytes in the target with null
characters. Since all it takes to terminate a string is a single null
character, this is almost always a waste of time.

Ok, so that's not so bad. CPUs are fast these days, and filling a buffer with
zeros is not an expensive operation, right? Unless you're doing it a few
billion times, but let's not worry about premature optimization."

Does he think languages with built in String types have some magical
optimization juice that make string operations fast? How long does it take to
instantiate a new String object, run it's copy constructor, blah blah.

Any C programmer looks to avoid these types of situations.

~~~
_kst_
> What do people get out of pedantic rants such as this? Anyone who's written
> anything in C is quite aware of the issue here.

Unfortunately, that's not the case.

I presume this link was posted here as a result of my recent comment on the
"Don't Learn C the Wrong Way" article, which recommends strncpy() and
strlcpy() as safer alternatives to strcpy().

I've see a number of calls to strncpy(). I've rarely seen such calls
explicitly null-terminate the destination array. I've even seen things like

    
    
        strncpy(dest, src, strlen(src));
    

which is certainly no safer than strcpy().

What I get out of this particular pedantic rant is an opportunity to let C
programmers know that strncpy() isn't what they might thing it is (as well as
a mention on the front page of Hacker News!).

------
gvb
There is a very simple, efficient solution: always put a null termination
character in the end position of the destination after the strncpy(). If the
source is longer than the destination, it will properly terminate the
destination. If it isn't longer, it didn't cost (hardly) anything.

Unfortunately, it isn't automatic so some times humans will forget to do it.

    
    
        #include <stdio.h>
        #include <string.h>
    
        int main(int argc, char *argv[])
        {
            char *src = "Gobblygook is a long string";
            char dest[5];
    
            strncpy(dest, src, sizeof(dest));
            dest[sizeof(dest) - 1] = '\0';
    
            printf("src  = %s\ndest = %s\n", src, dest);
        }
    
        make test
       ./test
        src  = Gobblygook is a long string
        dest = Gobb

~~~
pjc50
While I've done this myself, it's not a 100% solution, because now you get a
truncated string that further processing may not be expecting. For example, if
you validate the un-truncated string somehow then take a copy of it, the copy
may be invalid.

------
cremno
A similar article: [https://randomascii.wordpress.com/2013/04/03/stop-using-
strn...](https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-
already/)

This article is also on GH (including some changes):
[https://github.com/Keith-S-Thompson/the-flat-trantor-
society...](https://github.com/Keith-S-Thompson/the-flat-trantor-
society/blob/master/002-strncpy.md)

------
StavrosK
> That second paragraph means that if the string pointed to by s2 is shorter
> than n characters, it doesn't just copy n characters and add a terminating
> null character, which is what you'd expect.

But what I would expect is that it would copy len(s2) characters, not n.

~~~
patal
That's correct. I think the author meant that.

------
earlz
It's a shame that strncpy doesn't have the semantics of OpenBSD's strlcpy,
which I think behaves as everyone would expect.

[http://www.openbsd.org/cgi-
bin/man.cgi?query=strlcpy](http://www.openbsd.org/cgi-
bin/man.cgi?query=strlcpy)

------
bitwize
You should be using strcpy_s...

------
tobiasu
This beard has its own beards.

