sprintf and vsprintf deprecation not effective in plain C and Objective-C files

Originator:mark
Number:rdar://FB11761475 Date Originated:2022-11-06
Status:Open Resolved:
Product:Developer Tools Product Version:Xcode 14.1 14B47b
Classification:Something else not on this list Reproducible:Always
 
Please describe the issue:
In the macOS 13 SDK shipping in Xcode 14.1, sprintf and vsprintf are deprecated. I applaud this decision.

From <stdio.h>:
187  #if !defined(_POSIX_C_SOURCE)
188  __deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
189  #endif
190  int      sprintf(char * __restrict, const char * __restrict, ...) __printflike(2, 3);

Unfortunately, the sprintf/vsprintf deprecation is only normally effective when compiling C++ and Objective-C++ source. It is not effective when compiling plain C or Objective-C files without C++.

mark@arm-and-hammer zsh% cat sprintf.c
#include <stdio.h>

void F(char* s) {
  sprintf(s, "");
}
mark@arm-and-hammer zsh% for l in sprintf.m sprintf.cc sprintf.mm; do ln -sf sprintf.c "${l}"; done
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.c -o sprintf.o
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.m -o sprintf.o
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.cc -o sprintf.o
sprintf.cc:4:3: error: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Werror,-Wdeprecated-declarations]
  sprintf(s, "");
  ^
[…]
1 error generated.
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.mm -o sprintf.o
sprintf.mm:4:3: error: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Werror,-Wdeprecated-declarations]
  sprintf(s, "");
  ^
[…]
1 error generated.

On identical source code, the deprecation warnings only appeared when building C++ and Objective-C++. The reason for this is that when compiling plain C or Objective-C, the sprintf declaration that’s been marked deprecated is not used. The bottom of <stdio.h> has:

414  #if defined (__GNUC__) && _FORTIFY_SOURCE > 0 && !defined (__cplusplus)
415  /* Security checking functions.  */
416  #include <secure/_stdio.h>
417  #endif

Bear in mind that _FORTIFY_SOURCE, unless explicitly defined by the user, is normally set to 2. From <_types.h>:

61  #ifndef _FORTIFY_SOURCE
62  #  if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && ((__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__-0) < 1050)
63  #    define _FORTIFY_SOURCE 0
64  #  else
65  #    define _FORTIFY_SOURCE 2   /* on by default */
66  #  endif
67  #endif

Therefore, when __cplusplus is not defined (it’s defined when compiling C++ or Objective-C++, but not plain C or Objective-C), <secure/_stdio.h> is #included.

<secure/_stdio.h> has:

45  #undef sprintf
46  #define sprintf(str, ...) \
47    __builtin___sprintf_chk (str, 0, __darwin_obsz(str), __VA_ARGS__)
48  #endif

The sprintf declared in <stdio.h> and marked deprecated is normally never used when compiling plain C or Objective-C. No deprecation warnings regarding sprintf will ever appear for those languages. Developers will not be warned to migrate away from this interface that’s difficult to use correctly.

As an exception, warnings appear when _FORTIFY_SOURCE is set to 0 when building plain C or Objective-C. This can happen either by a compiler -D option or a #define. _FORTIFY_SOURCE is also defined to 0 when Address Sanitizer is enabled, via the -fsanitize=address compiler option. So, strangely, although sprintf deprecation warnings do not appear when building plain C or Objective-C, they do appear under ASan.

mark@arm-and-hammer zsh% clang -Wall -Werror -fsanitize=address -c sprintf.c -o sprintf.o
sprintf.c:4:3: error: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Werror,-Wdeprecated-declarations]
  sprintf(s, "");
  ^
[…]
1 error generated.

The same applies to vsprintf.

macOS 13.0 22A380
Xcode 14.1 14B47b

We are tracking this at https://crbug.com/1381706.

Please list the steps you took to reproduce the issue:
The attached test program is named sprintf.c (plain C), and symbolic links are created so it can also be accessed as sprintf.m (Objective-C), sprintf.cc (C++), and sprintf.mm (Objective-C++).

Create the symbolic links:

mark@arm-and-hammer zsh% for l in sprintf.m sprintf.cc sprintf.mm; do ln -sf sprintf.c "${l}"; done

Now compile this code four times, once for each of the four languages:

mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.c -o sprintf.o
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.m -o sprintf.o
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.cc -o sprintf.o
mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.mm -o sprintf.o

What did you expect to happen?
Deprecation warnings should have appeared for all four compilations. The deprecation warnings should look as they do for the C++ (sprintf.cc) test case:

mark@arm-and-hammer zsh% clang -Wall -Werror -c sprintf.cc -o sprintf.o
sprintf.cc:4:3: error: 'sprintf' is deprecated: This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead. [-Werror,-Wdeprecated-declarations]
  sprintf(s, "");
  ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdio.h:188:1: note: 'sprintf' has been explicitly marked deprecated here
__deprecated_msg("This function is provided for compatibility reasons only.  Due to security concerns inherent in the design of sprintf(3), it is highly recommended that you use snprintf(3) instead.")
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h:215:48: note: expanded from macro '__deprecated_msg'
        #define __deprecated_msg(_msg) __attribute__((__deprecated__(_msg)))
                                                      ^
1 error generated.

What actually happened?
Deprecation warnings did appear for the C++ (sprintf.cc) and Objective-C++ (sprintf.mm) test cases, but did not appear for the plain C (sprintf.c) or Objective-C (sprintf.m) test cases. Instead, compilation proceeded without any warning that sprintf was deprecated.

Comments

base64 --decode | gunzip > sprintf.c

H4sIAD1ZaGMCA1POzEvOKU1JVbApLknJzNfLsOPiKsvPTFFw00jOSCzSUijWVKjmUlAoLijKzCtJ0yjWUVBS0rTmquUCAAIitqQ6AAAA


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!