diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2018-06-12 13:50:37 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2018-06-12 13:54:42 -0700 |
commit | 987dc9c9dbe36601789aae4552a7c0ceb1ca0852 (patch) | |
tree | 75eb0e85bd86584ff82263de70c32a8781bdb643 | |
parent | d49e6dc08e5625ea393cfc7e17a99f66f41840e3 (diff) | |
download | nasm-987dc9c9dbe36601789aae4552a7c0ceb1ca0852.tar.gz |
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>
-rw-r--r-- | asm/directiv.dat | 1 | ||||
-rw-r--r-- | asm/error.c | 3 | ||||
-rw-r--r-- | asm/eval.c | 10 | ||||
-rw-r--r-- | asm/nasm.c | 284 | ||||
-rw-r--r-- | asm/pragma.c | 8 | ||||
-rw-r--r-- | asm/preproc.c | 25 | ||||
-rw-r--r-- | include/error.h | 3 | ||||
-rw-r--r-- | include/nasm.h | 16 | ||||
-rw-r--r-- | include/nasmlib.h | 2 | ||||
-rw-r--r-- | nasmlib/readnum.c | 4 |
10 files changed, 255 insertions, 101 deletions
diff --git a/asm/directiv.dat b/asm/directiv.dat index 96649488..185568a6 100644 --- a/asm/directiv.dat +++ b/asm/directiv.dat @@ -93,6 +93,7 @@ gprefix gsuffix lprefix lsuffix +limit ; --- Pragma operations subsections_via_symbols ; macho diff --git a/asm/error.c b/asm/error.c index 1f91cd78..3fdaa027 100644 --- a/asm/error.c +++ b/asm/error.c @@ -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} @@ -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; @@ -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; + + p += 2; - if (p[2] == 0) { /* -- => stop processing options */ + 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"); + } +} diff --git a/asm/pragma.c b/asm/pragma.c index e7dba760..71861a94 100644 --- a/asm/pragma.c +++ b/asm/pragma.c @@ -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); +} diff --git a/asm/preproc.c b/asm/preproc.c index acdbaac6..88bc6971 100644 --- a/asm/preproc.c +++ b/asm/preproc.c @@ -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; /* diff --git a/include/error.h b/include/error.h index 55cb1278..83b28da0 100644 --- a/include/error.h +++ b/include/error.h @@ -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; diff --git a/include/nasm.h b/include/nasm.h index 477cb65e..c1ecf8a1 100644 --- a/include/nasm.h +++ b/include/nasm.h @@ -759,6 +759,22 @@ struct pragma { }; /* + * 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. */ diff --git a/include/nasmlib.h b/include/nasmlib.h index 2cfe9a37..f25ef827 100644 --- a/include/nasmlib.h +++ b/include/nasmlib.h @@ -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 diff --git a/nasmlib/readnum.c b/nasmlib/readnum.c index e5b1cd69..47080884 100644 --- a/nasmlib/readnum.c +++ b/nasmlib/readnum.c @@ -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; |