From 4a387c5b6a379baa3d5baac2bce1e2343dc12ce8 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Thu, 8 Dec 2011 13:34:20 -0700 Subject: [PATCH] add some missing checks on bytecode parsing The `tests/racket/stress/fuzz' test fails much less frequently, though problems certainly remain. --- collects/tests/racket/stress/fuzz.rkt | 28 +++++++++++----- src/racket/src/compile.c | 22 +++++++++--- src/racket/src/env.c | 4 +++ src/racket/src/marshal.c | 28 ++++++++++++++-- src/racket/src/read.c | 48 +++++++++++++++++++++------ src/racket/src/salloc.c | 11 ++++++ src/racket/src/schpriv.h | 2 ++ src/racket/src/string.c | 16 ++++----- src/racket/src/syntax.c | 1 + src/racket/src/validate.c | 18 ++++++---- 10 files changed, 137 insertions(+), 41 deletions(-) diff --git a/collects/tests/racket/stress/fuzz.rkt b/collects/tests/racket/stress/fuzz.rkt index 99ca3f99be..8851117f44 100644 --- a/collects/tests/racket/stress/fuzz.rkt +++ b/collects/tests/racket/stress/fuzz.rkt @@ -8,8 +8,15 @@ (bytes-set! bs byte (bitwise-xor (expt 2 bit) val))) (define (run-file bs) - (eval (parameterize ([read-accept-compiled #t]) - (with-input-from-bytes bs read)))) + (sync + (parameterize ([current-custodian (make-custodian)]) + (thread + (lambda () + (custodian-limit-memory (current-custodian) + (* 512 (expt 2 20))) + (with-handlers ([void void]) + (eval (parameterize ([read-accept-compiled #t]) + (with-input-from-bytes bs read))))))))) (define (run fname seed0) (define seed (or seed0 (+ 1 (random (expt 2 30))))) @@ -21,17 +28,20 @@ (for ([i (in-range (quotient len 10000))]) (flip-bit bs (random len))) (with-handlers ([void void]) (run-file bs))) -(let ([seed #f] [file #f] [dir #f]) +(let ([seed0 #f] [file #f] [dir #f] [forever? #f]) (command-line #:once-each - ["-s" seed "random seed" (set! seed (string->number seed))] + ["-s" seed "random seed" (set! seed0 (string->number seed))] + ["--oo" "forever" (set! forever? #t)] #:once-any ["-f" file "filename to run" (set! file file)] ["-d" dir* "dir to run" (set! dir dir*)] ["-c" "run over all collections" (set! dir (find-collects-dir))] #:args () (void)) - (cond [file (run file seed)] - [dir (for ([p (in-directory dir)] - #:when (regexp-match #rx"\\.zo" p)) - (run p seed))] - [else (printf "Nothing to do.\n")])) + (let loop () + (cond [file (run file seed0)] + [dir (for ([p (in-directory dir)] + #:when (regexp-match #rx"\\.zo" p)) + (run p seed0))] + [else (printf "Nothing to do.\n")]) + (when forever? (loop)))) diff --git a/src/racket/src/compile.c b/src/racket/src/compile.c index 18b189b564..2a4308532f 100644 --- a/src/racket/src/compile.c +++ b/src/racket/src/compile.c @@ -4127,12 +4127,24 @@ Scheme_Object *scheme_make_application(Scheme_Object *v) Scheme_App_Rec *scheme_malloc_application(int n) { Scheme_App_Rec *app; - int size; + intptr_t size; - size = (sizeof(Scheme_App_Rec) - + ((n - mzFLEX_DELTA) * sizeof(Scheme_Object *)) - + n * sizeof(char)); - app = (Scheme_App_Rec *)scheme_malloc_tagged(size); + if (n < 0) { + scheme_signal_error("bad application count"); + app = NULL; + } else if (n > 4096) { + size = scheme_check_overflow(n, + sizeof(char), + (sizeof(Scheme_App_Rec) + + ((n - mzFLEX_DELTA) * sizeof(Scheme_Object *)))); + app = (Scheme_App_Rec *)scheme_malloc_fail_ok(scheme_malloc_tagged, size); + if (!app) scheme_signal_error("out of memory allocating application bytecode"); + } else { + size = (sizeof(Scheme_App_Rec) + + ((n - mzFLEX_DELTA) * sizeof(Scheme_Object *)) + + n * sizeof(char)); + app = (Scheme_App_Rec *)scheme_malloc_tagged(size); + } app->so.type = scheme_application_type; diff --git a/src/racket/src/env.c b/src/racket/src/env.c index 18bc7803af..bd0f29cb31 100644 --- a/src/racket/src/env.c +++ b/src/racket/src/env.c @@ -1352,6 +1352,10 @@ Scheme_Object **scheme_make_builtin_references_table(void) scheme_misc_count += sizeof(Scheme_Object *) * (builtin_ref_counter + 1); #endif + for (j = builtin_ref_counter + 1; j--; ) { + t[j] = scheme_false; + } + for (j = 0; j < 4; j++) { if (!j) kenv = kernel_env; diff --git a/src/racket/src/marshal.c b/src/racket/src/marshal.c index 39d61bff29..b2d1e275bf 100644 --- a/src/racket/src/marshal.c +++ b/src/racket/src/marshal.c @@ -275,7 +275,13 @@ static Scheme_Object *read_letrec(Scheme_Object *obj) lr->body = SCHEME_CAR(obj); obj = SCHEME_CDR(obj); - sa = MALLOC_N(Scheme_Object*, c); + if (c < 0) return NULL; + if (c < 4096) + sa = MALLOC_N(Scheme_Object*, c); + else { + sa = scheme_malloc_fail_ok(scheme_malloc, scheme_check_overflow(c, sizeof(Scheme_Object *), 0)); + if (!sa) scheme_signal_error("out of memory allocating letrec bytecode"); + } lr->procs = sa; for (i = 0; i < c; i++) { if (!SCHEME_PAIRP(obj)) return NULL; @@ -312,6 +318,8 @@ static Scheme_Object *read_top(Scheme_Object *obj) if (!SCHEME_PAIRP(obj)) return NULL; top->prefix = (Resolve_Prefix *)SCHEME_CAR(obj); top->code = SCHEME_CDR(obj); + if (!SAME_TYPE(SCHEME_TYPE(top->prefix), scheme_resolve_prefix_type)) + return NULL; return (Scheme_Object *)top; } @@ -585,6 +593,8 @@ static Scheme_Object *read_sequence_save_first(Scheme_Object *obj) static Scheme_Object *read_sequence_splice(Scheme_Object *obj) { obj = scheme_make_sequence_compilation(obj, 1); + if (!obj) return NULL; + if (SAME_TYPE(SCHEME_TYPE(obj), scheme_sequence_type)) obj->type = scheme_splice_sequence_type; return obj; @@ -958,6 +968,9 @@ static Scheme_Object *read_toplevel(Scheme_Object *obj) flags = 0; } + if (depth < 0) return NULL; + if (pos < 0) return NULL; + return scheme_make_toplevel(depth, pos, 1, flags); } @@ -1029,6 +1042,7 @@ static Scheme_Object *do_read_local(Scheme_Type t, Scheme_Object *obj) flags = 0; n = (int)SCHEME_INT_VAL(obj); + if (n < 0) return NULL; return scheme_make_local(t, n, flags); } @@ -1086,7 +1100,7 @@ static Scheme_Object *write_resolve_prefix(Scheme_Object *obj) static Scheme_Object *read_resolve_prefix(Scheme_Object *obj) { Resolve_Prefix *rp; - Scheme_Object *tv, *sv, **a, *stx; + Scheme_Object *tv, *sv, **a, *stx, *tl; intptr_t i; if (!SCHEME_PAIRP(obj)) return NULL; @@ -1119,7 +1133,15 @@ static Scheme_Object *read_resolve_prefix(Scheme_Object *obj) i = rp->num_toplevels; a = MALLOC_N(Scheme_Object *, i); while (i--) { - a[i] = SCHEME_VEC_ELS(tv)[i]; + tl = SCHEME_VEC_ELS(tv)[i]; + if (!SCHEME_FALSEP(tl) + && !SCHEME_SYMBOLP(tl) + && (!SCHEME_PAIRP(tl) + || !SCHEME_SYMBOLP(SCHEME_CAR(tl))) + && !SAME_TYPE(SCHEME_TYPE(tl), scheme_variable_type) + && !SAME_TYPE(SCHEME_TYPE(tl), scheme_module_variable_type)) + return NULL; + a[i] = tl; } rp->toplevels = a; diff --git a/src/racket/src/read.c b/src/racket/src/read.c index 899228b3e7..4e127a9fc8 100644 --- a/src/racket/src/read.c +++ b/src/racket/src/read.c @@ -4066,6 +4066,7 @@ typedef struct Scheme_Load_Delay { #define ZO_CHECK(x) if (!(x)) scheme_ill_formed_code(port); #define RANGE_CHECK(x, y) ZO_CHECK (x y) +#define RANGE_POS_CHECK(x, y) ZO_CHECK ((x > 0) && (x y)) #define RANGE_CHECK_GETS(x) RANGE_CHECK(x, <= port->size - port->pos) typedef struct CPort { @@ -4179,13 +4180,22 @@ static Scheme_Object *read_compact_svector(CPort *port, int l) o->type = scheme_svector_type; SCHEME_SVEC_LEN(o) = l; - if (l > 0) - v = MALLOC_N_ATOMIC(mzshort, l); - else + if (l > 0) { + if (l > 4096) { + v = (mzshort *)scheme_malloc_fail_ok(scheme_malloc_atomic, + scheme_check_overflow(l, sizeof(mzshort), 0)); + if (!v) + scheme_signal_error("out of memory allocating vector"); + } else { + v = MALLOC_N_ATOMIC(mzshort, l); + } + } else { v = NULL; + l = 0; /* in case it was negative */ + } SCHEME_SVEC_VEC(o) = v; - while (l--) { + while (l-- > 0) { mzshort cn; cn = read_compact_number(port); v[l] = cn; @@ -4295,7 +4305,7 @@ static Scheme_Object *read_compact(CPort *port, int use_stack) break; case CPT_SYMREF: l = read_compact_number(port); - RANGE_CHECK(l, < port->symtab_size); + RANGE_POS_CHECK(l, < port->symtab_size); v = port->symtab[l]; if (v == SYMTAB_IN_PROGRESS) { /* there is a cycle */ @@ -4351,8 +4361,12 @@ static Scheme_Object *read_compact(CPort *port, int use_stack) l = read_compact_number(port); RANGE_CHECK_GETS(el); s = read_compact_chars(port, buffer, BLK_BUF_SIZE, el); - us = (mzchar *)scheme_malloc_atomic((l + 1) * sizeof(mzchar)); - scheme_utf8_decode_all((const unsigned char *)s, el, us, 0); + if (l < 4096) + us = (mzchar *)scheme_malloc_atomic((l + 1) * sizeof(mzchar)); + else + us = (mzchar *)scheme_malloc_fail_ok(scheme_malloc_atomic, (l + 1) * sizeof(mzchar)); + if (scheme_utf8_decode((const unsigned char *)s, 0, el, us, 0, l, NULL, 0, 0) != l) + scheme_ill_formed_code(port); us[l] = 0; v = scheme_make_immutable_sized_char_string(us, l, 0); v = scheme_intern_literal_string(v); @@ -4648,6 +4662,7 @@ static Scheme_Object *read_compact(CPort *port, int use_stack) port->symtab[l] = (Scheme_Object *)cl; v = read_compact(port, 0); if (!SAME_TYPE(SCHEME_TYPE(v), scheme_closure_type) + || !((Scheme_Closure *)v)->code || ((Scheme_Closure *)v)->code->closure_size) { scheme_ill_formed_code(port); return NULL; @@ -4659,7 +4674,7 @@ static Scheme_Object *read_compact(CPort *port, int use_stack) case CPT_DELAY_REF: { l = read_compact_number(port); - RANGE_CHECK(l, < port->symtab_size); + RANGE_POS_CHECK(l, < port->symtab_size); v = port->symtab[l]; if (!v) { if (port->delay_info) { @@ -4675,6 +4690,9 @@ static Scheme_Object *read_compact(CPort *port, int use_stack) port->pos = save_pos; port->symtab[l] = v; } + } else if (v == SYMTAB_IN_PROGRESS) { + /* there is a cycle */ + scheme_ill_formed_code(port); } return v; break; @@ -4983,11 +5001,19 @@ static Scheme_Object *read_compiled(Scheme_Object *port, /* Load table mapping symtab indices to stream positions: */ all_short = scheme_get_byte(port); - so = (intptr_t *)scheme_malloc_fail_ok(scheme_malloc_atomic, sizeof(intptr_t) * symtabsize); + if (symtabsize < 0) + so = NULL; + else + so = (intptr_t *)scheme_malloc_fail_ok(scheme_malloc_atomic, + scheme_check_overflow(symtabsize, sizeof(intptr_t), 0)); + if (!so) + scheme_read_err(port, NULL, -1, -1, -1, -1, 0, NULL, + "read (compiled): could not allocate symbol table of size %" PRIdPTR, + symtabsize); if ((got = scheme_get_bytes(port, (all_short ? 2 : 4) * (symtabsize - 1), (char *)so, 0)) != ((all_short ? 2 : 4) * (symtabsize - 1))) scheme_read_err(port, NULL, -1, -1, -1, -1, 0, NULL, - "read (compiled): ill-formed code (bad table count: %ld != %ld)", + "read (compiled): ill-formed code (bad table count: %" PRIdPTR " != %" PRIdPTR ")", got, (all_short ? 2 : 4) * (symtabsize - 1)); offset += got; @@ -5328,6 +5354,8 @@ Scheme_Object *scheme_unmarshal_wrap_get(Scheme_Unmarshal_Tables *ut, if ((l < 0) || ((uintptr_t)l >= ut->rp->symtab_size)) scheme_ill_formed_code(ut->rp); + if (SAME_OBJ(ut->rp->symtab[l], SYMTAB_IN_PROGRESS)) + scheme_ill_formed_code(ut->rp); if (!ut->rp->symtab[l]) { Scheme_Object *v; diff --git a/src/racket/src/salloc.c b/src/racket/src/salloc.c index abb1ad862b..2d43d053ec 100644 --- a/src/racket/src/salloc.c +++ b/src/racket/src/salloc.c @@ -632,6 +632,17 @@ static void raise_out_of_memory(void) scheme_raise_out_of_memory(NULL, NULL); } +intptr_t scheme_check_overflow(intptr_t n, intptr_t m, intptr_t a) +{ + intptr_t v; + + v = (n * m) + a; + if ((v < n) || (v < m) || (v < a) || (((v - a) / n) != m)) + scheme_signal_error("allocation size overflow"); + + return v; +} + void *scheme_malloc_fail_ok(void *(*f)(size_t), size_t s) { void *v; diff --git a/src/racket/src/schpriv.h b/src/racket/src/schpriv.h index d097cb134c..31965916be 100644 --- a/src/racket/src/schpriv.h +++ b/src/racket/src/schpriv.h @@ -3663,6 +3663,8 @@ int scheme_can_inline_fp_comp(); void scheme_unused_object(Scheme_Object*); void scheme_unused_intptr(intptr_t); +intptr_t scheme_check_overflow(intptr_t n, intptr_t m, intptr_t a); + /*========================================================================*/ /* places */ /*========================================================================*/ diff --git a/src/racket/src/string.c b/src/racket/src/string.c index 68966d3add..83f743c148 100644 --- a/src/racket/src/string.c +++ b/src/racket/src/string.c @@ -4723,10 +4723,10 @@ byte_converter_p(int argc, Scheme_Object *argv[]) /**********************************************************************/ static intptr_t utf8_decode_x(const unsigned char *s, intptr_t start, intptr_t end, - unsigned int *us, intptr_t dstart, intptr_t dend, - intptr_t *ipos, intptr_t *jpos, - char compact, char utf16, int *_state, - int might_continue, int permissive) + unsigned int *us, intptr_t dstart, intptr_t dend, + intptr_t *ipos, intptr_t *jpos, + char compact, char utf16, int *_state, + int might_continue, int permissive) /* Results: non-negative => translation complete, = number of produced chars -1 => input ended in middle of encoding (only if might_continue) @@ -5079,16 +5079,16 @@ static intptr_t utf8_decode_x(const unsigned char *s, intptr_t start, intptr_t e } intptr_t scheme_utf8_decode(const unsigned char *s, intptr_t start, intptr_t end, - unsigned int *us, intptr_t dstart, intptr_t dend, - intptr_t *ipos, char utf16, int permissive) + unsigned int *us, intptr_t dstart, intptr_t dend, + intptr_t *ipos, char utf16, int permissive) { return utf8_decode_x(s, start, end, us, dstart, dend, ipos, NULL, utf16, utf16, NULL, 0, permissive); } intptr_t scheme_utf8_decode_as_prefix(const unsigned char *s, intptr_t start, intptr_t end, - unsigned int *us, intptr_t dstart, intptr_t dend, - intptr_t *ipos, char utf16, int permissive) + unsigned int *us, intptr_t dstart, intptr_t dend, + intptr_t *ipos, char utf16, int permissive) /* Always returns number of read characters, not error codes. */ { intptr_t opos; diff --git a/src/racket/src/syntax.c b/src/racket/src/syntax.c index fd46506afc..1093a80f1a 100644 --- a/src/racket/src/syntax.c +++ b/src/racket/src/syntax.c @@ -7279,6 +7279,7 @@ static Scheme_Object *datum_to_syntax_inner(Scheme_Object *o, --cnt; } + if (!first) return_NULL; if (!SCHEME_NULLP(o)) { o = datum_to_syntax_inner(o, ut, stx_src, sub_stx_wraps, ht, tainted); if (!o) return_NULL; diff --git a/src/racket/src/validate.c b/src/racket/src/validate.c index ff619e546e..5f3282014a 100644 --- a/src/racket/src/validate.c +++ b/src/racket/src/validate.c @@ -793,7 +793,7 @@ static void validate_unclosed_procedure(Mz_CPort *port, Scheme_Object *expr, if (q == self_pos) self_pos_in_closure = i; p = q + delta; - if ((q < 0) || (p >= depth) || (stack[p] <= VALID_UNINIT)) + if ((q < 0) || (p < 0) || (p >= depth) || (stack[p] <= VALID_UNINIT)) scheme_ill_formed_code(port); vld = stack[p]; if (vld == VALID_VAL_NOCLEAR) @@ -884,6 +884,10 @@ static void module_validate(Scheme_Object *data, Mz_CPort *port, if (!SCHEME_MODNAMEP(m->modname)) scheme_ill_formed_code(port); + validate_toplevel(m->dummy, port, stack, tls, depth, delta, + num_toplevels, num_stxes, num_lifts, tl_use_map, + 0); + scheme_validate_code(port, m->bodies[0], m->max_let_depth, m->prefix->num_toplevels, m->prefix->num_stxes, m->prefix->num_lifts, NULL, @@ -1012,7 +1016,7 @@ void scheme_validate_expr(Mz_CPort *port, Scheme_Object *expr, no_flo(need_flonum, port); - if ((c < 0) || (p < 0) || (d >= depth) + if ((c < 0) || (p < 0) || (d < 0) || (d >= depth) || (stack[d] != VALID_TOPLEVELS) || (p >= (num_toplevels + num_lifts + num_stxes + (num_stxes ? 1 : 0))) || ((p >= num_toplevels) && (p < num_toplevels + num_stxes + (num_stxes ? 1 : 0)))) @@ -1071,7 +1075,7 @@ void scheme_validate_expr(Mz_CPort *port, Scheme_Object *expr, int q = SCHEME_LOCAL_POS(expr); int p = q + delta; - if ((q < 0) || (p >= depth)) + if ((q < 0) || (p >= depth) || (p < 0)) scheme_ill_formed_code(port); if (SCHEME_GET_LOCAL_FLAGS(expr) != SCHEME_LOCAL_FLONUM) @@ -1128,8 +1132,9 @@ void scheme_validate_expr(Mz_CPort *port, Scheme_Object *expr, no_flo(need_flonum, port); - if ((q < 0) || (p >= depth) || ((stack[p] != VALID_BOX) - && (stack[p] != VALID_BOX_NOCLEAR))) + if ((q < 0) || (p >= depth) || (p < 0) + || ((stack[p] != VALID_BOX) + && (stack[p] != VALID_BOX_NOCLEAR))) scheme_ill_formed_code(port); if (SCHEME_GET_LOCAL_FLAGS(expr) == SCHEME_LOCAL_CLEAR_ON_READ) { @@ -1315,7 +1320,7 @@ void scheme_validate_expr(Mz_CPort *port, Scheme_Object *expr, no_flo(need_flonum, port); - if ((c < 0) || (p < 0) || (d >= depth) + if ((c < 0) || (p < 0) || (d < 0) || (d >= depth) || (stack[d] != VALID_TOPLEVELS) || (p != num_toplevels) || (i >= num_stxes)) @@ -1346,6 +1351,7 @@ void scheme_validate_expr(Mz_CPort *port, Scheme_Object *expr, for (i = 0; i < c; i++, p++) { if ((q < 0) + || (p < 0) || (SCHEME_LET_AUTOBOX(lv) && ((p >= depth) || ((stack[p] != VALID_BOX) && (stack[p] != VALID_BOX_NOCLEAR))))