2

I got to know about the function - strncpy_s(), which is called the secure version of string copy. More secure than the functions - strcpy() and strncpy().

strncpy_s() is described more at this link - https://en.cppreference.com/w/c/string/byte/strncpy

I was thinking of using this function - strncpy_s() in my codebase to handle all the possible scenarios wherein its older siblings generally fail to handle - like when srcString is lengthier than destString. Or if srcString is not NULL terminated.

So I was wondering should the usage of strncpy_s() be -

strncpy_s(destString, sizeof(destString), srcString, (sizeof(srcString)>sizeof(destString)?(sizeof(destString)-1):(sizeof(srcString)-1))); - [1]

to handle all the possible scenarios gracefully - ie

  1. when the srcString is greater than destString, then truncate the srcString to the length destString.
  2. when the destString is greater than srcString, then copy the entire content of srcString to destString with NULL termination.
  3. when both srcString and destString are of same length, then copy the entire content of srcString to destString with NULL termination.
  4. when srcString is not NULL terminated. If the srcString is smaller than destString then copy the one shy of the content of srcString to the destString with NULL termination. If destString is smaller than the srcString then copy the content from srcString of the size of destString.

Can anyone think that the above mentioned usage of strncpy_s() [1] could fail in any scenario, which I am not able to think of?

Edit: I have updated the action taken in the scenario - (2), (3) and (4)

Darshan L
  • 824
  • 8
  • 30
  • `I was wondering should the usage of strncpy_s() sizeof(destString` if you use a compile time `sizeof`, you might as well write a `static_assert`. – KamilCuk Aug 09 '20 at 22:29
  • @kamilCuk Could you please elaborate your idea? I couldn't grasp it – Darshan L Aug 10 '20 at 03:07
  • *More secure than the functions - strcpy() and strncpy()* Not really. They're effectively non-portable versions that lock your code to Microsoft, **without** being more secure. [**Field Experience With Annex K — Bounds Checking Interfaces**](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm): "A widespread fallacy originated by Microsoft's deprecation of the standard functions in an effort to increase the adoption of the APIs is that every call to the standard functions is necessarily unsafe and should be replaced by one to the "safer" API." – Andrew Henle Aug 10 '20 at 09:35
  • (cont) "Microsoft Visual Studio implements an early version of the APIs. However, the implementation is incomplete and conforms neither to C11 nor to the original TR 24731-1. ... As a result of the numerous deviations from the specification the Microsoft implementation cannot be considered conforming or portable." – Andrew Henle Aug 10 '20 at 09:36

2 Answers2

3

You should not use strncpy_s at all; it is one of the Annex K functions, which have never been implemented as an integrated part of any C library (Microsoft's compilers implement an overlapping set of functions with similar names and different semantics) and are currently being considered for removal from the standard. (For more information on this, see this old answer and the various documents it links to.)

You should instead write your own function that uses a combination of strlen, memmove, and discrete logic to implement exactly the behavior you want. You say you want the string to be truncated when the destination buffer is too small. You don't say what you want to happen when the destination buffer is at least big enough, but the obvious thing to do is copy the whole string and stop. And you also don't say what you want to happen when srcString is not NUL-terminated, but the correct thing to do in that case is to crash (see this old post from The Old New Thing about why — sorry about the absence of paragraph breaks, look for "You should crash" somewhere in the middle). Therefore:

void safe_strncpy(char *dest, size_t destsz, char *src)
{
    size_t srcsz = strlen(src);  // crash here if not nul-terminated
    if (srcsz > destsz - 1)
        srcsz = destsz - 1;
    memmove(dest, src, srcsz);   // memmove is safe if dest and src overlap
    dest[srcsz] = '\0';
}

Call just like you would call strcpy, but also providing the size of the destination buffer. Keep in mind that the size of the destination buffer is not the size of any string it may happen to have contained earlier in the program. The size of the destination buffer is the size you declared it with (if a variable) or the size you passed to malloc (if a heap allocation).

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 2
    Or in other words: A language which forces its users to SO for the most basic tasks imaginable is unusable for serious software development. – Peter - Reinstate Monica Aug 09 '20 at 22:35
  • @Peter-ReinstateMonica I don't understand your point. Is your point that people shouldn't answer question on SO? Is your point that the presented solution is wrong? Is your point that [tag:c] is "not usable for serious software development"? Funny how a lot of critical systems run successfully on [tag:C]. It's like... there are competent programmers in this world... and ... best practices ... and ... code review and ... unit testing, integration testing, system testing and acceptance testing are a thing. – bolov Aug 09 '20 at 22:48
  • @bolov No, people should certainly post code. But such code gets used in ways and places one didn't think of, so it better be good. See e.g. https://dl.acm.org/doi/10.1109/MSR.2019.00050, or https://stackoverflow.blog/2019/11/26/copying-code-from-stack-overflow-you-might-be-spreading-security-vulnerabilities/. And yes, obviously I think C is not a good programming language by today's standards. If you need to write your own string copy routine you are using the wrong tool. – Peter - Reinstate Monica Aug 09 '20 at 23:04
  • @Peter-ReinstateMonica I still don't understand your 1st point. If you see a problem with the code then point the error, otherwise "it better be good" is ... a questionable thing to say. You have the code, it's simple, very few lines. It's there in the answer. If you think it's wrong you should be specific about what exactly you think is the bug, otherwise there is no point in saying "it better be good". As for your 2nd point, I don't agree but you are entitled to your opinion. – bolov Aug 09 '20 at 23:10
  • @Peter-ReinstateMonica yeah, that's still not relevant to this particular answer. You can talk about these issues on meta.stackoverflow.com . For this answer there is no point: what should the answerer do in light of your raised issues: write commercial-grade unit testing for the answer? Add a disclaimer? Delete its answer? – bolov Aug 09 '20 at 23:36
  • @bolov Yes, a disclaimer, or an information "this has been used for 20 years in program x" or "this is what openssl did to fix heartbleed", or "it's part of my github portfolio where you can find the unit tests", or "I pulled it from glibc". – Peter - Reinstate Monica Aug 10 '20 at 00:20
  • @Peter-ReinstateMonica I think the burden of thoroughly testing the code falls on the persons using the code. I always see answers (both ones I give and the ones I use) as a starting point. It's the user's responsibility how it's used. A snippet of code posted here can be used in a hobby at-home program that's never distributed or in critical software. You can't apply the same standard to both. If I post code here it's not my responsibility if my code crashed your super-duper-uber critical software because I am not responsible in any way for your software. – bolov Aug 10 '20 at 01:40
  • @Peter-ReinstateMonica That being said I do know the problem of mindlessly copy-pasting code from SO without understanding it into production code exists. At my former company they called it "copy-paste no creativity". But that is really a problem of the other side of this site. Here *reasonably confident the code is correct* is more than an enough standard. I see no reason why I should bet my life the code here is correct, because here we are not writing critical software, we are giving free answers to people on the internet asking for help. – bolov Aug 10 '20 at 01:44
0

I strongly agree with @zwol about using not using strncpy_s().


I would like to offer alternative code to meet OP's objectives.

  1. when the srcString is greater than destString, then truncate the srcString to the length destString.

I'd take to to also not to inspect srcString data beyond destsz elements. This has the side effect of not requiring a null character in srcString beyond destsz.

  1. when the destString is greater than srcString.
  2. when both srcString and destString are of same length.
  3. when srcString is not NULL null character terminated.

In particular, return a clear indication of trouble in the return value.
In all cases when dest != NULL and dest > 0, dest will point to a string with a null chracter.

Below code not only addresses for OP's concerns, but also handle cases when buffers overlap by using memmove().

// Return error flag
bool sf_strncpy(char *dest, size_t destsz, const char *src) {
  // Optional pointer test
  if (dest == NULL) return true;

  // Size test
  if (destsz == 0) return true;

  // Optional pointer test
  if (src == NULL) ( dest[0] = '\0'; return true; }

  // Look for null character, but not beyond destsz
  const char *end = memchr(src, '\0', destsz);
  if (end == NULL) {
    memmove(dest, src, destsz - 1);
    dest[destsz - 1] = '\0';
    return true;
  }
  memmove(dest, src, end - src);
  return false;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256