-
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests for stpncpy() security wrapper.
This adds three tests for the proper behavior of the stpncpy() security wrapper, both with and without explictly setting _FORTIFY_SOURCE. TESTED: Tested on 10.4-10.5 ppc, 10.4-10.6 i386, 10.5-10.6 ppc (i386 Rosetta), 10.5-12.x x86_64, 11.x-14.x arm64. Fails in expected cases without the corresponding fix, and passes in all cases with the fix.
- Loading branch information
Showing
4 changed files
with
232 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
/* | ||
* Copyright (c) 2024 Frederick H. G. Wright II <[email protected]> | ||
* | ||
* Permission to use, copy, modify, and distribute this software for any | ||
* purpose with or without fee is hereby granted, provided that the above | ||
* copyright notice and this permission notice appear in all copies. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
*/ | ||
|
||
/* | ||
* This provides test cases for the stpncpy() security wrapper. | ||
* There may or may not be compile-time warnings in some cases, but | ||
* the final pass/fail should be as expected. | ||
* | ||
* Informational messages regarding the relevant compile flags are also | ||
* included. | ||
*/ | ||
|
||
/* Allow deferring the stdio.h include */ | ||
int printf(const char *format, ...); | ||
|
||
#define PRINT_VAR(x) printf(" %s = %lld\n", #x, (long long) x) | ||
#define PRINT_UNDEF(x) printf(" " #x " is undefined\n") | ||
|
||
void | ||
print_before(void) | ||
{ | ||
printf(" Before string.h include:\n"); | ||
#ifdef _FORTIFY_SOURCE | ||
PRINT_VAR(_FORTIFY_SOURCE); | ||
#else | ||
PRINT_UNDEF(_FORTIFY_SOURCE); | ||
#endif | ||
#ifdef _USE_FORTIFY_LEVEL | ||
PRINT_VAR(_USE_FORTIFY_LEVEL); | ||
#else | ||
PRINT_UNDEF(_USE_FORTIFY_LEVEL); | ||
#endif | ||
} | ||
|
||
#include <string.h> | ||
|
||
void | ||
print_after(void) | ||
{ | ||
printf(" After string.h include:\n"); | ||
#ifdef _FORTIFY_SOURCE | ||
PRINT_VAR(_FORTIFY_SOURCE); | ||
#else | ||
PRINT_UNDEF(_FORTIFY_SOURCE); | ||
#endif | ||
#ifdef _USE_FORTIFY_LEVEL | ||
PRINT_VAR(_USE_FORTIFY_LEVEL); | ||
#else | ||
PRINT_UNDEF(_USE_FORTIFY_LEVEL); | ||
#endif | ||
} | ||
|
||
#if defined(_USE_FORTIFY_LEVEL) && _USE_FORTIFY_LEVEL > 0 | ||
#define CHECKS_ARE_ENABLED 1 | ||
#else | ||
#define CHECKS_ARE_ENABLED 0 | ||
#endif | ||
|
||
#include <assert.h> | ||
#include <libgen.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
#include <sys/wait.h> | ||
|
||
const char *test_str = "The Quick Brown Fox"; | ||
|
||
#define BUF_LEN 128 /* Generously longer than test_str */ | ||
static char dest[BUF_LEN]; | ||
|
||
/* Test stpncpy() known good at compile time */ | ||
void | ||
test_good_stpncpy_static(void) | ||
{ | ||
(void) stpncpy(dest, test_str, BUF_LEN); | ||
} | ||
|
||
/* Test stpncpy() known bad at compile time */ | ||
void | ||
test_bad_stpncpy_static(void) | ||
{ | ||
(void) stpncpy(dest, test_str, BUF_LEN+1); | ||
} | ||
|
||
/* Test stpncpy() known good as auto buf */ | ||
void | ||
test_good_stpncpy_auto(void) | ||
{ | ||
char buf[BUF_LEN]; | ||
|
||
(void) stpncpy(buf, test_str, BUF_LEN); | ||
} | ||
|
||
/* Test stpncpy() known bad as auto buf */ | ||
void | ||
test_bad_stpncpy_auto(void) | ||
{ | ||
char buf[BUF_LEN]; | ||
|
||
(void) stpncpy(buf, test_str, BUF_LEN+1); | ||
} | ||
|
||
/* | ||
* Some documentation suggests that the checks can work for cases where | ||
* the output buffer length isn't known at compile time. Empirically, | ||
* this does not seem to be the case, though perhaps there are tools which | ||
* can make this work for testing. | ||
* | ||
* Meanwhile, we include a couple of tests for this, but disable them. | ||
*/ | ||
|
||
/* Test stpncpy() known good as runtime malloc() */ | ||
void | ||
test_good_stpncpy_runtime(void) | ||
{ | ||
char *buf; | ||
|
||
buf = malloc(BUF_LEN); | ||
if (!buf) { | ||
perror("malloc() failed"); | ||
test_bad_stpncpy_static(); /* Try to provoke failure */ | ||
return; | ||
} | ||
(void) stpncpy(buf, test_str, BUF_LEN); | ||
free(buf); | ||
} | ||
|
||
/* Test stpncpy() known bad as runtime malloc() */ | ||
void | ||
test_bad_stpncpy_runtime(void) | ||
{ | ||
char *buf; | ||
|
||
buf = malloc(BUF_LEN); | ||
if (!buf) { | ||
perror("malloc() failed"); | ||
return; | ||
} | ||
(void) stpncpy(buf, test_str, BUF_LEN+1); | ||
free(buf); | ||
} | ||
|
||
typedef void test_func_t(void); | ||
|
||
/* Run test func as a subprocess, to capture abort() */ | ||
int | ||
run_test_func(test_func_t *func) | ||
{ | ||
pid_t child, done; | ||
int status; | ||
|
||
child = fork(); | ||
if (child < 0) { | ||
perror("fork() failed"); | ||
exit(100); | ||
} | ||
if (child == 0) { | ||
(*func)(); | ||
exit(0); | ||
} | ||
done = wait(&status); | ||
if (done != child) { | ||
fprintf(stderr, "Unexpected wait() pid, %d != %d\n", done, child); | ||
exit(110); | ||
} | ||
return status; | ||
} | ||
|
||
int | ||
main(int argc, char *argv[]) | ||
{ | ||
int have_compile_time_checks = CHECKS_ARE_ENABLED; | ||
int have_runtime_checks = 0; /* These don't currently work */ | ||
|
||
(void) argc; | ||
|
||
printf("Running %s\n", basename(argv[0])); | ||
print_before(); | ||
print_after(); | ||
/* Forking with unflushed buffers may produce duplicate output. */ | ||
fflush(NULL); | ||
|
||
if (have_compile_time_checks) { | ||
assert(run_test_func(&test_good_stpncpy_static) == 0); | ||
assert(run_test_func(&test_bad_stpncpy_static) != 0); | ||
assert(run_test_func(&test_good_stpncpy_auto) == 0); | ||
assert(run_test_func(&test_bad_stpncpy_auto) != 0); | ||
} | ||
if (have_runtime_checks) { | ||
assert(run_test_func(&test_good_stpncpy_runtime) == 0); | ||
assert(run_test_func(&test_bad_stpncpy_runtime) != 0); | ||
} | ||
|
||
printf("Tests pass\n"); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/* | ||
* Version of test_stpncpy_chk with enable forced to 1 (not the usual 2). | ||
*/ | ||
|
||
#define _FORTIFY_SOURCE 1 | ||
|
||
#include "test_stpncpy_chk.c" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* Version of test_stpncpy_chk with enable forced on. | ||
* | ||
* The security wrapper mechanism is unavailable on 10.4, available but | ||
* defaulted off in 10.5, and enabled and defaulted on in 10.6+. | ||
* Overriding the default here enables it on 10.5, with no effect on other | ||
* OS versions. | ||
*/ | ||
|
||
#define _FORTIFY_SOURCE 2 | ||
|
||
#include "test_stpncpy_chk.c" |