
Stop using strncpy already (2013) - ohjeez
https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
======
AdmiralAsshat
In principle, I feel like "Ignore the language-provided standard libraries and
use this little macro I wrote in all your code" is bad advice.

If the standard libs are _that bad_ , the community should really lobby for
the inclusion of a "safe" string copy into the C standard. Failing that, use a
battle-tested third-party library like bstrlib[0].

[0][http://bstring.sourceforge.net/](http://bstring.sourceforge.net/)

~~~
pcwalton
The C standards committee is incredibly conservative, only adding new features
if absolutely necessary. Unless the composition and goals of the committee
drastically change, a new standard string API will never happen.

If you must write C, you should use a real string library, and not string.h.

~~~
iforgotpassword
Nonsense, strncpy and friends are just fi2•∟1F↔Ñ

~~~
adiusmus
Looks like a Perl script I read once.

------
gp7
Two years after this post, the Linux kernel added strscpy, the api of which is
equivalent to the safe strncpy in this post. Internally, it stops copying once
it reaches the null terminator.

[https://lwn.net/Articles/659214/](https://lwn.net/Articles/659214/)

~~~
IChooseYou
Also Microsoft's strncpy_s has been around forever.

~~~
rurban
But Microsoft's strncpy_s is unsafe. My safeclib also had the unsafe versions
behind the non-default ./configure --enable-unsafe switch.

But this year I decided to make them safe instead. In this case the spec is
broken.

------
wmil
I feel like all of these articles can be summed up with:

"C strings and string functions aren't designed to be safe. If you want safe
strings use a library."

------
beefhash
> _strlcpy is designed to solve the null-termination problems – it always
> null-terminates. It’s certainly an improvement over strncpy, however it
> isn’t natively available in VC++._

Let's not forget that it's also not natively available in glibc. Though you'll
find it in every BSD you can think of.

~~~
jandrese
Sadly, it still has this braindamage:

    
    
      Like snprintf(3), the strlcpy() and strlcat() functions return the total
      length of the string they tried to create.  For strlcpy() that means the
      length of src.  For strlcat() that means the initial length of dst plus
      the length of src.
    

So if you've mmaped in something like the OpenStreetMaps xml dump file and try
to strlcpy out the first 100 bytes (because you didn't want to have to null
terminate it yourself after a memcpy), you'll notice that your program is
running rather slowly.

~~~
needlepont
That is trusting user input. You don't do that, right?

~~~
quietbritishjim
It's not about trusting user input. It's asking for a function to create a 100
byte substring of a very long string and expecting it to take time~100 not
time~len(src).

In fairness, that is probably just not the target "market" for strlcpy.
Presumably, it is meant for "please copy this whole string which I expect to
fit in the target (but catch me in the rare case that it does not)".

~~~
needlepont
As you note: your example is specious.

~~~
quietbritishjim
What example? Are you confusing me with GP commenter perhaps?

------
umvi
I feel like there are a lot of good alternatives to C. What we did on our team
was get our C compiling under g++, and then refactor as C++14. Now string
copies are trivial and safe. I'm sure it would have been even safer to switch
to Rust or something, but no one in my company has any experience with that,
while there were a few C++ gurus.

~~~
jstimpfle
What are the good alternatives? You can either copy memory manually, or have a
system like C++'s that causes lots of tiny allocations, which is very
inefficient. Both in space and time. You can also opt for immutable strings to
be able to share strings more conveniently and efficiently, at the cost of
O(n^2) build time (where automatic optimizations fail) or decreased
convenience (explicit string-stream aka string builder).

If efficiency is not a concern for you and you rather like the convenience,
good for you.

Personally I feel plain const char pointers (plus length, depending on
expected size) are good enough to emulate the immutable string type. For
modifiable strings, I still prefer viewing them as chunks of memory, using
char pointer + length + capacity. Nice thing is, you can easily go from the
former to the latter in the lifetime of any one string. You can also pool many
strings in large chunks if you build only one string at any one time.

~~~
pjmlp
You care about that after a profiler proves that it is actually a problem to
the expected delivery SLAs for the application.

Micro-optimizing each line of C code based on gut feeling is not an option if
quality matters.

~~~
gnulinux
This is a horrible stance and this is repeated like broken record. Please stop
saying this? A good software engineer should know (1) a suitable data
structure to solve the problem in hand (2) algorithms that have certain
asymptotic characteristic that makes most sense for the problem (what
assumptions can you make). "Premature optimization is the root of all evil"
can be used for and only for "optimization". Choosing the right data structure
is never an optimization, it's a design choice. You can't just start coding
everything with a linked list and then optimize bottlenecks, that's just not
how it works.

~~~
pjmlp
As you say "A good software engineer should know a suitable data structure to
solve the problem in hand".

So I expect a good software engineer on my team to be able to design a data
structure where the fact that a linked list is being used doesn't hinder to
change it afterwards to an array, a B-Tree or what have you.

Without changing a single line of client code I might add.

Such good software engineer will certainly had attended an algorithms and data
structures class on their CS degree and be aware of architecture designs based
on Abstract Data Types.

Yes, even in C with its flaky translation units instead of proper module
systems, it is possible to implement Abstract Data Types application
architectures, which any good software engineer can easily tackle.

And for those that aspire to reach that level, here are a few examples.

"Algorithms + Data Structures = Programs" by Niklaus Wirth, 1st edition is
Modula-2, the 2nd in Oberon

"Introduction to Algorithms" by Thomas Cormen, Clifford Stein, Ronald Rivest,
Charles Leiserson

"Abstract data types and Modula-2: a worked example of design using data
abstraction" by Richard Mitchell

"Abstract Data Types in Modula-2" by Rachel Harrison

"Abstract Data Types and Algorithms" by Azmoodeh, Manoochchr

~~~
gnulinux
Your comment still doesn't make sense, even though you decided to choose a
certain ADS and then latee decide on the implementation later based on
empirical performance data, this is still not unusual. For (1) this is not how
people write C. There are ADS libraries in C with which you wouldn't need need
to use any DS that you can't change but this is not a usual way to write C
code. This will also come with the overhead of pointer indirection and worse
cache performance since all your objects will be void* stored in the DS
(unless you m4 or macro generate your DS with all datatypes. This is what
linux does for example, and I sometimes use this technique too, but this
hardly the usual way to write C code). And (2) usually complex problems
require special enough datastructures that it doesn't make much sense talk
about ADSs. Everyone can code basic foobar with List interface then decide to
use ArrayList or LinkedList based on performance. It's more interesting when
the programmer decides to use a set, a hash map, a bloom filter etc to solve
the problem as any other type wouldn't make sense.

~~~
pjmlp
We will have to agree to disagree, then.

Sofware quality and meeting delivery SLAs should trump micro-optimizing cache
access per code line.

So if SLAs define 100ms per access, no need to go crazy making everything
under 5ms.

Thankfully, the Linux foundation has another view on the matter.

[https://www.youtube.com/watch?v=XfNt6MsLj0E](https://www.youtube.com/watch?v=XfNt6MsLj0E)

------
hedora
I don’t understand why silently truncating strings is considered to be
acceptable practice.

If you have the length of both, simply assert the length of the destination is
<= the length of the source. Problem solved (bugs become obvious in testing).

~~~
russdill
Because the API is stupid and not intuitive. It's for memory buffers where the
string is either NULL terminated or it is the length of the memory buffer.
This saves an extra byte. yay?

~~~
russdill
I'll add that it's really handy for that specific situation as it also avoids
leaking uninitialized bytes.

------
dang
Discussed at the time:
[https://news.ycombinator.com/item?id=5491121](https://news.ycombinator.com/item?id=5491121)

------
kodis
I'd say "Stop using C and C++ already" or "Stop using null terminated strings
already", but I recognize that those are not always practical solutions.

I do have some maintainability concerns though about the promulgation of
numerous competing home-brew solutions to a near trivial problem throughout a
code base.

------
hzhou321
If we preset the end of buffer with '\0' and then assume the buffer having
1-less capacity, wouldn't it address the strncpy issue?

~~~
needlepont
Of course, but everyone wants to overdo the complexity of the time worn
solution. OMG you need to null terminate the string after all the _other_
gymnastics..gee C sure does suck! Why don't we use _rust|go|c++_ ad-nauseam.
bzero(buf,sz); / _memset nazis here_ / strncpy(buf,src,sz - 1);

~~~
bitwize
It doesn't make sense to have to null-terminate the string by hand (maybe)
after doing a "safe" STRING copy. Which means it's easy to forget to do and
that's a dangerous wart in the design.

Really, stop using C. To quote Hayao Miyazaki, C was a mistake.

------
mcguire
Been a while since I used C++...

How does this work if the size of the destination isn't known at compile time?
I.e., it's not "buffer[5]”?

~~~
brucedawson
You get a compile error.

And this is progress. I have fixed code that looked something like this:

    
    
        strncpy(dst, sizeof(dst), src);
        data[sizeof(dst)-1] = 0;
    

See the bug? Of course not, because it's not visible, but I've seen this
pattern used where dst is a pointer. This means that four or eight bytes
(three or seven if you don't count the null terminator) gets copied,
regardless of how many bytes dst points to. With strcpy_safe an attempt to
copy to dst would not work and the developer would have to fix their code.

Every mistake that can be made, will be made.

------
ahoka
strncp is fine, however it was never meant for null-terminated strings! A safe
alternative is using snprintf and checking the return value for truncation.

~~~
brucedawson
snprintf is hardly safe. It does guarantee null termination, so that is
progress, but it requires the caller to pass the buffer size. As I mention in
the "More typing means more errors" section of the article software developers
are endlessly creative about ways to pass the wrong buffer size. They
_usually_ pass the right buffer size, but 99.9% correct still means a lot of
bugs. These bugs can be avoided by using a template function to infer the
destination buffer size. A template wrapper around snprintf would work very
nicely.

~~~
bigpicture
> These bugs can be avoided by using a template function to infer the
> destination buffer size

I'm a big fan of using strategies to avoid bugs. Another strategy than can be
a big help is to spend a little time thinking about what kinds of strings you
are going to be using and how big they might be. And then sanitizing your
program input based on those sizes. Find the too-long strings before they get
into your program and before you start copying them around.

The system I'm responsible for came into being as a set of programs on a
Univac II and was a bunch of C and FORTRAN when I joined. Because of the need
to pass data between C and FORTRAN programs/code, we had a set of 10-20
standard string sizes, based on what kind of text you had. We no longer write
new code in either C or FORTRAN, but maintain the concept of standard sizes
for our text. By specifying the kind of text (we also do this with numbers) in
the design steps, we save far more time and effort than we spend over the
entire SDLC.

