News for hackers. Stuff that matters.
Re:Beware of strncpy and strncat by tlb on Wednesday June 06, 2018 @11:50AM (#17248109)
Beware of the "correct" solutions proposed here.

  // OK: correctly copy src
  char dest[8];
  strncpy(dest, src, sizeof(dest) - 1);
With a long src, it fails to null-terminate dest. dest[7] will be whatever the contents of uninitialized memory were, so reading dest as a string is likely to run past the end.

strlcpy has a better API, though sadly it's not standard on Linux.

Re:Beware of strncpy and strncat by majke on Wednesday June 06, 2018 @12:05PM (#17248245)
should be either

  char dest[8] = {0};
  strncpy(dest, src, sizeof(dest) - 1);
or

  char dest[8];
  strncpy(dest, src, sizeof(dest) - 1);
  dest[sizeof(dest)-1] = '\0';

On the same note I always wondered what "snprintf" does. That is - will it ALWAYS zero terminate (given non-NULL buffer, of size > 0)?
Re:Beware of strncpy and strncat by pascal_cuoq on Wednesday June 06, 2018 @12:26PM (#17248473)
Yes. “snprintf(dest, sizeof dest, "%s", src);” where dest is a char array almost is the programmer-friend version of strncpy() that does exactly what one would expect, neither more nor less. It always does '\0'-terminate its output as long as it is passed a size >0.

The only issue with that idiom is that it is only defined to pass a '\0'-terminated string for src. Although it will only write the specified number of characters, it will read from src until the end of the string, and invoke undefined behavior if src does not point to a well-formed string:

https://taas.trust-in-soft.com/tsnippet/t/e0538551

A mnemonic is that snprintf needs to return the length of the string that would have been output if there had been enough room (not counting the terminating '\0'), so it needs to compute the length of the string pointed by src.

A close variant, below, avoids this issue but instead has the problem that the printf star argument is typed as an int, which is narrower than size_t on a typical 64-bit compilation platform.

    snprintf(dest, "%.*s", (int)(sizeof dest)-1, src);
Re:Beware of strncpy and strncat by loeg on Wednesday June 06, 2018 @04:03PM (#17250582)
> but instead has the problem that the printf star argument is typed as an int, which is narrower than size_t on a typical 64-bit compilation platform.

If you are using >2 GB buffers and anticipate >2 GB strings, it probably makes sense to track lengths explicitly and use memcpy() etc instead of string routines anyway.

Re:Beware of strncpy and strncat by pascal_cuoq on Wednesday June 06, 2018 @05:07PM (#17251135)
I was more thinking of the case where >2GiB strings are not useful for normal use and the programmer does not anticipate them, but a malicious user can cause such strings to happen, for instance by sending them over the network in minutes or hours, causing unforeseen behavior.
Re:Beware of strncpy and strncat by loeg on Wednesday June 06, 2018 @05:24PM (#17251254)
Sure, that's a good point.

However, while it may also be possible, in some code, for a malicious user to control the buffer size, the int precision argument, as used in this construct, derives from the buffer size, and not the input string.

If the user can control the buffer size, then yes, we get the very undesireable buffer overflow via overflow from positive to negative[0]:

    A negative precision is taken as if the precision were
    omitted.
[0]: snprintf(3) man page from linux
Re:Beware of strncpy and strncat by wahern on Wednesday June 06, 2018 @02:04PM (#17249463)
An annoyance with snprintf is that it has a failure mode, and in particular it can fail with ENOMEM under memory pressure.[1] This isn't merely theoretical as glibc implements snprintf by reusing the stdio machinery--effectively instantiating a temporary FILE structure, buffer, etc. glibc tries to stack allocate these objects using magic constants, but the code is incredibly complex. It's been awhile since I dove into the code, but IIRC snprintf could fail if you try to compose strings longer than the magic internal constants and malloc fails.

strlcpy() has the same semantics as snprintf(buf, sizeof buf, "%s", src) but without the failure mode. Because glibc is so stubborn, and rather than trying to wrap snprintf() to abort on OOM, I invariably include this simple implementation in a common header,

  static size_t
  aux_strlcpy(char *dst, const char *src, size_t lim)
  {
    size_t len, n;
    len = strlen(src);
    if (lim > 0) {
      n = MIN(lim - 1, len);
      memcpy(dst, src, n);
      dst[n] = '\0';
    }
    return len;
  }
which is substantially simpler than the canonical version from OpenBSD.

A related problem with snprintf is the mixture of signed and unsigned types for communicating object size. Mixing signed and unsigned types is, IME, error prone, so if I have code that uses snprintf more than a few times I usually wrap it in a function that separates the status from the [logical] size return values. People complain that strlcpy is problematic because it similarly communicates status (i.e. truncation) and object size through the same channel. But 1) it's not as nearly as error prone as mixing signed and unsigned types and 2) idiomatic use of strlcpy is easy to code and mentally parse and I've never felt the urge to wrap strlcpy in a helper routine. If truncation is always a failure than I'll simply use a two-line routine that returns an error code. But more often than not I rely on silent truncation. IMO, C code shouldn't be doing complex string operations using native C strings[2], and where it does make sense to use C strings it's usually things like configuration values where garbage in is garbage out; if an overlong property name is truncated it's no different than if it was misspelled. (People seriously overestimate the utility of supporting dynamic object sizes everywhere, and underestimate the inherent complexity it causes, which is significant even in low-level languages like C++ or Rust that make it safer and more convenient.)

[1] Linux's overcommit doesn't save you from, e.g., process resource limits.

[2] In fact, IMO complex parsing and composition shouldn't even be done with any kind of generic string API. If you get to the point where you're parsing and composing highly structured text, you should be using proper techniques with specialized data types. Writing ad hoc string munging code is error prone and a maintenance nightmare in any language, including scripting languages. But if I must than not only do I avoid C, I avoid any low-level, statically typed language. Scripting languages were literally invented for writing ad hoc string munging code.

Re:Beware of strncpy and strncat by wahern on Wednesday June 06, 2018 @03:01PM (#17250030)
Another important caveat to snprintf: because glibc's snprintf might use dynamic memory allocation it's not async-signal-safe. OpenBSD notably rewrote their snprintf implementation to be async-signal-safe (with the exception of floating-point formatting). They did this so extension functions like dprintf(), commonly used in signal handlers for debugging or logging, would be easier to implement, and also because a lot of software assumes that (or doesn't even consider whether) snprintf is async-signal-safe.

I wonder whether glibc's dprintf() is async-signal-safe....

Re:Beware of strncpy and strncat by masklinn on Wednesday June 06, 2018 @12:26PM (#17248474)
Can't you just strncpy sizeof(dest) if you're going to zero-out the last byte of the buffer unconditionally?
Re:Beware of strncpy and strncat by loeg on Wednesday June 06, 2018 @04:05PM (#17250598)
Yeah. You've "wasted" an extra byte copy, but that is usually so cheap as to be virtually free.
Re:Beware of strncpy and strncat by MBCook on Wednesday June 06, 2018 @02:45PM (#17249879)
But just adding the null byte isn’t necessarily safe if you’re dealing with Unicode, right?
Re:Beware of strncpy and strncat by loeg on Wednesday June 06, 2018 @04:05PM (#17250609)
Correct. You can't just truncate a utf-8 stream in an arbitrary byte position and expect it to remain well-formed.
Re:Beware of strncpy and strncat by BadThink6655321 on Wednesday June 06, 2018 @12:25PM (#17248451)
The first is inefficient. The declaration statement writes eight zeros to dest. strncpy() writes len bytes of zeros to dest, then copies the string, stopping at fewer than len bytes if a NUL is found in the src.
Re:Beware of strncpy and strncat by tlb on Wednesday June 06, 2018 @12:19PM (#17248396)
Yes, snprintf does the right thing.
Re:Beware of strncpy and strncat by wyldfire on Wednesday June 06, 2018 @12:08PM (#17248280)
> strlcpy has a better API, though sadly it's not standard on Linux.

I think it's because drepper hates us [1].

[1] http://www.sourceware.org/ml/libc-alpha/2000-08/msg00061.htm...

Re:Beware of strncpy and strncat by huhtenberg on Wednesday June 06, 2018 @12:18PM (#17248372)
> Dammit, it is not safe. It hides bugs in programs. If a string is too long for an allocated memory block the copying must not simply silently stop.

This is spot on. There are very few cases when silently truncating a copy of a string is an OK thing to do.

Re:Beware of strncpy and strncat by wyldfire on Wednesday June 06, 2018 @12:21PM (#17248415)
I think it's not genuine to describe the behavior of strlcpy() as "silently truncating". strlcpy() is documented as "If the return value is >= dstsize, the output string has been truncated. It is the caller's responsibility to handle this."
Re:Beware of strncpy and strncat by thefifthsetpin on Wednesday June 06, 2018 @12:30PM (#17248523)
Silent doesn't imply undocumented. strlcpy's truncation is silent because it doesn't inform the caller about whether or not it truncated.
Re:Beware of strncpy and strncat by stephencanon on Wednesday June 06, 2018 @12:37PM (#17248595)
The caller is informed: "If the return value is >= dstsize, the output string has been truncated"
Re:Beware of strncpy and strncat by akkartik on Wednesday June 06, 2018 @12:51PM (#17248747)
How is returning a different value "silent"? It's informing the caller just fine.

This is C we're talking about. Always be checking return values. (And errno! And errno! And errno!)

Re:Beware of strncpy and strncat by bripeace on Wednesday June 06, 2018 @01:58PM (#17249417)
Careful! Errno can be set even when there is not an error.it's value is only relevant when the return value of the function indicates an error.

That said there are some functions where the possible return values can't indicate an error. In those cases errno should be set to zero before the function call and then a change during will alert you to an error.

Why it's like this I have no idea but it's pretty annoying.

Re:Beware of strncpy and strncat by nothrabannosir on Wednesday June 06, 2018 @02:23PM (#17249637)
> Why it's like this I have no idea but it's pretty annoying.

The bit about errno possibly being non-zero but not indicating error unless the function does? I guess because the called function can itself call functions / libs which set errno and properly handle those (or just not care). Without the "errno only has meaning if the function returns error" guard, every single function needs to always reset errno to zero before returning, which is annoying and will almost certainly not happen in the majority of cases.

When the function has no way to communicate error, there is no other option but to use errno. But it's good that it's the exception, I imagine.

Re:Beware of strncpy and strncat by Sohcahtoa82 on Wednesday June 06, 2018 @01:26PM (#17249105)
But....it does inform the caller via the return value...what are you expecting it to do?
Re:Beware of strncpy and strncat by raverbashing on Wednesday June 06, 2018 @12:24PM (#17248438)
I'll take truncating errors in place of non-null terminating errors, thanks.

Can it makes things worse? Yes. But C is broken.

Re:Beware of strncpy and strncat by loeg on Wednesday June 06, 2018 @04:07PM (#17250623)
Ok, but, we've already got that kind of foot-shooting enabled by strncpy(), and glibc can't remove it as it is part of ISO C. If glibc must contain a truncating string copy, I'd much rather have strlcpy than strncpy. Do you find that persuasive?
Re:Beware of strncpy and strncat by matheusmoreira on Wednesday June 06, 2018 @12:27PM (#17248495)
I think he's right. Treating strings as if they were normal arrays makes things easier and more uniform. The size of arrays should always be known.

I still keep the NUL terminator around for compatibility reasons. Linux system calls don't need the NUL, but other C code might depend on it.

Re:Beware of strncpy and strncat by gowld on Wednesday June 06, 2018 @12:15PM (#17248348)
> 08 Aug 2000

Any progress since?

Re:Beware of strncpy and strncat by wyldfire on Wednesday June 06, 2018 @12:22PM (#17248418)
I think he still hates us ;)
Re:Beware of strncpy and strncat by loeg on Wednesday June 06, 2018 @04:19PM (#17250739)
On the plus side, Drepper left Redhat in 2010 and has been out as the glibc DFL since 2012.[0]

Amusingly, a request for strlcpy was the very 2nd comment on that LWN article.

Unfortunately, glibc still hasn't added it — Roland rejected it again in 2015.[1] Also unfortunately, RH hired Drepper back in 2017. Because they hate their other employees, I guess. Fortunately, no one is eager to give him back the reins of glibc.

[0]: https://lwn.net/Articles/488847/

[1]: https://sourceware.org/ml/libc-alpha/2015-11/msg00086.html

Re:Beware of strncpy and strncat by bitwize on Wednesday June 06, 2018 @12:18PM (#17248377)
The C11 Annex K functions, strcpy_s et al., have a better API than even strlcpy, and are (an optional) part of the C11 standard. They are what you should be using.
Re:Beware of strncpy and strncat by rurban on Wednesday June 06, 2018 @05:41PM (#17251335)
Nope. I'm the implementor of this variant, and even the safe variant was designed as unsafe.

In my implementation, the safeclib, I made it safe just recently.

Re:Beware of strncpy and strncat by dwc on Wednesday June 06, 2018 @01:36PM (#17249210)
It's worth noting that strlcpy and strlcat are quite small, very stable, and can be brought into any program that needs them. Yes, it's an extra step but not an onerous one. Of course it's nice to autoconf them if practical.
Re:Beware of strncpy and strncat by dig1 on Wednesday June 06, 2018 @12:20PM (#17248402)
Even strncpy() man page warns about this. Correct behavior is:

  strncpy(buf, str, buflen - 1);
  if (buflen > 0)
    buf[buflen - 1] = '\0';
Re:Beware of strncpy and strncat by unpopular42 on Wednesday June 06, 2018 @12:36PM (#17248585)
If you expect that it's possible for buflen to be 0, you should check before the strncpy call, not after.
Re:Beware of strncpy and strncat by DSMan195276 on Wednesday June 06, 2018 @01:04PM (#17248881)
Especially since `strncpy` takes a `size_t`, meaning that `buflen - 1` will turn from `-1` to `SIZE_MAX` due to the signed to unsigned conversion. I think OP just slightly misremembered what is on the man page:

    strncpy(buf, str, n);
    if (n > 0)
        buf[n - 1]= '\0';
Which still checks `n` after calling `strncpy`, like you mentioned, but doesn't have the underflow problem.
Re:Beware of strncpy and strncat by veli_joza on Wednesday June 06, 2018 @01:22PM (#17249069)
If my count is good, this is correction of correction of correction of original article's correction. And average developer is expected to get it right the first time? It's a miracle anything built in C actually works.
Re:Beware of strncpy and strncat by orwin on Wednesday June 06, 2018 @05:24PM (#17251250)
Yes. An average developper with knowledge in C will. Honestly, around my 3rd year i created a lib containing a struct for strings (well, two, and a couple functions working with them) and i don't think i ever used an strX function again.

I was pretty proud of the "string for already allocated buffer" part of this lib tbh.

Anyway, i'll never take C for a four-hour coding challenge, but for bigger, non-web project, C is really awesome, thank to BLAS and to the fact that most languages implement mostly painless C ffi.

Re:Beware of strncpy and strncat by DSMan195276 on Wednesday June 06, 2018 @11:41PM (#17253073)
I would add that while I do normally use regular C strings rather then my own library, I still can't remember the last time I used any of the random `str*` functions. I don't do much string composition anyway, but the little I do is almost always using `snprintf` or variants (Which have a non-braindead API, unlike `strncpy`). Tons easier to read and much more effective then a bunch of `strcpy`, `strcat` and such.

The only thing I wish the standard library would have is an `asprintf` which would return an allocated buffer. I've written my own, but the easy version utilizing regular `sprintf` requires calling `sprintf` twice (Once to get the length, and once to actually put the string in the allocated buffer). A regular supported version would be much more efficient.

Re:Beware of strncpy and strncat by jwilk on Wednesday June 06, 2018 @06:55PM (#17251817)
dig1 remembered it right. This bug was introduced in man-pages 3.62:

https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/...

Re:Beware of strncpy and strncat by DSMan195276 on Wednesday June 06, 2018 @10:36PM (#17252822)
Huh, well that's embarrassing. It looks like it's still in there too. I googled the man page but I guess I saw an older version. I might submit a patch if I find time. It should really be something like this:

    if (n > 0) {
        strncpy(buf, str, n - 1);
        buf[n - 1] = '\0';
    }
Of course, the error isn't super huge considering if you're using a zero-length buffer as a null-terminated string you're going to have other problems. But they still do check for 0, so IMO it's worth correcting.
Re:Beware of strncpy and strncat by jwilk on Thursday June 07, 2018 @05:06AM (#17254376)
Matthew Kilgore submitted patch to fix this:

https://marc.info/?l=linux-man&m=152834469929748

Re:Beware of strncpy and strncat by dig1 on Thursday June 07, 2018 @08:54AM (#17255322)
Huh, I blindly pasted example assuming man pages are always right. Double check next time :D
Re:Beware of strncpy and strncat by DSMan195276 on Thursday June 07, 2018 @08:58AM (#17255340)
Yes, that would be me :)
Re:Beware of strncpy and strncat by kevin_thibedeau on Wednesday June 06, 2018 @12:34PM (#17248567)
It still wastes time zero padding dest when src is shorter.
Re:Beware of strncpy and strncat by nebulous1 on Wednesday June 06, 2018 @12:37PM (#17248592)
He's assuming that dest is zero-initialized, which I believe it will be as written. However, he should mention that because there will be plenty of similar cases where dest will not be initialized.

edit: As asveikau notes below, this is not the case

Re:Beware of strncpy and strncat by docker_up on Wednesday June 06, 2018 @12:46PM (#17248701)
No, only global variables are zero-initialized. Neither stack variables nor malloced memory will be zero-initialized. OP should have memset to 0 or calloced the memory.
Re:Beware of strncpy and strncat by asveikau on Wednesday June 06, 2018 @12:38PM (#17248613)
> He's assuming that dest is zero-initialized, which I believe it will be as written.

Given this snippet, incorrect. Stack allocated buffers aren't zeroed out as in the above declaration.

Re:Beware of strncpy and strncat by nebulous1 on Wednesday June 06, 2018 @12:42PM (#17248658)
Well, then that's pretty bad!