2

I'm running OS X Sierra and trying to compile a c program that uses strcpy_s, but my installed clang compiler is using the c99 standard, but from what I've read strcpy_s requires c11.

Here's the code I'm trying to compile

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

int main(void)
{
    char source[] = "Test string";
    char destination[50];
    if(strcpy_s(destination, sizeof(destination), source))
        printf("string copied - %s",destination);

    return 0;
}

And here's the command I'm using to compile

$ clang copytest.c -o copytest
copytest.c:11:5: warning: implicit declaration of function 'strcpy_s' is invalid in C99 [-Wimplicit-function-declaration]
        if(strcpy_s(copied_string, sizeof(copied_string), source))
           ^
1 warning generated.
Undefined symbols for architecture x86_64:
  "_strcpy_s", referenced from:
      _main in copytest-e1e05a.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I've tried compiling with the standard flag...

clang -std=c11 copytest.c -o copytest

but I get the same exact "invalid in c99" warning. I've also tried compiling with gcc instead, and I still get the same c99 warning.

I tried upgrading via homebrew which shows the following

Warning: gcc 9.2.0 is already installed and up-to-date

I have clang version 9.0.0

$ clang -v
Apple LLVM version 9.0.0 (clang-900.0.39.2)

My xcode version is Xcode 9.2, which from everything I've read should come with c11 support.

Am I doing something wrong with the compiling, is my code itself incorrect? This is the only similar question I found on here, but it didn't even have an answer. Thanks

user1104854
  • 2,137
  • 12
  • 51
  • 74
  • 1
    Here https://en.cppreference.com/w/c/string/byte/strcpy is written that you need to `#define __STDC_WANT_LIB_EXT1__ 1` before `string.h` is included – fas Sep 12 '19 at 22:28
  • 1
    And I've found this https://stackoverflow.com/questions/47867130/stdc-lib-ext1-availability-in-gcc-and-clang, "safe" functions with the `_s` suffix are poorly supported among compilers. I've failed to run this on my mac – fas Sep 12 '19 at 22:31
  • OK, I've retracted my close vote. Here's a library that I recommend to others that provides Annex K: https://github.com/rurban/safeclib – S.S. Anne Sep 13 '19 at 00:36

2 Answers2

10

The _s functions are an optional component of the 2011 C standard (Annex K), and, to the best of my knowledge, they have never been implemented as an integrated part of any C library. Portable code cannot rely on their availability. (Microsoft's C compilers for Windows implement an overlapping set of functions with the same names but different semantics (and sometimes even a different argument list), and at least one bolt-on implementation does exist. See this old answer, and the much longer question and answer it links to, for more detail.)

Also, the _s functions do not solve the problem that they were intended to solve (unsafe string handling); it is necessary to put actual thought into a proper fix for each use of strcpy, instead of globally search-and-replacing strcpy with strcpy_s, etc., as was the hope of the authors of Annex K. If you do put appropriate amounts of thought into a proper fix, you won't need any of the _s functions to implement it. For instance, here's a fixed version of your example program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

int main(void)
{
    char source[] = "Test string";
    char destination[50];

    size_t srclen = strlen(source);
    if (srclen + 1 > sizeof destination) {
        fprintf(stderr, "string too long to copy - %zu bytes, need %zu\n",
                sizeof destination, srclen + 1);
        return 1;
    } else {
        memcpy(destination, source, srclen + 1);
        printf("string copied - %s\n", destination);
        return 0;
    }
}

And here's an even better version:

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "usage: ./test 'message of arbitrary length'\n");
        return 1;
    }
    char *destination = strdup(argv[1]);
    if (!destination) {
        perror("strdup");
        return 1;
    }
    printf("string copied - '%s'\n", destination);
    free(destination);
    return 0;
}

Therefore: Never use any of the _s functions. If you need to write a program that compiles on Windows with no warnings, put #define _CRT_SECURE_NO_WARNINGS 1 at the top of each file to make MSVC stop giving you bad advice.

zwol
  • 135,547
  • 38
  • 252
  • 361
1

If all, or even most, programmers wrote the suggested solutions above all the time, then these functions wouldn't be needed. We have a lot of evidence that many programmers do not write such careful code, going back to Spaf's notes on the Robert T Morris finger worm in the late 1980's.

You also would prefer not to have to duplicate 10 lines of code for every call site of strcpy. That leads to unreadable code. More so, what zwol suggests is really just an implementation of the function he claims we don't need. A good programmer would take that, stick it in a header, and name it something helpful, maybe checked_strcpy? Or even strcpy_s?

The second suggested implementation, which is purportedly better is not - it would cause an allocation when we might already have a buffer. Allocations are expensive, using this approach everywhere would be bad for perf. It also introduces new complexity because now we'd have to free every duplicated string - imagine doing that with repeated calls to strcat.

There is a fairly nicely done cross-platform implementation here: https://github.com/intel/safestringlib

I'm also not sure whether this is actually any different, but worth taking a look - https://github.com/coruus/safeclib

  • The whole point of my answer is that *nothing less than what I suggested will actually fix the bugs*. Using the _s functions just moves the bug around, and attempting to encapsulate the code I wrote in a function would *also* just move the bug around. You have to write 10 lines of code at each callsite because only in this way do you have sufficient information available to do something genuinely safe. With the abstractions available in C it's not possible to do better than this. – zwol Mar 27 '23 at 13:30