Git Product home page Git Product logo

safeclib's Introduction

Safe C Library - README

safeclib

Copying

This project's licensing restrictions are documented in the file 'COPYING' under the root directory of this release. Basically it's MIT licensed.

Overview

This library implements the secure C11 Annex K1 functions on top of most libc implementations, which are missing from them.

The ISO TR24731 Bounds Checking Interface documents indicate that the key motivation for the new specification is to help mitigate the ever increasing security attacks, specifically the buffer overrun.2

The rationale document says "Buffer overrun attacks continue to be a security problem. Roughly 10% of vulnerability reports cataloged by CERT from 01/01/2005 to 07/01/2005 involved buffer overflows. Preventing buffer overruns is the primary, but not the only, motivation for this technical report."3

The rationale document continues "that these only mitigate, that is lessen, security problems. When used properly, these functions decrease the danger buffer overrun attacks. Source code may remain vulnerable due to other bugs and security issues. The highest level of security is achieved by building in layers of security utilizing multiple strategies."3

The rationale document lists the following key points for TR24731:

  • Guard against overflowing a buffer
  • Do not produce unterminated strings
  • Do not unexpectedly truncate strings
  • Provide a library useful to existing code
  • Preserve the zero terminated string datatype
  • Only require local edits to programs
  • Library based solution
  • Support compile-time checking
  • Make failures obvious
  • Zero buffers, null strings
  • Runtime-constraint handler mechanism
  • Support re-entrant code
  • Consistent naming scheme
  • Have a uniform pattern for the function parameters and return type
  • Deference to existing technology

and the following can be added...

  • provide a library of functions with like behavior
  • provide a library of functions that promote and increase code safety and security
  • provide a library of functions that are efficient

The C11 Standard adopted many of these points, and added some secure _s variants in the Annex K. The Microsoft Windows/MINGW secure API did the same, but deviated in some functions from the standard. Besides Windows (with its msvcrt, ucrt, reactos msvcrt and wine msvcrt variants) only the unused stlport, Android's Bionic, Huawei securec and Embarcadero implemented this C11 secure Annex K API so far. They are still missing from glibc, musl, FreeBSD, darwin and DragonFly libc, OpenBSD libc, newlib, dietlibc, uClibc, minilibc.

Design Considerations

This library implements since 3.0 all functions defined in the specifications.4 Included in the library are extensions to the specification to provide a complementary set of functions with like behavior.

This library is meant to be used on top of all the existing libc's which miss the secure C11 functions. Of course tighter integration into the system libc would be better, esp. with the printf, scanf and IO functions. See the seperate libc-overview document.

Austin Group Review of ISO/IEC WDTR 24731 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1106.txt

C11 standard (ISO/IEC 9899:2011) http://en.cppreference.com/w/c

CERT C Secure Coding Standard5

Stackoverflow discussion: https://stackoverflow.com/questions/372980/do-you-use-the-tr-24731-safe-functions

DrDobbs review6 http://www.drdobbs.com/cpp/the-new-c-standard-explored/232901670

C17 reconsidered safeclib but looked only at the old incomplete Cisco version, not our complete and fixed version. http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm

  • Use of errno

The TR24731 specification says an implementation may set errno for the functions defined in the technical report, but is not required to. This library does not set errno in most functions, only in bsearch_s, fscanf_s, fwscanf_s, gets_s, gmtime_s, localtime_s, scanf_s, sscanf_s, swscanf_s, strtok_s, vfscanf_s, vfwscanf_s, vsscanf_s, vswscanf_s, wcstok_s, wscanf_s.

In most cases the safeclib extended ES* errors do not set errno, only when the underlying insecure system call fails, errno is set. The library does use errno return codes as required by functional APIs. Specific Safe C String and Safe C Memory errno codes are defined in the safe_errno.h file.

  • Runtime-constraints

Per the spec, the library verifies that the calling program does not violate the function's runtime-constraints. If a runtime-constraint is violated, the library calls the currently registered runtime-constraint handler.

Per the spec, multiple runtime-constraint violations in the same call to a library function result in only one call to the runtime-constraint handler. The first violation encountered invokes the runtime-constraint handler.

With --disable-constraint-handler calling the runtime-constraint handler can be disabled, saving some memory, but not much run-time performance.

With --with-default-handler=<abort|ignore> you may set the default constraint handler at compile-time to abort_handler_s or ignore_handler_s.

The runtime-constraint handler might not return. If the handler does return, the library function whose runtime-constraint was violated returns an indication of failure as given by the function’s return. With valid dest and dmax values, dest is cleared. With the optional --disable-null-slack only the first value of dest is cleared, otherwise the whole dest buffer.

rsize_t The specification defines a new type. This type, rsize_t, is conditionally defined in the safe_lib.h header file.

RSIZE_MAX The specification defines the macro RSIZE_MAX which expands to a value of type rsize_t. The specification uses RSIZE_MAX for both the string functions and the memory functions. This implementation defines two macros: RSIZE_MAX_STR and RSIZE_MAX_MEM. RSIZE_MAX_STR defines the range limit for the safe string functions. RSIZE_MAX_MEM defines the range limit for the safe memory functions. The point is that string limits can and should be different from memory limits. There also exist RSIZE_MAX_WSTR, RSIZE_MAX_MEM16, RSIZE_MAX_MEM32.

  • Compile-time constraints

With supporting compilers the dmax overflow checks and several more are performed at compile-time. Currently only since clang-5 with diagnose_if support. This checks similar to _FORTIFY_SOURCE=2 if the __builtin_object_size of the dest buffer is the same size as dmax, and errors if dmax is too big. With the optional --enable-warn-dmax it prints a warning if the sizes are different, which is esp. practical as compile-time warning. It can be promoted via the optional --enable-error-dmax to be fatal. On unsupported compilers, the overflow check and optional equality warn-dmax check is deferred to run-time. This check is only possible with __builtin_object_size and -O2 when the dest buffer size is known at compile-time, otherwise only the simplier dest == NULL, dmax == 0 and dmax > RSIZE_MAX checks are performed.

  • Header Files

The specification states the various functions would be added to existing Standard C header files: stdio.h, string.h, etc. This implementation separates the memory related functions into the safe_mem_lib.h header, the string related functions into the safe_str_lib.h header, and the rest into the safe_lib.h header. There are also the internal safe_compile.h, safe_config.h safe_lib_errno.h and safe_types.h headers, but they do not need to be included. You can also include all safec API's with <safec.h>.

The make file builds a single library libsafec-VERSION.a and .so. Built but not installed are also libmemprims, libsafeccore and libstdunsafe.

It is possible to split the make such that a separate safe_mem_lib.so and safe_str_lib.so are built. It is also possible to integrate the prototypes into the Standard C header files, but that may require changes to your development tool chain.

Userspace Library

The build system for the userspace library is the well known GNU build system, a.k.a. Autotools. This system is well understood and supported by many different platforms and distributions which should allow this library to be built on a wide variety of platforms. See the Tested platforms section for details on what platforms this library was tested on during its development.

  • Building

For those familiar with autotools you can probably skip this part. For those not and want to get right to building the code see below. And, for those that need additional information see the INSTALL file in the same directory.

To build you do the following:

./build-aux/autogen.sh
./configure
make

autogen.sh only needs to be run if you are building from the git repository. Optionally, you can do make check if you want to run the unit tests.

On Apple M1-M3 hardware I was told to use this:

./configure --disable-hardening CC="clang -arch arm64 -arch x86_64" \
  CXX="clang -arch arm64 -arch x86_64" CPP="clang -E" CXXCPP="clang -E"

This builds safeclib as a fat lib for macOS arm64 + X86-64 using clang.

  • Installing

Installation must be preformed by root, an Administrator on most systems. The following is used to install the library.

sudo make install

Safe Linux Kernel Module

The build for the kernel module has not been integrated into the autotools build infrastructure. Consequently, you have to run a different makefile to build the kernel module.

  • Building

.To build do the following:

./configure --disable-wchar
make -f Makefile.kernel

This assumes you are compiling on a Linux box and this makefile supports the standard kernel build system infrastructure documented in: /usr/src/linux-kernel/Documentation/kbuild/modules.txt

NOTE: If you build the kernel module then wish to build the userspace library or vice versa you will need to do a make clean otherwise a make check will fail to build.

  • Installing

The kernel module will be found at the root of the source tree called slkm.ko. The file testslkm.ko are the unit tests run on the userspace library but in Linux kernel module form to verify functionality within the kernel.

Tested Platforms

The library has been tested on the following systems:

  • Linux Fedora core 31 - 39 amd64/i386 glibc 2.28 - 2.38 (all gcc's + clang's)
  • Mac OS X 10.6-12 w/ Apple developer tools and macports (all gcc's + clang's)
  • Linux Debian 9 - 11 amd64/i386 glibc 2.24 - 2.28 (all gcc's + clang's)
  • Linux centos 7 amd64
  • Linux Void amd64 musl-1.1.16
  • x86_64-w64-mingw32 native and cross-compiled
  • i686-w64-mingw32 native, and cross-compiled and tested under wine
  • i386-mingw32 cross-compiled
  • cygwin32 gcc (newlib)
  • cygwin64 gcc -std=c99 (newlib)
  • freebsd 10 - 13 amd64
  • linux docker images under qemu: i386/debian, x86_64/rhel, arm32v7/debian, aarch64: arm64v8/{debian,centos,rhel,fedora}, s390x/fedora (the only big endian test I could find), ppc64le/{debian,ubuntu,fedora,centos,rhel}
  • User Mode Linux (UML), Linux kernel version v3.5.3 w/ Debian Squeeze rootfs

with most available compilers. See build-aux/smoke.sh and the various CI configs.

Known Issues

  1. If you are building the library from the git repository you will have to first run build-aux/autogen.sh which runs autoreconf to install the autotools files and create the configure script.

  2. If you use cmake, you'd need to add -DCMAKE_APPLE_SILICON_PROCESSOR=$(uname -m) for Apple Silicon M1 or M2 processors.

References

Footnotes

  1. C11 Standard (ISO/IEC 9899:2011) Annex K

  2. Programming languages, their environments and system software interfaces, Extensions to the C Library, Part I: Bounds-checking interfaces, ISO/IEC TR 24731-1.

  3. Rationale for TR 24731 Extensions to the C Library Part I: Bounds-checking interfaces, ISO/IEC JTC1 SC22 WG14 N1225. 2

  4. The Open Group Base Specifications Issue 7 http://pubs.opengroup.org/onlinepubs/9699919799/functions/contents.html

  5. CERT C Secure Coding Standard https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Secure+Coding+Standard

  6. DrDobbs review http://www.drdobbs.com/cpp/the-new-c-standard-explored/232901670

safeclib's People

Contributors

barracuda156 avatar bitkis avatar bossmc avatar ffontaine avatar jeremyhannon avatar juno-explorer avatar kilobyte avatar klimkin avatar mschulz-at-hilscher avatar rurban avatar sonicbison avatar xiche avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

safeclib's Issues

kernel module broken ?

Hello,

I tried to build the kernel module but it seems broken:

  • slkm_init.c includes ../safeclib/safeclib_private.h which is not found (this include should be replaced by safeclib_private.h and Kbuild should add -I$(src)/src in ccflags-y)
  • even with this update, time.h (and wchar.h) are included in most of the header files but are not available in kernel space.

Best Regards,

Fabrice

memcpy_s: clear destination buff if (smax > RSIZE_MAX_MEM) but dst and dstMax are valid.

I think I found a bug. From memcpy_s description in the standard:

If there is a runtime-constraint violation, the memcpy_s function stores zeros in the first
s1max characters of the object pointed to by s1 if s1 is not a null pointer and s1max is
not greater than RSIZE_MAX.

But in the current memcpy_s, the destination buffer is not set to zeros even if both the dst and dstMax are valid. This will require splitting apart these two max comparisons.

I haven't checked other functions for the same failure.

Missing MINGW64 secure API extensions

sec_api/string_s.h, sec_api/wchar_s.h

EXTERN errno_t _strset_s(char *_Dst,  size_t _DstSize,  int _Value);
EXTERN errno_t _strlwr_s(char *_Str, size_t _Size);
EXTERN errno_t _strnset_s(char *_Str, size_t _Size, int _Val, size_t _MaxCount);
EXTERN errno_t _strupr_s(char *_Str, size_t _Size);
EXTERN errno_t _wcserror_s(wchar_t *_Buf, size_t _SizeInWords, int _ErrNum);
EXTERN errno_t _wcsnset_s(wchar_t *_Dst, size_t _DstSizeInWords, wchar_t _Val, size_t _MaxCount);
EXTERN errno_t _wcsset_s(wchar_t *_Str, size_t _SizeInWords, wchar_t _Val);
EXTERN errno_t _wcslwr_s(wchar_t *_Str, size_t _SizeInWords);
EXTERN errno_t _wcsupr_s(wchar_t *_Str, size_t _Size);

we don't do the locale specific funcs

vsprintf_s() should return 0 for "vsprintf_s: len exceeds dmax"

According to standard:
If no runtime-constraint violation occurred, the vsprintf_s function returns the number of characters written in the array, not counting the terminating null character. If an encoding error occurred, vsprintf_s returns a negative value. If any other runtime-constraint violation occurred, vsprintf_s returns zero.

Scenario "vsprintf_s: len exceeds dmax" is run-time constraint violation & not encoding error so the return value should be 0.

Link to the code instance: https://github.com/rurban/safeclib/blob/master/src/str/vsprintf_s.c#L147

Why not to check runtime-constraints sort by priority?

Hi rurban,

According to your implementation, I wonder why not to check runtime-constraints sort by priority? Such as about strcpy_s, NULL pointer(ESNULLP) issue should be the highest priority, because that will cause process crashes, ESZEROL, ESLEMAX, ESOVRLP, or ESNOSPC should have lower priority, they just cause feature is not working. What do you think?

strcpy_s (char * restrict dest, rsize_t dmax, const char * restrict src)
{
.......
if (unlikely(dest == NULL)) {
.......
return RCNEGATE(ESNULLP);
}
if (unlikely(dmax == 0)) {
........
return RCNEGATE(ESZEROL);
}
if (unlikely(dmax > RSIZE_MAX_STR)) {
.......
}
if (unlikely(src == NULL)) {
......
return RCNEGATE(ESNULLP);
}
........
}

sscanf_s not honoring size token for %s/%c

Documentation notes that %s and %c must include both a pointer to buffer and a size of buffer as 2 parameters per token. However, the implementation appears to not handle this properly, and actually works the same as previous scanf, with just a pointer to buffer. If adding the size parameter, results in a crash.

fixes for seperate builddir, make distcheck

../../../src/safeclib_private.h:191:10: fatal error: 'safe_str_constraint.h' file not found. wrong -I

and several src files are missing in the dist: make distcheck

clean the docs: add missing wchar, remove doc/html from the dist. we only need the manpages.

safe_config.h: Enable --nullslack, memmax, strmax

It is defined in the sources, but we need to add a generated safe_config.h
with configured, user-definable overrides, esp. for the RSIZE_MAX_* values.

  • DISABLE_EXTS (not in C11, Annex K) (?, was --enable-exts)
  • RSIZE_MAX_MEM
  • RSIZE_MAX_STR
  • SAFECLIB_STR_NULL_SLACK
  • STRTOK_DELIM_MAX_LEN 16

Missing C11 functions

See https://rurban.github.io/safeclib/doc/safec-3.0/index.html for the current status.

str/wchar/multibyte:

  • vsnwprintf_s (done, but inherently unsafe)
  • vswprintf_s (done)
  • snwprintf_s (done, but inherently unsafe)
  • swprintf_s (done)
  • snprintf_s (added, but inherently unsafe)
  • vsnprintf_s (added, but inherently unsafe)
  • vsprintf_s (added)
  • vwprintf_s (done)
  • wprintf_s (done)
  • sscanf_s (done)
  • vsscanf_s (done)
  • wctomb_s (done)
  • wcstombs_s (done)
  • mbstowcs_s (done)
  • strerror_s (done)
  • strerrorlen_s (done)
  • fwscanf_s (done)
  • swscanf_s (done)
  • vfwscanf_s (done)
  • vswscanf_s (done)
  • vwscanf_s (done)
  • wscanf_s (done)
  • fwprintf_s (done)
  • vfwprintf_s (done)
  • wcscpy_s (done)
  • wcsncpy_s (done)
  • wcscat_s (done)
  • wcsncat_s (done)
  • wcstok_s (done)
  • wcsnlen_s (done)
  • wcrtomb_s (done)
  • mbsrtowcs_s (done)
  • wcsrtombs_s (done)

mem/byte:

  • wmemcpy_s (done)
  • wmemmove_s (done)

io: (all done)

  • fopen_s
  • fprintf_s
  • freopen_s
  • fscanf_s
  • scanf_s
  • vfscanf_s
  • printf_s
  • vfprintf_s
  • vprintf_s
  • gets_s

os:

  • getenv_s
  • asctime_s (done)
  • ctime_s (done)
  • gmtime_s
  • localtime_s

misc:

  • bsearch_s
  • qsort_s

unify error messages, save memory

Currently the error messages are customized to include the function name as prefix, and the argument name.
This is great for the user, but most likely memory overkill.
Normal libc's have a unique error message per number, stored globally only once. Not 100x times.

Measure the memory impact.
possibly convert to unified strings
or to fprintf("%s: %s message", __FUNCTION__, "dest"), so we can share at least the base message.

wcsfc_s: add reorder?

There can be decomposed char sequences which need to be reordered to be fully NFD and to be comparable to the same char sequence, which is already NFD.

Validation over dmax should be (dmax<= 0) instead of (dmax== 0)

The validation check which we are having for checking the destination size equal to zero should be updated to less than equal to zero for more robustness.
As standard claims that destination size should not be equal zero (which has implicit meaning that it shouldn't be less than zero too).

--disable-constraint-handler option

#undef the run-time invoke_safe_{str,mem}_constraint_handler function calls in safe_config.h
to avoid the large strings, as in FreeBSD.

Most of the OS/libc folks complain about it, but it is useful, similar to their malloc hooks. They are only used in the error cases.

`#define RCNEGATE(x) (-x)` for non-kernel usage?

For many/most of the functions, the C11AnnexK standard says that a negative value should be returned if there is a runtime constraint violation. However, it appears the safeclib functions are returning positive numbers if they're not being compiled for __KERNEL__. In every safeclib function the error return values are wrapped as RCNEGATE(x) and in safeclib_private.h the macro is #define RCNEGATE(x) (x) when __KERNEL__ is not defined.

So maybe you all can help me with some historical perspective. Is there an OS/platform that only accepts unsigned return values for error codes? Under what scenarios should the error values not be negative? The C11 Annex K functions are defined with int as the return type, so I would assume the negative version should always be returned.

split _chk and _unchecked at compile-time

with all or most arguments already checked at compile-time, we can split up the function into a fast unchecked worker (evtl. even using the unsafe variant without _s), and the slow _chk function, which does the preliminary arg checks of those args which are not known at compile-time. currently for clang-5+ only.
memcpy_s is 10% faster if all 4 args are known, and the _chk function can be bypassed.

evtl. using static_assert for gcc, which does not have diagnose_if.
we need both, diagnose_if from #40 to be able to test it, by changing error to warning via -DTEST_BOS and the tests-bos target.
we cannot test the static assertions via gcc.

gcc has several limitations over clang:

  • there's no constexpr, only const statements checks. so we cannot check the args, we can only check inside the _s function, which needs to be inlined then. or roll our own static_assert expr, usable for comma expressions, returning the checked value.
  • the object_size size is NOT known for choose_expr, the compile-time check if we can use the fast or slow function.
  • const or literal integer values are not checkable via constant_p, they throw errors.

Linking failure with GCC 7 (Retpoline vs. PIC)

When I try to compile libsafec-20180303 (or git master) on Fedora 27 on x86_64 with gcc-7.3.1-5, the build fails with the following linker error:

  CCLD     libsafec-3.3.la
/usr/bin/ld: mem/.libs/safe_mem_constraint.o: relocation R_X86_64_PC32 against undefined symbol `__x86_return_thunk' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
make[2]: *** [Makefile:1136: libsafec-3.3.la] Error 1

Reverting c86bd9e fixes the issue for me.

fixup man pages

The generated man pages, esp. the ones with mult. APIs in one file (wcsnorm_s.c) need to be improved.

  • remove the files (*_s.c.3), just one per API
  • cleanup mult., like wcsnorm_s
  1. either create a dynamic perl fixup script,
  2. or manually sync doc updates with static man pages.

sprintf non-'n' functions should return zero when runtime constraint violation

We’ve noticed there’s a difference in the C11AnnexK standard between the error return values for ‘n’ and non-n versions of the sprintf functions, but the safeclib implementations don’t make this distinction.

For sprintf:

Returns
If no runtime-constraint violation occurred, the sprintf_s function returns the number
of characters written in the array, not counting the terminating null character. If an
encoding error occurred, sprintf_s returns a negative value. If any other runtime constraint
violation occurred, sprintf_s returns zero.

For snprintf:

Returns
The snprintf_s function returns the number of characters that would have been
written had n been sufficiently large, not counting the terminating null character, or a
negative value if a runtime-constraint violation occurred. Thus, the null-terminated
output has been completely written if and only if the returned value is nonnegative and
less than n.

Note that for the ‘n’ versions, a negative value is returned for runtime constraint violations, whereas the non-n versions return zero. This difference doesn’t really make sense to us, but we found a 2005 document explaining at least the need for the non-'n' versions to return a negative value for encoding errors instead of always returning zero as in the older C standard.

We notice that all of the ‘printf’ implementations in safeclib use RCNEGATE() to return an error code, which never maps to zero, so the non-n versions aren’t following the spec because they’re not returning zero. So if everyone’s ok with it, we’ll submit a PR to clarify the runtime constraint error values returned by the sprintf family of functions per the standard. We have proposed a separate issue to ask why error values are being returned as positive numbers instead of negative numbers.

Although safe_lib_errno_.h already has #define EOK ( 0 ), I assume we'll have to overload zero and define another symbol like #define SPRINTF_CONS_VIOL ( 0 ) so at least when someone reads the code they can see the what type of error we're returning.

found by @Juno-Explorer

Move *cpy/move functions more towards C11/Windows

The current implementation of C11 Annex K functions deviate a bit from the spec and the windows sec_api, the windows sec_api is a bit more relaxed with empty slen arguments than safeclib.

Get away with the #ifdef HAVE_C11 logic. See also issue #31
Check all HAVE_C11 guards in the source and tests and make it the new default. This would make the library independent on using a C11 compiler for safeclib, and still follow the specs, but allow slen=0 shortcuts without failing.

In particular C11 sec_api allows empty slen to return immediately without dest check and cleaning.
Move that slen check upwards to mimic existing behavior.
The problem is with the footnote below A zero return value implies ... that the result in s1 is null terminated. With slen=0 alone it would not be guaranteed.

This would be the new slen check, the very first.
Following the specs, unlike windows sec_api:

    if (unlikely(slen == 0 && dest && dmax)) {
        *dest = '\0';
        return EOK;
    }

or variant 2, violating the spec footnote, following the windows sec_api.


    if (unlikely(slen == 0)) {
        if (dest && dmax)
            *dest = '\0';
        return EOK;
    }

e.g. the C11 standard says:

K.3.7.1.4 The strncpy_s function

Synopsis

#define __STDC_WANT_LIB_EXT1__ 1 
#include <string.h>
         errno_t strncpy_s(char * restrict s1,
              rsize_t s1max,
              const char * restrict s2,
              rsize_t n);

Runtime-constraints

  • Neither s1 nor s2 shall be a null pointer. Neither s1max nor n shall be greater than RSIZE_MAX. s1max shall not equal zero. If n is not less than s1max, then s1max shall be greater than strnlen_s(s2, s1max). Copying shall not take place between objects that overlap.
  • If there is a runtime-constraint violation, then if s1 is not a null pointer and s1max is greater than zero and not greater than RSIZE_MAX, then strncpy_s sets s1[0] to the null character.

Description

  • The strncpy_s function copies not more than n successive characters (characters that follow a null character are not copied) from the array pointed to by s2 to the array pointed to by s1. If no null character was copied from s2, then s1[n] is set to a null character.

All elements following the terminating null character (if any) written by strncpy_s in the array of s1max characters pointed to by s1 take unspecified values when strncpy_s returns.

Returns
The strncpy_s function returns zero if there was no runtime-constraint violation. Otherwise, a nonzero value is returned. - A zero return value implies that all of the requested characters from the string pointed to by s2 fit within the array pointed to by s1 and that the result in s1 is null terminated.

ie. there's no ESZERO violation with n=0

Also with dmax > MAX clean dest, at least dest[0] or strlen(dest), not the whole overlarge dmax.

$ git grep 'slen == 0'
src/extstr/strcasestr_s.c:    if (unlikely(slen == 0)) {
src/extstr/strcpyfld_s.c:    if (unlikely(slen == 0)) {
src/extstr/strcpyfldin_s.c:    if (unlikely(slen == 0)) {
src/extstr/strcpyfldout_s.c:    if (unlikely(slen == 0)) {
src/extstr/strcspn_s.c:    if (unlikely(slen == 0 )) {
src/extstr/strpbrk_s.c:    if (unlikely(slen == 0 )) {
src/extstr/strspn_s.c:    if (unlikely(slen == 0 )) {
src/extstr/strstr_s.c:    if (unlikely(slen == 0)) {
src/extwchar/wcslwr_s.c:    if (unlikely(slen == 0)) {
src/extwchar/wcsstr_s.c:    if (unlikely(slen == 0)) {
src/extwchar/wcsupr_s.c:    if (unlikely(slen == 0)) {
src/str/strncat_s.c:            if (unlikely(slen == 0)) {
src/str/strncat_s.c:            if (unlikely(slen == 0)) {
src/str/strncpy_s.c:    if (unlikely(slen == 0)) {
src/str/strncpy_s.c:        if (unlikely(slen == 0)) {
src/str/strncpy_s.c:            if (unlikely(slen == 0)) {
src/str/strtok_s.c:            if (unlikely(slen == 0)) {
src/str/strtok_s.c:            if (unlikely(slen == 0)) {
src/wchar/wcsncat_s.c:            if (unlikely(slen == 0)) {
src/wchar/wcsncat_s.c:            if (unlikely(slen == 0)) {
src/wchar/wcsncpy_s.c:    if (unlikely(slen == 0)) {
src/wchar/wcsncpy_s.c:            if (unlikely(slen == 0)) {
src/wchar/wcsncpy_s.c:      if (unlikely(slen == 0)) {
src/wchar/wcstok_s.c:            if (unlikely(slen == 0)) {
src/wchar/wcstok_s.c:            if (unlikely(slen == 0)) {

$ git grep HAVE_C11 src
src/extmem/memccpy_s.c:#ifdef HAVE_C11
src/extmem/memcpy16_s.c:#ifdef HAVE_C11
src/extmem/memcpy32_s.c:#ifdef HAVE_C11
src/extmem/memmove16_s.c:#ifdef HAVE_C11
src/extmem/memmove32_s.c:#ifdef HAVE_C11
src/extmem/memset16_s.c:#ifdef HAVE_C11
src/extmem/memset32_s.c:#ifdef HAVE_C11
src/extstr/strcpyfld_s.c:#ifdef HAVE_C11
src/extstr/strcpyfldin_s.c:#ifdef HAVE_C11
src/extstr/strcpyfldout_s.c:#ifdef HAVE_C11
src/extwchar/wcslwr_s.c:#ifdef HAVE_C11
src/extwchar/wcsupr_s.c:#ifdef HAVE_C11
src/mem/memcpy_s.c:#ifdef HAVE_C11
src/mem/memmove_s.c:#ifdef HAVE_C11
src/mem/memset_s.c:#if defined HAVE_MEMSET_S && defined HAVE_C11 && defined WANT_C11
src/mem/memset_s.c:#ifdef HAVE_C11

Add and fix cygwin support

cygwin32:

extstr/strcasestr_s.c: In function 'strcasestr_s':
extstr/strcasestr_s.c:134:13: error: array subscript has type 'char' [-Werror=char-subscripts]
             if (toupper(dest[i]) != toupper(src[i])) {
             ^

dynamic sprintf error messages, only when needed

Most error messages can be ignored, as they are only passed to the current constraint_handler, which is by default ignoring it. Only with --enable-debug the ignore_handler uses it.

Add a global bool has_safe_str_constraint_handler and skip processing the err_msg when it is empty.
With a handler create a new message with more info and more common strings, skipping the __FUNCTION__ prefix. This saves a lot of .cstring space, and we can print some wrong sizes.
The handler is only invoked in the error case.

Test if it's really worth.

/* has_safe_str_constraint_handler check mandatory on DEBUG */
#ifndef DEBUG
# define HAS_SAFE_STR_CONSTRAINT_HANDLER has_safe_str_constraint_handler
#else
# define HAS_SAFE_STR_CONSTRAINT_HANDLER 1
#endif

                if (HAS_SAFE_STR_CONSTRAINT_HANDLER)
                    sprintf(msg, "%s: overlapping objects", __FUNCTION__);
                handle_error(orig_dest, orig_dmax, msg, ESOVRLP);

unversion the .pc ??

remove the -3.4 from the .pc, to ease the integration into autotool projects. searching for safec. not safec-3.4, safec-3.3, safec-3.2 ...

See PR #56
versioning was added in the 2nd earliest commit after the sf fork, in 2012.
proper versioning helps in tighten the API to a specific version. "you only get what you asked for."
problem is that only the pkg-config .pc is versioned, (the lib anyway), but not the headers.
thus installing them in parallel will need to override the incdir and the -I path.

when adding the unversioned variant, name if libsafec.pc.

So there are four options:

  1. keep the versioned safe-*.pc asis.
  2. maintain two .pc's, a versioned and an unversioned. I'm leaning towards this one
  3. remove the versioned .pc (as done in PR #56).
  4. add versioned headers also, into a seperate safec-x.y path.

fix ax_valgrind_check.m4 for BSD make

ax_valgrind_check.m4 (from autoconf-archive) creates a GNUmakefile-only snippet.
fix it also in my autoconf-archive mirror: see https://github.com/rurban/autoconf-archive/commits/valgrind
For now you have to use gmake for clean and check-valgrind.

ifneq ($(LIBTOOL),)
valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute
else
valgrind_lt =
endif

and more ifeq.

ifeq ($(VALGRIND_ENABLED),yes)
ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes)
ifneq ($$(TESTS),)
else ifeq ($$(VALGRIND_ENABLED),yes)

and even a define/call/endef

The only portable solution I can think of is to use include files.
Fixed in the similar AX_CODE_COVERAGE like this: http://lists.gnu.org/archive/html/autoconf-archive-maintainers/2016-07/msg00001.html

function prototypes don't have `restrict` keyword despite AC_C_RESTRICT.

I'm just curious why the library function prototypes don't use the restrict keyword per the C11 Annex K library. I see in the configure.ac file that you're using AC_C_RESTRICT which gnu defines as:

If the C compiler recognizes the restrict keyword, don't do anything. If it recognizes only a variant spelling (__restrict, restrict, or _Restrict), then define restrict to that. Otherwise, define restrict to be empty. Thus, programs may simply use restrict as if every C compiler supported it; for those that do not, the makefile or configuration header defines it away.

So I assume there's no harm in inserting the restrict keyword for all of the char * pointers per the standard since it would either be removed or matched each compiler's keyword. I can make this change if you want.

For strncpy_s and strncat_s slen==0 should be valid.

For strncpy_s() and strncat_s() standard doesn’t speak about what to do when slen=0!! it only says that slen shouldn’t be greater than RSIZE_MAX.

I think slen = 0 should not be considered as runtime constraint violation and instead of ESZEROL we must return EOK and without modifying the destination buffer. I think strncat_s() misses this check.

memory functions: smax=0 should be valid

I see that all of the memory operations (memcpy_s, memmove_s, memset_s) check if smax == 0 and if so then handle it as a constraint violation. However, I can't find that requirement in the C11AnnexK standard. I agree, it's stupid for a user to request copying zero bytes but I think it's still a valid operation that should have a return value of EOK (zero) instead of ESZEROL. Please let me know if I missed something in the standard. A side benefit of removing this check would be a slight performance improvement in the normal non-zero case.

FYI @Juno-Explorer

--disable-extensions ?

Provide an option to strip all non-C11 Annex K functions?
Or an option to strip all 16/32 variants?

fix ../configure out-of-tree

error: cannot find input file: `src/Makefile.in'

# really severe build strictness also place generated object files (.o)
# into the same directory as their source files, in order to avoid
# collisions when non-recursive make is used.
AM_INIT_AUTOMAKE([1.10 no-define foreign subdir-objects dist-bzip2 dist-xz])

What is branch/tag strategy?

Looks like there are a few tags and branches.

If I want to pull something down and build/install on Linux, is there a particular branch or tag I should be using?

Is there documentation, if not I'd be happy to update the README!

Thanks!

don't use inclusion guards when including header files

inclusion guards (sentinels) should not be used outside of the header in which they are defined. And I think it's alright/good/expected that a header file could be #included multiple times in a chain of headers. That's why we have inclusion guards inside every header file to prevent actual multiple inclusion of the header contents. So I don't think the library should include code like the following.

#ifndef __SAFE_LIB_H__
  #include "safe_lib.h"
#endif

It should simply be #include "safe_lib.h" Inside the header file we already have:

#ifndef __SAFE_LIB_H__
#define __SAFE_LIB_H__
... file contents
#endif /* __SAFE_LIB_H__ */

Are you receiving a warning somewhere when you write the code like this? Maybe a warning that needs to be tuned/silenced?

Variance in name of parameters used in declaration & definition of functions

We were adding doxygen tags to SafeLib C file comments and came across variance in naming
of function parameters in function declaration & definition.

Please refer

While we're at it, would there be any issue if we resolve the differences.

Add compile-time object_size checks as with FORTIFY _chk

With newer compilers and -D_FORTIFY_SOURCE>=2 check the dmax sizes at compile-time against __builtin_object_size (=BOS) compiler builtins, similar to the glibc and Bionic FORTIFY _chk functions, with gcc and newer clang. Just at compile-time already, not at run-time.

Add compile-time errors and warnings, and run-time warnings on --enable-warn-dmax if the 2 sizes deviate.

With clang-7 even add compile-time warnings and errors, without clang-7/diagnose_if defer to run-time.
The compile-time warning is a -Wuser-defined-warnings, and may be suppressed or increased to -Werror.

  • error with "dmax exceeds dest" if dmax>BOS(dest). overflow even if dmax < RSIZE_MAX
  • warn with ESLEWRNG "Wrong dmax" if dmax!=BOS(dest) with --enable-warn-dmax or clang-7.

A fatalized ESLEWRNG does not clear dest.

This might catch more wrong dmax, smax args, when the object-size is known at compile-time.
It it does no harm writing them out explicitly at the user call-site, it's even better. Yes, glibc disagrees, for the additional arg pass-thru performance hit. It's one single register/stack write.
An object may suddenly be dynamically malloced, and then glibc turns insecure, we not.

e.g.

char *dest[80];
strcpy_s(dest, 79, "1");

=> "strcpy_s: wrong dmax 79, dest has size 80" warnings to the invoke_safe_str_constraint_handler, only visible if a handler is installed.

See https://android-developers.googleblog.com/2017/04/fortify-in-android.html
esp. the new diagnose_if https://clang.llvm.org/docs/AttributeReference.html#diagnose-if

fix headers in docs

don't link to safeclib_private.h in the html and esp. man pages.
grep private doc/man/man3/* should be empty.

getting compilation error for test_vfwscanf_s.c

Hi, I'm getting a compilation error in test_vfwscanf_s.c. Here is the error:

**test_vfwscanf_s.c: In function 'test_vfwscanf_s':
test_vfwscanf_s.c:70:10: error: incompatible type for argument 3 of 'vfwscanf_s'
rc = vfwscanf_s(f, NULL, NULL);
^
In file included from test_vfwscanf_s.c:10:0:
../include/safe_str_lib.h:526:1: note: expected 'va_list' but argument is of type 'void '
vfwscanf_s(FILE restrict stream,
^

From my understanding, the 3rd argument accepts is 'va_list', and it can't be null.. thoughts?

Doxygen tags for function header comments

We're using doxygen for auto-generating documentation and the safeclib C file comments already use /** which is a doxygen-compatible comment type so doxygen parses the comment. But doxygen throws a lot of warnings about the use of #include within the comments. Doxygen warning:

C:/proj/jci_base_libs/Private/Libraries/C11AnnexK/safeclib/memmove_s.c:43: warning: explicit link request to 'include' could not be resolved

I haven't seen this issue before so I'm not exactly sure how it should be resolved other than completely removing that comment line so that the 'synopsis' is simply the function prototype. (at that point, why not just remove the SYNOPSIS and NAME sections because they duplicate the function definition immediately below?)

While we're at it, would there be any issue if we added doxygen style tags like @brief, @param, @return to the function comment headers? The modified doxygen header comment for memmove_s would look like:

/**
 * @brief 
 *    The memmove_s function copies smax bytes from the region pointed to by 
 *    src into the region pointed to by dest. 
 * @details
 *    This copying takes place as if the smax bytes from the region pointed 
 *    to by src are first copied into a temporary array of smax bytes that does 
 *    not overlap the region pointed to by dest or src, and then the smax 
 *    bytes from the temporary array are copied into the object region to by 
 *    dest.
 *
 * @remark SPECIFIED IN
 *    ISO/IEC TR 24731, Programming languages, environments
 *    and system software interfaces, Extensions to the C Library,
 *    Part I: Bounds-checking interfaces
 *
 * @param dest[out] pointer to the memory that will be replaced by src.
 * @param dmax   maximum length of the resulting dest, in bytes
 * @param src    pointer to the memory that will be copied to dest
 * @param smax   maximum number bytes of src that can be copied
 *
 * @pre   Neither dest nor src shall be a null pointer.
 * @pre   Neither dmax nor smax shall be 0.
 * @pre   dmax shall not be greater than RSIZE_MAX_MEM.
 * @pre   smax shall not be greater than dmax.
 *
 * @return
 *    EOK        successful operation
 *    ESNULLP    NULL pointer
 *    ESZEROL    zero length
 *    ESLEMAX    length exceeds max limit
 *    If there is a runtime-constraint violation, the memmove_s function
 *      stores zeros in the first dmax characters of the region pointed to
 *      by dest if dest is not a null pointer and dmax is not greater
 *      than RSIZE_MAX_MEM.
 *
 * @relates
 *    memmove16_s(), memmove32_s(), memcpy_s(), memcpy16_s() memcpy32_s()
 *
 */
errno_t
memmove_s (void *dest, rsize_t dmax, const void *src, rsize_t smax)
{
   ...
}

so the doxygen html documentation would now look like:
image

whereas originally the generated documentation without tags looked like:
image

restore mingw

  • detect mingw and mingw cross (for testing with wine).
  • skip conflicting declarations with their non-C11 MINGW_HAS_SECURE_API:
    strtok_s, vsnprintf_s, wcstok_s
  • note their extensions, esp. their _l locale versions.
  • add dllimport/dllexport decl and cleanup the header mess in the sources.
./configure --enable-unsafe --host=x86_64-w64-mingw32
wchar/vswprintf_s.c:160: warning: passing arg 2 of `vswprintf' makes pointer from integer without a cast
wchar/vswprintf_s.c:160: warning: passing arg 3 of `vswprintf' from incompatible pointer type
wchar/vswprintf_s.c:160: error: too many arguments to function `vswprintf'

eliminate inclusion loops in safe_mem_lib.h and safe_str_lib.h

I'm concerned that there are inclusion loops. Specifically, safe_mem_lib.h and safe_str_lib.h both include safe_lib.h which then includes both of them. I assume this is because safe_lib.h defines rsize_t and the abort/ignore handlers. I would suggest that rsize_t be moved into safe_types.h and maybe the handlers need to be put in their own .c/.h files. That way, safe_mem_lib.h and safe_str_lib.h can include safe_types.h and the safe_handlers.h but that's it. Then safe_lib.h will include safe_str_lib.h and safe_mem_lib.h.

Any issue with that restructuring to eliminate inclusion loops?

Activate the tests, fix broken funcs

The tests actually do not return FAIL on any fails.
They only checked if crashing or not, but didn't promote any functional tests error, like wrong result or wrong error code.

In particular throw ESLEMAX when smax exceeds max, before the smax>dmax check (ESNOSPC).

Broken tests or functions:

  • sprintf_s (wrong tests)
  • snprintf_s (wrong tests)
  • memcpy_s (wrong ESNOSPC with smax)
  • memcpy16_s (wrong ESNOSPC with smax, wrong test)
  • memcpy32_s (wrong ESNOSPC with smax, wrong test)
  • t_memcmp_s (wrong ESNOSPC with smax)
  • t_memcmp16_s (wrong ESNOSPC with smax)
  • t_memcmp32_s (wrong ESNOSPC with smax)
  • t_memmove_s (wrong ESNOSPC with smax, wrong test)
  • t_memmove16_s (wrong ESNOSPC with smax, wrong test)
  • t_memmove32_s (wrong ESNOSPC with smax, wrong test)
  • t_memset_s (loose tests with C11)
  • t_strcpyfldout_s (wrong test)
  • t_strljustify_s (fix impl - original was ok, fix 2 test)

Improved C++ support

First of all, thank you for supporting the great library.

I'm trying to use safeclib in C++11 code. My test case is very simple

#include <libsafec/safe_str_lib.h>

int main()
{
}

With GCC 5.4.0 there are lots of similar errors due to usage of restrict keyword

safe_compile.h:38:45: error: conflicting declaration ‘void* restrict’
                                       void *restrict       /* ptr */,
                                             ^
safe_compile.h:37:51: note: previous declaration as ‘const char* restrict’
 typedef void (*constraint_handler_t) (const char *restrict /* msg */,
                                                   ^

Adding #define restrict __restrict before inclusion of the header does the trick.
The solution, however, is far from being elegant. Probably this can be handled somehow during configure.

With Clang 4.0.1 there is an additional issue with errors like this

safe_str_lib.h:106:19: error: no matching literal operator for call to 'operator""_XSTR' with arguments of types 'const char *' and 'unsigned long', and no matching literal operator template
    BOS_CHK(dest) BOS_NULL(src);
                  ^
safe_compile.h:178:56: note: expanded from macro 'BOS_NULL'
    __attribute__((diagnose_if(_BOS_NULL(buf), "empty "_XSTR(buf), bos_chk_err)))

Apparently there should be space between string literal and _XSTR() macro. Otherwise C++11 compiler will treat a whole expression as user-defined literal.
This one can be solved by a simple search/replace but still it's worth mentioning I think.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.