views:

1733

answers:

6

In the mold of a previous question I asked about the so-called safe library deprecations, I find myself similarly bemused as to why fopen() should be deprecated.

The function takes two C strings, and returns a FILE* ptr, or NULL on failure. Where are the thread-safety problems / string overrun problems? Or is it something else?

Thanks in advance

+1  A: 

The new versions do parameter validation whereas the old ones didn't.

See this SO thread for more information.

rein
What validation does fopen_s do that fopen can't? If you look at http://www.opengroup.org/onlinepubs/009695399/functions/fopen.html there are a long list of error codes defined. Other people have already mentioned that errno can (and perhaps is, even on MS) be implemented in a thread-safe matter.
Matthew Flaschen
A: 

Thread safety. fopen() uses a global variable, errno, while the fopen_s() replacement returns an errno_t and takes a FILE** argument to store the file pointer to.

laalto
`errno` could be thread local.
dalle
I believe the Microsoft C runtime uses a thread-local variable for errno, so thread-safety should not be an issue here.
Adam Rosenfield
Could but isn't in Microsoft CRT.
laalto
@laalto: errno isn't a global variable, every time you use it the compiler makes a secret call to a function that returns the address of errno. So it's most likely thread-safe.
Bastien Léonard
+28  A: 

You can use fopen(). Seriously, don't take any notice of Microsoft here, they're doing programmers a real disservice by deviating from the ISO standards. They seem to think that people writing code are somehow brain-dead and don't know how to check parameters before calling library functions.

If someone isn't willing to learn the intricacies of C programming, they really have no business doing it. They should move on to a safer language.

This is just another attempt at vendor lock-in by Microsoft of developers (although they're not the only ones who try it; I'm not specifically berating them). I usually add:

#define _CRT_SECURE_NO_WARNINGS

(or the "-D" variant) to most of my projects to ensure I'm not bothered by the compiler when writing perfectly valid, legal C code.

Microsoft has provided extra functionality in the fopen_s() function (file encodings, for one) as well as changing how things are returned. This may make it better for Windows programmers but makes the code inherently unportable.

You can tell from the prototype why they've changed this one. The fact that they're returning an errno and setting the file handle using double indirection almost certainly indicates that they think the real fopen() has a threading issue with the global errno:

errno_t fopen_s( 
    FILE** pFile,
    const char *filename,
    const char *mode 
);

Why they didn't just define errno as a function call (to return a thread-specific error) or a field in a thread-specific data structure like every other implementation is beyond me.

Aside: According to some comments, they do define errno to be thread-specific. This seems to indicate that the sole purpose of the "safe" version is the extra functionality.

If you're only ever going to code for Windows, by all means use it. I myself prefer the ability to compile and run my code anywhere (with as little change as possible).

paxdiablo
Totally agree. Use the standard one instead but know the limitations of what you're using.
rein
I really have to agree. They can do all they want to stop you from calling unsafe functions, but there's not much the can do to stop you from writing your own routine that runs past the end of a string. If you want to program in C, there are inherent complexities and insecurities that you have to be aware of. I think that MS would be much better pushing people to use languages like C# when it's the more appropriate language, than to try and change the way people write C.
Kibbee
I thought Microsoft _did_ #define errno to be a function call so that it's a thread-local variable? I'm not in front of a Windows box at the moment, so I can't verify that.
Adam Rosenfield
Well, they do (at least on VC++ 2005 SP1).
RaphaelSP
I think providing safer strcmp/strcpy etc. methods is a good idea. So this counts for MS. But they've tried to make safe methods safer (fopen_s, strncpy_s) which is totally insane and they ignored any standards/protability issues. So +1 for MS and -2 => -1 for MS
rstevens
+1  A: 

Or is it something else?

Some implementations of the FILE structure used by 'fopen' has the file descriptor defined as 'unsigned short'. This leaves you with a maximum of 255 simultaneously open files, minus stdin, stdout, and stderr.

While the value of being able to have 255 open files is debatable, of course, this implementation detail materializes on the Solaris 8 platform when you have more than 252 socket connections! What first appeared as a seemingly random failure to establish an SSL connection using libcurl in my application turned out to be caused by this, but it took deploying debug versions of libcurl and openssl and stepping the customer through debugger script to finally figure it out.

While it's not entirely the fault of 'fopen', one can see the virtues of throwing off the shackles of old interfaces; the choice to deprecate might be based on the pain of maintaining binary compatibility with an antiquated implementation.

veefu
You're point is perfectly reasonable, but there is a new C standard in the works. Microsoft should be putting their efforts behind improving that (not that they will).
Dana the Sane
@Dana: how is this related to the standard? The only restriction I see is that FOPEN_MAX must be at least 8.
Bastien Léonard
+7  A: 

There is an official ISO/IEC JTC1/SC22/WG14 (C Language) technical report TR24731-1 (bounds checking interfaces) and its rationale available at:

There is also work towards TR24731-2 (dynamic allocation functions).

The stated rationale for fopen_s() is:

6.5.2 File access functions

When creating a file, the fopen_s and freopen_s functions improve security by protecting the file from unauthorized access by setting its file protection and opening the file with exclusive access.

The specification says:

6.5.2.1 The fopen_s function

Synopsis

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
errno_t fopen_s(FILE * restrict * restrict streamptr,
                const char * restrict filename,
                const char * restrict mode);

Runtime-constraints

None of streamptr, filename, or mode shall be a null pointer.

If there is a runtime-constraint violation, fopen_s does not attempt to open a file. Furthermore, if streamptr is not a null pointer, fopen_s sets *streamptr to the null pointer.

Description

The fopen_s function opens the file whose name is the string pointed to by filename, and associates a stream with it.

The mode string shall be as described for fopen, with the addition that modes starting with the character ’w’ or ’a’ may be preceded by the character ’u’, see below:

  • uw truncate to zero length or create text file for writing, default permissions
  • ua append; open or create text file for writing at end-of-file, default permissions
  • uwb truncate to zero length or create binary file for writing, default permissions
  • uab append; open or create binary file for writing at end-of-file, default permissions
  • uw+ truncate to zero length or create text file for update, default permissions
  • ua+ append; open or create text file for update, writing at end-of-file, default permissions
  • uw+b or uwb+ truncate to zero length or create binary file for update, default permissions
  • ua+b or uab+ append; open or create binary file for update, writing at end-of-file, default permissions

To the extent that the underlying system supports the concepts, files opened for writing shall be opened with exclusive (also known as non-shared) access. If the file is being created, and the first character of the mode string is not ’u’, to the extent that the underlying system supports it, the file shall have a file permission that prevents other users on the system from accessing the file. If the file is being created and first character of the mode string is ’u’, then by the time the file has been closed, it shall have the system default file access permissions10).

If the file was opened successfully, then the pointer to FILE pointed to by streamptr will be set to the pointer to the object controlling the opened file. Otherwise, the pointer to FILE pointed to by streamptr will be set to a null pointer.

Returns

The fopen_s function returns zero if it opened the file. If it did not open the file or if there was a runtime-constraint violation, fopen_s returns a non-zero value.

10) These are the same permissions that the file would have been created with by fopen.

Jonathan Leffler
+2  A: 

The fopen_s() function has been added by Microsoft to the C runtime with the following fundamental differences from fopen():

  • if the file is opened for writing ("w" or "a" specified in the mode) then the file is opened for exclusive (non-shared) access (if the platform supports it).
  • if the "u" specifier is used in the mode argument with the "w" or "a" specifiers, then by the time the file is closed, it will have system default permissions for others users to access the file (which may be no access if that's the system default).
  • if the "u" specified is not used in those cases, then when the file is closed (or before) the permissions for the file will be set such that other users will not have access to the file.

Essentially it means that files the application writes are protected from other users by default.

They did not do this to fopen() due to the likelyhood that existing code would break.

Microsoft has chosen to deprecate fopen() to encourage developers for Windows to make conscious decisions about whether the files their applications use will have loose permissions or not.

Jonathan Leffler's answer provides the proposed standardization language for fopen_s(). I added this answer hoping to make clear the rationale.

Michael Burr