/* mpxrt-utils.c -*-C++-*- * ************************************************************************* * * @copyright * Copyright (C) 2014, Intel Corporation * All rights reserved. * * @copyright * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * @copyright * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ #define __STDC_FORMAT_MACROS #include "config.h" #include #include #include #include #include #include #include #include #include "mpxrt-utils.h" #ifndef HAVE_SECURE_GETENV #define secure_getenv __secure_getenv #endif #define MPX_RT_OUT "CHKP_RT_OUT_FILE" #define MPX_RT_ERR "CHKP_RT_ERR_FILE" #define MPX_RT_VERBOSE "CHKP_RT_VERBOSE" #define MPX_RT_VERBOSE_DEFAULT VERB_BR #define MPX_RT_MODE "CHKP_RT_MODE" #define MPX_RT_MODE_DEFAULT MPX_RT_COUNT #define MPX_RT_MODE_DEFAULT_STR "count" #define MPX_RT_STOP_HANDLER "CHKP_RT_STOP_HANDLER" #define MPX_RT_STOP_HANDLER_DEFAULT MPX_RT_STOP_HANDLER_ABORT #define MPX_RT_STOP_HANDLER_DEFAULT_STR "abort" #define MPX_RT_HELP "CHKP_RT_HELP" #define MPX_RT_ADDPID "CHKP_RT_ADDPID" #define MPX_RT_BNDPRESERVE "CHKP_RT_BNDPRESERVE" #define MPX_RT_BNDPRESERVE_DEFAULT 0 #define MPX_RT_PRINT_SUMMARY "CHKP_RT_PRINT_SUMMARY" #define MAX_FILE_NAME PATH_MAX typedef struct env_var_s { char *env_name; char *env_val; struct env_var_s *next; } env_var_t; typedef struct { env_var_t *first; env_var_t *last; } env_var_list_t; /* Following vars are initialized at process startup only and thus are considered to be thread safe. */ static int summary; static int add_pid; static mpx_rt_mode_t mode; static mpx_rt_stop_mode_handler_t stop_handler; static env_var_list_t env_var_list; static verbose_type verbose_val; static FILE *out; static FILE *err; static char out_name[MAX_FILE_NAME]; static char err_name[MAX_FILE_NAME]; /* Following vars are read at process finalization only. All write accesses use the same value and thus are considered to be thread safe. */ static int out_file_dirty; static int err_file_dirty; static int files_overwritten; /* Mutex used to sync output. */ static pthread_mutex_t lock; static void * malloc_check (size_t size) { void *res = malloc (size); if (!res) __mpxrt_print (VERB_ERROR, "Couldn't allocate %zu bytes.", size); else memset (res, 0, size); return res; } static void env_var_list_add (const char* env, const char* val) { env_var_t* n; if (val == 0) return; n = (env_var_t *)malloc_check (sizeof (env_var_t)); if (!n) return; if (env_var_list.first == 0) env_var_list.first = n; if (env_var_list.last) env_var_list.last->next = n; env_var_list.last = n; n->env_name = (char *)malloc_check (strlen (env) + 1); n->env_val = (char *)malloc_check (strlen (val) + 1); if (!n->env_name || !n->env_val) return; strcpy (n->env_name, env); strcpy (n->env_val, val); } static void set_file_stream (FILE** file, char* file_name, const char* env, FILE* deflt) { int pid; if (env != 0) { if (add_pid) { pid = getpid (); snprintf (file_name, MAX_FILE_NAME, "%s.%d", env, pid); } else snprintf (file_name, MAX_FILE_NAME, "%s", env); *file = fopen (file_name, "we"); if (*file != 0) return; } *file = deflt; } /* * this function will be called after fork in the child * open new files with pid of the process */ static void open_child_files () { char *out_env; char *err_env; out_env = secure_getenv (MPX_RT_OUT); err_env = secure_getenv (MPX_RT_ERR); if (add_pid == 0 && (out_env != 0 || err_env != 0)) { __mpxrt_print (VERB_ERROR, "MPX RUNTIME WARNING: out/err files are " "overwritten in new processes since %s was not set.\n", MPX_RT_ADDPID); files_overwritten = 1; } set_file_stream (&out, out_name, out_env, stdout); if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0)) set_file_stream (&err, err_name, err_env, stderr); else /* in case we get the same file name for err and out */ err = out; } /* * this function is called after fork in the parent */ static void at_fork_check (void) { char *out_env; char *err_env; out_env = secure_getenv (MPX_RT_OUT); err_env = secure_getenv (MPX_RT_ERR); if (add_pid == 0 && (out_env != 0 || err_env != 0)) files_overwritten = 1; } static mpx_rt_mode_t set_mpx_rt_mode (const char *env) { if (env == 0) return MPX_RT_MODE_DEFAULT; else if (strcmp (env, "stop") == 0) return MPX_RT_STOP; else if (strcmp (env,"count") == 0) return MPX_RT_COUNT; { __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values are" "[stop | count]\nUsing default value %s\n", env, MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR); return MPX_RT_MODE_DEFAULT; } } static mpx_rt_stop_mode_handler_t set_mpx_rt_stop_handler (const char *env) { if (env == 0) return MPX_RT_STOP_HANDLER_DEFAULT; else if (strcmp (env, "abort") == 0) return MPX_RT_STOP_HANDLER_ABORT; else if (strcmp (env, "exit") == 0) return MPX_RT_STOP_HANDLER_EXIT; { __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values are" "[abort | exit]\nUsing default value %s\n", env, MPX_RT_STOP_HANDLER, MPX_RT_STOP_HANDLER_DEFAULT); return MPX_RT_STOP_HANDLER_DEFAULT; } } static void print_help (void) { fprintf (out, "MPX Runtime environment variables help.\n"); fprintf (out, "%s \t set output file for info & debug [default: stdout]\n", MPX_RT_OUT); fprintf (out, "%s \t set output file for error [default: stderr]\n", MPX_RT_ERR); fprintf (out, "%s \t set verbosity type [default: %d]\n" "\t\t\t 0 - print only internal run time errors\n" "\t\t\t 1 - just print summary\n" "\t\t\t 2 - print summary and bound violation information\n " "\t\t\t 3 - print debug information\n", MPX_RT_VERBOSE, MPX_RT_VERBOSE_DEFAULT); fprintf (out, "%s \t\t set MPX runtime behavior on #BR exception." " [stop | count]\n" "\t\t\t [default: %s]\n", MPX_RT_MODE, MPX_RT_MODE_DEFAULT_STR); fprintf (out, "%s \t set the handler function MPX runtime will call\n" "\t\t\t on #BR exception when %s is set to \'stop\'." " [abort | exit]\n" "\t\t\t [default: %s]\n", MPX_RT_STOP_HANDLER, MPX_RT_MODE, MPX_RT_STOP_HANDLER_DEFAULT_STR); fprintf (out, "%s \t\t generate out,err file for each process.\n" "\t\t\t generated file will be MPX_RT_{OUT,ERR}_FILE.pid\n" "\t\t\t [default: no]\n", MPX_RT_ADDPID); fprintf (out, "%s \t set value for BNDPRESERVE bit.\n" "\t\t\t BNDPRESERVE = 0 flush bounds on unprefixed call/ret/jmp\n" "\t\t\t BNDPRESERVE = 1 do NOT flush bounds\n" "\t\t\t [default: %d]\n", MPX_RT_BNDPRESERVE, MPX_RT_BNDPRESERVE_DEFAULT); fprintf (out, "%s \t print summary at the end of the run\n" "\t\t\t [default: no]\n", MPX_RT_PRINT_SUMMARY); fprintf (out, "%s \t\t print this help and exit.\n" "\t\t\t [default: no]\n", MPX_RT_HELP); exit (0); } static void validate_bndpreserve (const char *env, int *bndpreserve) { if (env == 0) bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT; else if (strcmp (env, "0") == 0) *bndpreserve = 0; else if (strcmp (env, "1") == 0) *bndpreserve = 1; else { __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values " "are [0 | 1]\nUsing default value %d\n", env, MPX_RT_BNDPRESERVE, MPX_RT_BNDPRESERVE_DEFAULT); *bndpreserve = MPX_RT_BNDPRESERVE_DEFAULT; } } static verbose_type init_verbose_val (const char *env) { if (env == 0) return MPX_RT_VERBOSE_DEFAULT; else if (strcmp(env, "0") == 0) return VERB_ERROR; else if (strcmp(env, "1") == 0) return VERB_INFO; else if (strcmp(env, "2") == 0) return VERB_BR; else if (strcmp(env, "3") == 0) return VERB_DEBUG; __mpxrt_print (VERB_ERROR, "Illegal value '%s' for %s. Legal values " "are [0..3]\nUsing default value %d\n", env, MPX_RT_VERBOSE, (int)MPX_RT_VERBOSE_DEFAULT); return MPX_RT_VERBOSE_DEFAULT; } static void env_var_print_summary (void) { env_var_t* node; __mpxrt_print (VERB_DEBUG, "Used environment variables:\n"); node = env_var_list.first; while (node != 0) { __mpxrt_print (VERB_DEBUG, " %s = %s\n", node->env_name, node->env_val); node = node->next; } } /* Return 1 if passes env var value should enable feature. */ static int check_yes (const char *val) { return val && (!strcmp (val, "yes") || !strcmp (val, "1")); } void __mpxrt_init_env_vars (int* bndpreserve) { char *out_env; char *err_env; char *env; pthread_mutex_init (&lock, NULL); out_env = secure_getenv (MPX_RT_OUT); env_var_list_add (MPX_RT_OUT, out_env); err_env = secure_getenv (MPX_RT_ERR); env_var_list_add (MPX_RT_ERR, err_env); env = secure_getenv (MPX_RT_ADDPID); env_var_list_add (MPX_RT_ADDPID, env); add_pid = check_yes (env); set_file_stream (&out, out_name, out_env, stdout); if (out_env == 0 || err_env == 0 || (strcmp (out_env, err_env) != 0)) set_file_stream (&err, err_name, err_env, stderr); else /* in case we get the same file name for err and out */ err = out; env = secure_getenv (MPX_RT_VERBOSE); env_var_list_add (MPX_RT_VERBOSE, env); verbose_val = init_verbose_val (env); env = secure_getenv (MPX_RT_MODE); env_var_list_add (MPX_RT_MODE, env); mode = set_mpx_rt_mode (env); env = secure_getenv (MPX_RT_STOP_HANDLER); env_var_list_add (MPX_RT_STOP_HANDLER, env); stop_handler = set_mpx_rt_stop_handler (env); env = secure_getenv (MPX_RT_BNDPRESERVE); env_var_list_add (MPX_RT_BNDPRESERVE, env); validate_bndpreserve (env, bndpreserve); env = secure_getenv (MPX_RT_PRINT_SUMMARY); env_var_list_add (MPX_RT_PRINT_SUMMARY, env); summary = check_yes (env); env = secure_getenv (MPX_RT_HELP); if (check_yes (env)) print_help (); /* * at fork - create new files for output and err according * to the env vars. */ pthread_atfork (NULL, at_fork_check, open_child_files); env_var_print_summary (); } void __mpxrt_utils_free (void) { if (files_overwritten) __mpxrt_print (VERB_INFO, "\nMPX RUNTIME WARNING: out/err files are" " overwritten in new processes since %s was not set.\n", MPX_RT_ADDPID); if (out != stdout) { fclose (out); if (out_file_dirty != 1) remove (out_name); } if (err != stderr) { fclose (err); if (err_file_dirty != 1) remove (err_name); } pthread_mutex_destroy (&lock); } void __mpxrt_write_uint (verbose_type vt, uint64_t val, unsigned base) { static const char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; char str[65]; int pos = 64;; str[pos--] = 0; if (vt > verbose_val || base <= 1 || base > sizeof (digits)) return; if (val < base) str[pos--] = digits[val]; else while (val) { str[pos--] = digits[val % base]; val = val / base; } __mpxrt_write (vt, str + pos + 1); } void __mpxrt_write (verbose_type vt, const char* str) { va_list argp; FILE *print_to; if (vt > verbose_val) return; if (vt == VERB_ERROR) { print_to = err; err_file_dirty = 1; } else { print_to = out; out_file_dirty = 1; } pthread_mutex_lock (&lock); write (fileno (print_to), str, strlen (str)); pthread_mutex_unlock (&lock); va_end (argp); } void __mpxrt_print (verbose_type vt, const char* frmt, ...) { va_list argp; FILE *print_to; if (vt > verbose_val) return; va_start (argp, frmt); if (vt == VERB_ERROR) { print_to = err; err_file_dirty = 1; } else { print_to = out; out_file_dirty = 1; } pthread_mutex_lock (&lock); vfprintf (print_to, frmt, argp); fflush (print_to); pthread_mutex_unlock (&lock); va_end (argp); } mpx_rt_mode_t __mpxrt_mode (void) { return mode; } mpx_rt_mode_t __mpxrt_stop_handler (void) { return stop_handler; } void __attribute__ ((noreturn)) __mpxrt_stop (void) { if (__mpxrt_stop_handler () == MPX_RT_STOP_HANDLER_ABORT) abort (); else if (__mpxrt_stop_handler () == MPX_RT_STOP_HANDLER_EXIT) exit (255); __builtin_unreachable (); } void __mpxrt_print_summary (uint64_t num_brs, uint64_t l1_size) { if (summary == 0) return; out_file_dirty = 1; pthread_mutex_lock (&lock); fprintf (out, "MPX runtime summary:\n"); fprintf (out, " Number of bounds violations: %" PRIu64 ".\n", num_brs); fprintf (out, " Size of allocated L1: %" PRIu64 "B\n", l1_size); fflush (out); pthread_mutex_unlock (&lock); }