Make any execution limit configurable, add eval limit

Make any "deadman"-style execution limit configurable on the command
line (--limit-foo) or via a pragma (%pragma limit foo).

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
This commit is contained in:
H. Peter Anvin 2018-06-12 13:50:37 -07:00
parent d49e6dc08e
commit 987dc9c9db
10 changed files with 255 additions and 101 deletions

View File

@ -93,6 +93,7 @@ gprefix
gsuffix
lprefix
lsuffix
limit
; --- Pragma operations
subsections_via_symbols ; macho

View File

@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 1996-2017 The NASM Authors - All Rights Reserved
* Copyright 1996-2018 The NASM Authors - All Rights Reserved
* See the file AUTHORS included with the NASM distribution for
* the specific copyright holders.
*
@ -68,6 +68,7 @@ const struct warning warnings[ERR_WARN_ALL+1] = {
{"unknown-pragma", "unknown %pragma facility or directive", false},
{"not-my-pragma", "%pragma not applicable to this compilation", false},
{"unknown-warning", "unknown warning in -W/-w or warning directive", false},
{"negative-rep", "regative %rep count", true},
/* THIS ENTRY MUST COME LAST */
{"all", "all possible warnings", false}

View File

@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 1996-2017 The NASM Authors - All Rights Reserved
* Copyright 1996-2018 The NASM Authors - All Rights Reserved
* See the file AUTHORS included with the NASM distribution for
* the specific copyright holders.
*
@ -72,6 +72,7 @@ static void *scpriv;
static int *opflags;
static struct eval_hints *hint;
static int deadman;
/*
@ -769,6 +770,11 @@ static expr *expr6(int critical)
bool rn_warn;
const char *scope;
if (++deadman > nasm_limit[LIMIT_EVAL]) {
nasm_error(ERR_NONFATAL, "expression too long");
return NULL;
}
switch (i) {
case '-':
i = scan(scpriv, tokval);
@ -954,6 +960,8 @@ expr *evaluate(scanner sc, void *scprivate, struct tokenval *tv,
expr *e;
expr *f = NULL;
deadman = 0;
hint = hints;
if (hint)
hint->type = EAH_NOHINT;

View File

@ -82,6 +82,7 @@ static void nasm_verror_gnu(int severity, const char *fmt, va_list args);
static void nasm_verror_vc(int severity, const char *fmt, va_list args);
static void nasm_verror_common(int severity, const char *fmt, va_list args);
static void usage(void);
static void help(char xopt);
static bool using_debug_info, opt_verbose_info;
static const char *debug_format;
@ -153,6 +154,60 @@ static char *quote_for_pmake(const char *str);
static char *quote_for_wmake(const char *str);
static char *(*quote_for_make)(const char *) = quote_for_pmake;
/*
* Execution limits that can be set via a command-line option or %pragma
*/
#define LIMIT_MAX_VAL (INT_MAX >> 1) /* Effectively unlimited */
int nasm_limit[LIMIT_MAX+1] =
{ LIMIT_MAX_VAL, 1000, 1000000, 1000000, 1000000 };
struct limit_info {
const char *name;
const char *help;
};
static const struct limit_info limit_info[LIMIT_MAX+1] = {
{ "passes", "total number of passes" },
{ "stalled-passes", "number of passes without forward progress" },
{ "macro-levels", "levels of macro expansion"},
{ "rep", "%rep count" },
{ "eval", "expression evaluation descent"}
};
enum directive_result nasm_set_limit(const char *limit, const char *valstr)
{
int i;
int64_t val;
bool rn_error;
for (i = 0; i <= LIMIT_MAX; i++) {
if (!nasm_stricmp(limit, limit_info[i].name))
break;
}
if (i > LIMIT_MAX) {
nasm_error(ERR_WARNING|ERR_PASS1|ERR_WARN_UNKNOWN_PRAGMA,
"unknown limit: `%s'", limit);
return DIRR_ERROR;
}
if (!nasm_stricmp(valstr, "unlimited")) {
val = LIMIT_MAX_VAL;
} else {
val = readnum(valstr, &rn_error);
if (rn_error || val < 0) {
nasm_error(ERR_WARNING|ERR_PASS1|ERR_WARN_BAD_PRAGMA,
"invalid limit value: `%s'", limit);
return DIRR_ERROR;
} else if (val > LIMIT_MAX_VAL) {
val = LIMIT_MAX_VAL;
}
}
nasm_limit[i] = val;
return DIRR_OK;
}
int64_t switch_segment(int32_t segment)
{
location.segment = segment;
@ -717,11 +772,13 @@ static char *quote_for_wmake(const char *str)
enum text_options {
OPT_BOGUS,
OPT_VERSION,
OPT_HELP,
OPT_ABORT_ON_PANIC,
OPT_MANGLE,
OPT_INCLUDE,
OPT_PRAGMA,
OPT_BEFORE
OPT_BEFORE,
OPT_LIMIT
};
struct textargs {
const char *label;
@ -732,6 +789,7 @@ struct textargs {
static const struct textargs textopts[] = {
{"v", OPT_VERSION, false, 0},
{"version", OPT_VERSION, false, 0},
{"help", OPT_HELP, false, 0},
{"abort-on-panic", OPT_ABORT_ON_PANIC, false, 0},
{"prefix", OPT_MANGLE, true, LM_GPREFIX},
{"postfix", OPT_MANGLE, true, LM_GSUFFIX},
@ -742,6 +800,7 @@ static const struct textargs textopts[] = {
{"include", OPT_INCLUDE, true, 0},
{"pragma", OPT_PRAGMA, true, 0},
{"before", OPT_BEFORE, true, 0},
{"limit-", OPT_LIMIT, true, 0},
{NULL, OPT_BOGUS, false, 0}
};
@ -756,7 +815,6 @@ static bool stopoptions = false;
static bool process_arg(char *p, char *q, int pass)
{
char *param;
int i;
bool advance = false;
if (!p || !p[0])
@ -900,71 +958,7 @@ static bool process_arg(char *p, char *q, int pass)
break;
case 'h':
printf
("usage: nasm [-@ response file] [-o outfile] [-f format] "
"[-l listfile]\n"
" [options...] [--] filename\n"
" or nasm -v (or --v) for version info\n\n"
" -t assemble in SciTech TASM compatible mode\n");
printf
(" -E (or -e) preprocess only (writes output to stdout by default)\n"
" -a don't preprocess (assemble only)\n"
" -M generate Makefile dependencies on stdout\n"
" -MG d:o, missing files assumed generated\n"
" -MF file set Makefile dependency file\n"
" -MD file assemble and generate dependencies\n"
" -MT file dependency target name\n"
" -MQ file dependency target name (quoted)\n"
" -MP emit phony target\n\n"
" -Zfile redirect error messages to file\n"
" -s redirect error messages to stdout\n\n"
" -g generate debugging information\n\n"
" -F format select a debugging format\n\n"
" -gformat same as -g -F format\n\n"
" -o outfile write output to an outfile\n\n"
" -f format select an output format\n\n"
" -l listfile write listing to a listfile\n\n"
" -Ipath add a pathname to the include file path\n");
printf
(" -Olevel optimize opcodes, immediates and branch offsets\n"
" -O0 no optimization\n"
" -O1 minimal optimization\n"
" -Ox multipass optimization (default)\n"
" -Pfile pre-include a file (also --include)\n"
" -Dmacro[=str] pre-define a macro\n"
" -Umacro undefine a macro\n"
" -Xformat specifiy error reporting format (gnu or vc)\n"
" -w+foo enable warning foo (equiv. -Wfoo)\n"
" -w-foo disable warning foo (equiv. -Wno-foo)\n"
" -w[+-]error[=foo]\n"
" promote [specific] warnings to errors\n"
" -h show invocation summary and exit\n\n"
" --pragma str pre-executes a specific %%pragma\n"
" --before str add line (usually a preprocessor statement) before the input\n"
" --prefix str prepend the given string to all the given string\n"
" to all extern, common and global symbols\n"
" --suffix str append the given string to all the given string\n"
" to all extern, common and global symbols\n"
" --lprefix str prepend the given string to all other symbols\n"
"\n"
"Response files should contain command line parameters,\n"
"one per line.\n"
"\n"
"Warnings for the -W/-w options:\n");
for (i = 0; i <= ERR_WARN_ALL; i++)
printf(" %-23s %s%s\n",
warnings[i].name, warnings[i].help,
i == ERR_WARN_ALL ? "\n" :
warnings[i].enabled ? " (default on)" :
" (default off)");
if (p[2] == 'f') {
printf("valid output formats for -f are"
" (`*' denotes default):\n");
ofmt_list(ofmt, stdout);
} else {
printf("For a list of valid output formats, use -hf.\n");
printf("For a list of debug formats, use -f <form> -y.\n");
}
help(p[2]);
exit(0); /* never need usage message here */
break;
@ -1068,25 +1062,61 @@ static bool process_arg(char *p, char *q, int pass)
case '-':
{
const struct textargs *tx;
size_t olen, plen;
if (p[2] == 0) { /* -- => stop processing options */
p += 2;
if (!*p) { /* -- => stop processing options */
stopoptions = true;
break;
}
plen = strlen(p);
for (tx = textopts; tx->label; tx++) {
if (!nasm_stricmp(p + 2, tx->label))
break;
olen = strlen(tx->label);
if (olen > plen)
continue;
if (nasm_memicmp(p, tx->label, olen))
continue;
if (tx->label[olen-1] == '-')
break; /* Incomplete option */
if (!p[olen] || p[olen] == '=')
break; /* Complete option */
}
if (!tx->label) {
nasm_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
"unrecognized option `--%s'", p);
}
param = strchr(p+olen, '=');
if (param)
*param++ = '\0';
if (tx->need_arg) {
if (!q) {
if (!param) {
param = q;
advance = true;
}
/* Note: a null string is a valid parameter */
if (!param) {
nasm_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
"option `--%s' requires an argument",
p + 2);
p);
break;
}
advance = true;
} else {
if (param) {
nasm_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
"option `--%s' does not take an argument",
p);
}
}
switch (tx->opt) {
@ -1098,7 +1128,7 @@ static bool process_arg(char *p, char *q, int pass)
break;
case OPT_MANGLE:
if (pass == 2)
set_label_mangle(tx->pvt, q);
set_label_mangle(tx->pvt, param);
break;
case OPT_INCLUDE:
if (pass == 2)
@ -1106,16 +1136,19 @@ static bool process_arg(char *p, char *q, int pass)
break;
case OPT_PRAGMA:
if (pass == 2)
preproc->pre_command("pragma", q);
preproc->pre_command("pragma", param);
break;
case OPT_BEFORE:
if (pass == 2)
preproc->pre_command(NULL, q);
preproc->pre_command(NULL, param);
break;
case OPT_BOGUS:
nasm_error(ERR_NONFATAL | ERR_NOFILE | ERR_USAGE,
"unrecognized option `--%s'", p + 2);
case OPT_LIMIT:
if (pass == 2)
nasm_set_limit(p+olen, param);
break;
case OPT_HELP:
help(0);
exit(0);
default:
panic();
}
@ -1325,9 +1358,8 @@ static void assemble_file(const char *fname, StrList **depend_ptr)
char *line;
insn output_ins;
int i;
int pass_max;
uint64_t prev_offset_changed;
unsigned int stall_count = 0; /* Make sure we make forward progress... */
int stall_count = 0; /* Make sure we make forward progress... */
switch (cmd_sb) {
case 16:
@ -1348,7 +1380,7 @@ static void assemble_file(const char *fname, StrList **depend_ptr)
/* Any segment numbers allocated before this point are permanent */
seg_alloc_setup_done();
pass_max = prev_offset_changed = (INT_MAX >> 1) + 2; /* Almost unlimited */
prev_offset_changed = nasm_limit[LIMIT_PASSES];
for (passn = 1; pass0 <= 2; passn++) {
pass1 = pass0 == 2 ? 2 : 1; /* 1, 1, 1, ..., 1, 2 */
pass2 = passn > 1 ? 2 : 1; /* 1, 2, 2, ..., 2, 2 */
@ -1585,7 +1617,8 @@ static void assemble_file(const char *fname, StrList **depend_ptr)
if (terminate_after_phase)
break;
if ((stall_count > 997U) || (passn >= pass_max)) {
if ((stall_count > nasm_limit[LIMIT_STALLED]) ||
(passn >= nasm_limit[LIMIT_PASSES])) {
/* We get here if the labels don't converge
* Example: FOO equ FOO + 1
*/
@ -1844,3 +1877,88 @@ static void usage(void)
{
fputs("type `nasm -h' for help\n", error_file);
}
static void help(const char xopt)
{
int i;
printf
("usage: nasm [-@ response file] [-o outfile] [-f format] "
"[-l listfile]\n"
" [options...] [--] filename\n"
" or nasm -v (or --v) for version info\n\n"
"\n"
"Response files should contain command line parameters,\n"
"one per line.\n"
"\n"
" -t assemble in SciTech TASM compatible mode\n");
printf
(" -E (or -e) preprocess only (writes output to stdout by default)\n"
" -a don't preprocess (assemble only)\n"
" -M generate Makefile dependencies on stdout\n"
" -MG d:o, missing files assumed generated\n"
" -MF file set Makefile dependency file\n"
" -MD file assemble and generate dependencies\n"
" -MT file dependency target name\n"
" -MQ file dependency target name (quoted)\n"
" -MP emit phony target\n\n"
" -Zfile redirect error messages to file\n"
" -s redirect error messages to stdout\n\n"
" -g generate debugging information\n\n"
" -F format select a debugging format\n\n"
" -gformat same as -g -F format\n\n"
" -o outfile write output to an outfile\n\n"
" -f format select an output format\n\n"
" -l listfile write listing to a listfile\n\n"
" -Ipath add a pathname to the include file path\n");
printf
(" -Olevel optimize opcodes, immediates and branch offsets\n"
" -O0 no optimization\n"
" -O1 minimal optimization\n"
" -Ox multipass optimization (default)\n"
" -Pfile pre-include a file (also --include)\n"
" -Dmacro[=str] pre-define a macro\n"
" -Umacro undefine a macro\n"
" -Xformat specifiy error reporting format (gnu or vc)\n"
" -w+foo enable warning foo (equiv. -Wfoo)\n"
" -w-foo disable warning foo (equiv. -Wno-foo)\n"
" -w[+-]error[=foo]\n"
" promote [specific] warnings to errors\n"
" -h show invocation summary and exit\n\n"
" --pragma str pre-executes a specific %%pragma\n"
" --before str add line (usually a preprocessor statement) before the input\n"
" --prefix str prepend the given string to all the given string\n"
" to all extern, common and global symbols\n"
" --suffix str append the given string to all the given string\n"
" to all extern, common and global symbols\n"
" --lprefix str prepend the given string to all other symbols\n"
" --limit-X val set execution limit X\n");
for (i = 0; i <= LIMIT_MAX; i++) {
printf(" %-15s %s (default ",
limit_info[i].name, limit_info[i].help);
if (nasm_limit[i] < LIMIT_MAX_VAL) {
printf("%d)\n", nasm_limit[i]);
} else {
printf("unlimited)\n");
}
}
printf("\nWarnings for the -W/-w options:\n");
for (i = 0; i <= ERR_WARN_ALL; i++)
printf(" %-23s %s%s\n",
warnings[i].name, warnings[i].help,
i == ERR_WARN_ALL ? "\n" :
warnings[i].enabled ? " (default on)" :
" (default off)");
if (xopt == 'f') {
printf("valid output formats for -f are"
" (`*' denotes default):\n");
ofmt_list(ofmt, stdout);
} else {
printf("For a list of valid output formats, use -hf.\n");
printf("For a list of debug formats, use -f <format> -y.\n");
}
}

View File

@ -50,6 +50,7 @@
#include "error.h"
static enum directive_result asm_pragma(const struct pragma *pragma);
static enum directive_result limit_pragma(const struct pragma *pragma);
/*
* Handle [pragma] directives. [pragma] is generally produced by
@ -66,6 +67,7 @@ static enum directive_result asm_pragma(const struct pragma *pragma);
* so far none of these have any defined pragmas at all:
*
* preproc - preprocessor
* limit - limit setting
* asm - assembler
* list - listing generator
* file - generic file handling
@ -86,6 +88,7 @@ static enum directive_result asm_pragma(const struct pragma *pragma);
static struct pragma_facility global_pragmas[] =
{
{ "asm", asm_pragma },
{ "limit", limit_pragma },
{ "list", NULL },
{ "file", NULL },
{ "input", NULL },
@ -254,3 +257,8 @@ static enum directive_result asm_pragma(const struct pragma *pragma)
return DIRR_UNKNOWN;
}
}
static enum directive_result limit_pragma(const struct pragma *pragma)
{
return nasm_set_limit(pragma->opname, pragma->tail);
}

View File

@ -339,12 +339,6 @@ enum {
#define NO_DIRECTIVE_FOUND 0
#define DIRECTIVE_FOUND 1
/*
* This define sets the upper limit for smacro and recursive mmacro
* expansions
*/
#define DEADMAN_LIMIT (1 << 20)
/* max reps */
#define REP_LIMIT ((INT64_C(1) << 62))
@ -2875,8 +2869,8 @@ issue_error:
return DIRECTIVE_FOUND;
}
defining = nasm_zalloc(sizeof(MMacro));
defining->max_depth =
(i == PP_RMACRO) || (i == PP_IRMACRO) ? DEADMAN_LIMIT : 0;
defining->max_depth = ((i == PP_RMACRO) || (i == PP_IRMACRO))
? nasm_limit[LIMIT_MACROS] : 0;
defining->casesense = (i == PP_MACRO) || (i == PP_RMACRO);
if (!parse_mmacro_spec(tline, defining, pp_directives[i])) {
nasm_free(defining);
@ -3048,11 +3042,18 @@ issue_error:
return DIRECTIVE_FOUND;
}
count = reloc_value(evalresult);
if (count >= REP_LIMIT) {
nasm_error(ERR_NONFATAL, "`%%rep' value exceeds limit");
if (count > nasm_limit[LIMIT_REP]) {
nasm_error(ERR_NONFATAL,
"`%%rep' count %"PRId64" exceeds limit (currently %d)",
count, nasm_limit[LIMIT_REP]);
count = 0;
} else
} else if (count < 0) {
nasm_error(ERR_WARNING|ERR_PASS2|ERR_WARN_NEG_REP,
"negative `%%rep' count: %"PRId64, count);
count = 0;
} else {
count++;
}
} else {
nasm_error(ERR_NONFATAL, "`%%rep' expects a repeat count");
count = 0;
@ -4194,7 +4195,7 @@ static Token *expand_smacro(Token * tline)
Token *org_tline = tline;
Context *ctx;
const char *mname;
int deadman = DEADMAN_LIMIT;
int deadman = nasm_limit[LIMIT_MACROS];
bool expanded;
/*

View File

@ -112,9 +112,10 @@ static inline vefunc nasm_set_verror(vefunc ve)
#define ERR_WARN_UNKNOWN_PRAGMA WARN(18) /* unknown pragma */
#define ERR_WARN_NOTMY_PRAGMA WARN(19) /* pragma inapplicable */
#define ERR_WARN_UNK_WARNING WARN(20) /* unknown warning */
#define ERR_WARN_NEG_REP WARN(21) /* negative repeat count */
/* The "all" warning acts as a global switch, it must come last */
#define ERR_WARN_ALL 21 /* Do not use WARN() here */
#define ERR_WARN_ALL 22 /* Do not use WARN() here */
struct warning {
const char *name;

View File

@ -758,6 +758,22 @@ struct pragma {
enum directive opcode; /* Operation as a D_ directives constant */
};
/*
* These are semi-arbitrary limits to keep the assembler from going
* into a black hole on certain kinds of bugs. They can be overridden
* by command-line options or %pragma.
*/
enum nasm_limit {
LIMIT_PASSES,
LIMIT_STALLED,
LIMIT_MACROS,
LIMIT_REP,
LIMIT_EVAL
};
#define LIMIT_MAX LIMIT_EVAL
extern int nasm_limit[LIMIT_MAX+1];
extern enum directive_result nasm_set_limit(const char *, const char *);
/*
* The data structure defining an output format driver, and the
* interfaces to the functions therein.

View File

@ -180,7 +180,7 @@ size_t pure_func strnlen(const char *, size_t);
* Convert a string into a number, using NASM number rules. Sets
* `*error' to true if an error occurs, and false otherwise.
*/
int64_t readnum(char *str, bool *error);
int64_t readnum(const char *str, bool *error);
/*
* Convert a character constant into a number. Sets

View File

@ -65,9 +65,9 @@ static int radix_letter(char c)
}
}
int64_t readnum(char *str, bool *error)
int64_t readnum(const char *str, bool *error)
{
char *r = str, *q;
const char *r = str, *q;
int32_t pradix, sradix, radix;
int plen, slen, len;
uint64_t result, checklimit;