23

MSVC compiler says that fopen() is deprecated, and recommends the use of fopen_s().

Is there any way to use fopen_s() and still be portable?

Any ideas for a #define?

Josh Darnell
  • 11,304
  • 9
  • 38
  • 66
Shantia
  • 1,129
  • 2
  • 9
  • 9

6 Answers6

38

Microsoft's *_s functions are unportable, I usually use equivalent C89/C99 functions and disable deprecation warnings (#define _CRT_SECURE_NO_DEPRECATE).

If you insist, you can use an adaptor function (not necessarily a macro!) that delegates fopen() on platforms that don't have fopen_s(), but you must be careful to map values of errno_t return code from errno.

errno_t fopen_s(FILE **f, const char *name, const char *mode) {
    errno_t ret = 0;
    assert(f);
    *f = fopen(name, mode);
    /* Can't be sure about 1-to-1 mapping of errno and MS' errno_t */
    if (!*f)
        ret = errno;
    return ret;
}

However, I fail to see how fopen_s() is any more secure than fopen(), so I usually go for portability.

Alex B
  • 82,554
  • 44
  • 203
  • 280
  • 9
    Funny thing is, they are now part of C11 (albeit in the optional Annex K) – rubenvb Jul 31 '12 at 15:08
  • Much better than my (now former, not that I need it anymore) pure macro approach. Your function approach partially reproduces the fopen_s behavior on failure by returning errno (= EINVAL, i.e. 22, fwiw). You could also generate an *invalid parameter exception* to match fopen_s behavior even more closely. – riderBill Feb 04 '16 at 06:34
  • 2
    BTW, according to [this](http://en.cppreference.com/w/cpp/error/errno), **errno** "expands to a static modifiable lvalue of type int )until to C++11)", and "expands to a thread-local modifiable lvalue of type int (since C++11)." So your return type should be int. – riderBill Feb 04 '16 at 06:44
  • 2
    *Funny thing is, they are now part of C11* [Microsoft's versions are **not** part of Annex K](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm): "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 Jul 23 '19 at 15:11
10

In C/C++ code,

#ifdef __unix
#define fopen_s(pFile,filename,mode) ((*(pFile))=fopen((filename),(mode)))==NULL
#endif

In Makefile

CFLAGS += -D'fopen_s(pFile,filename,mode)=((*(pFile))=fopen((filename),(mode)))==NULL'

Attention that on success fopen_s return 0 while fopen return a nonzero file pointer. Therefore it is necessary to add "==NULL" to the end of macro, e.g.:

if (fopen_s(&pFile,filename,"r")) perror("cannot open file");
Jichao
  • 1,753
  • 1
  • 14
  • 11
8

if you are using C11, fopen_s is a standard library.

In gcc you need to use --std=c11 parameter.

vmemmap
  • 510
  • 4
  • 20
Raffaello
  • 1,641
  • 15
  • 29
  • 5
    This answer is wrong. `fopen_s()` and other functions in [Annex K of the C11 standard](https://port70.net/~nsz/c/c11/n1570.html#K) are optional per [**K.2 Scope**, paragraph 1](https://port70.net/~nsz/c/c11/n1570.html#K.2p1): "This annex specifies a series of optional extensions ..." Effectively, as of 2019 only Microsoft has implemented Annex K, and the Microsoft implementation ["cannot be considered conforming or portable"](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm). – Andrew Henle Jul 23 '19 at 15:41
  • @edj in fact is c11 not c++11 even the parameter to use in gcc. so your comment is not correct. – Raffaello Aug 26 '19 at 13:52
  • 1
    @AndrewHenle even if you might be right, the question is directly asking a GCC to use fopen_s. so it might not be the universal solution, but it fit the question, say is wrong is too restrictive, might not be complete. As a per C language, not neither C ANSI is probably 100% portable in some complex software, too low level. So here goes again: tradeoff. – Raffaello Aug 26 '19 at 13:57
  • 2
    @Raffaello GCC has never implemented Annex K, and never will. [Some reasons given as to why](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1106.txt): "Let's vote against that proposal. It's controversial, arguably leads to buggier software, and does not reflect the consensus of the community." – Andrew Henle Aug 26 '19 at 14:21
  • 1
    [There's also this](http://sourceware-org.1504.n7.nabble.com/Implement-C11-annex-K-td279910.html): "My view is that Annex K was a big mistake -- the latest iteration of the committee falling for the antics of a "sponsor" that has no interest in actually implementing the standard and who has done nothing but try to undermine the C language for the past 20+ years" – Andrew Henle Aug 26 '19 at 14:22
  • 1
    Also see https://stackoverflow.com/questions/50724726/why-didnt-gcc-implement-s-functions among many other questions and answers here that point out the problems with Annex K and its `*_s` "safe" functions. In short, they're non-portable and the **only** implementation in any real use - Microsoft's - is non-conforming anyway. – Andrew Henle Aug 26 '19 at 14:27
  • @Raffaello Is fopen_s supported in clang's library, eg will it run on say FreeBSD? – Jay Apr 20 '20 at 13:37
  • @Jay as reported already `fopen_s` is not portable or never fully implemented outside the Microsoft world neither in C11. So, unfortunately no, strictly in C is just use `fopen` available – Raffaello Apr 21 '20 at 15:17
3

Many of Microsoft's secure functions are included in Annex K of the C11 standard, but it is not widely supported, so portability is still an issue. There is a need for the improved security in some applications; maybe the support will improve in the future.

I the past, I did it like this:

  #define fopen_s(fp, fmt, mode)          *(fp)=fopen( (fmt), (mode))

The macro is simple and straight forward, good enough for something quick and dirty, but it doesn't provide the exception behavior of fopen_s, and it won't provide the security of the real fopen_s function.

@Alex B's function approach above partially reproduces the proper behavior on failure; he returns errno (= EINVAL). His approach could be extended further by generating an invalid parameter exception to more fully reproduce the behavior of fopen_s.

riderBill
  • 818
  • 9
  • 16
-1
#define fopen_s(fp, fmt, mode)  ({\
    *(fp)=fopen( (fmt), (mode));\
    (*(fp) ) ? 0:errno;\
})
Tim Wong
  • 19
  • 4
  • 4
    Thank you for this code snippet, which might provide some limited short-term help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its long-term value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Toby Speight Jul 23 '19 at 15:50
  • I just add "({...})" supported by GCC and Clang to return value based on the 2nd floor answer of @riderBill. Thank riderBill. – Tim Wong Aug 11 '19 at 15:31
-2

Accroding to https://en.cppreference.com/w/c/io/fopen it's possible to enable *_s functions on the standard library:

As with all bounds-checked functions, fopen_s is only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including <stdio.h>.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Bill Sun
  • 51
  • 5
  • 1
    As the quoted text says, this option is only available if the implementation defines `__STDC_LIB_EXT1__`. Most implementations don't. – Toby Speight Jul 23 '19 at 15:53