From ba1f5be53228d6d90a7fb72a88b6f540932c382f Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Mon, 28 Nov 2016 18:17:36 -0700 Subject: [PATCH] fix potential crash when printing a changing hash table If a mutable hash table changes while it's being printed, various parts of the printing function could see a mismatch between the current size and an old array size. To avoid this problem, extract the size whenever extracting the array. --- pkgs/racket-test-core/tests/racket/print.rktl | 20 +++++++++++++++++++ racket/src/racket/src/print.c | 16 +++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pkgs/racket-test-core/tests/racket/print.rktl b/pkgs/racket-test-core/tests/racket/print.rktl index be7fce218b..629c4255de 100644 --- a/pkgs/racket-test-core/tests/racket/print.rktl +++ b/pkgs/racket-test-core/tests/racket/print.rktl @@ -268,6 +268,26 @@ (loop (sub1 i)))))) ;; ---------------------------------------- +;; Check that printing a hash table doesn't crash +;; if the table changes while it's being printed +;; (or checked for cycles): +(let () + (define ht (make-hash)) + + (struct trouble (t) + #:property prop:custom-write + (lambda (s p mode) + (hash-set! ht (random) 'ok) + (fprintf p "trouble"))) + + (for ([i (in-range 100)]) + (hash-set! ht (trouble i) i)) + + (for ([i 20]) + (define o (open-output-bytes)) + (print ht o))) + +;; ---------------------------------------- (report-errs) diff --git a/racket/src/racket/src/print.c b/racket/src/racket/src/print.c index 6b7ce16273..32e7e5df57 100644 --- a/racket/src/racket/src/print.c +++ b/racket/src/racket/src/print.c @@ -651,7 +651,7 @@ static int check_cycles(Scheme_Object *obj, int for_write, Scheme_Hash_Table *ht /* got here => printable */ Scheme_Hash_Table *t; Scheme_Object **keys, **vals, *val, *key; - int i; + int i, size; if (SCHEME_NP_CHAPERONEP(obj)) t = (Scheme_Hash_Table *)SCHEME_CHAPERONE_VAL(obj); @@ -660,8 +660,9 @@ static int check_cycles(Scheme_Object *obj, int for_write, Scheme_Hash_Table *ht keys = t->keys; vals = t->vals; + size = t->size; res = 0; - for (i = 0; i < t->size; i++) { + for (i = 0; i < size; i++) { if (vals[i]) { key = keys[i]; if (!SAME_OBJ((Scheme_Object *)t, obj)) @@ -928,7 +929,7 @@ static void setup_graph_table(Scheme_Object *obj, int for_write, Scheme_Hash_Tab } else if (pp && SCHEME_CHAPERONE_HASHTPx(obj)) { /* got here => printable */ Scheme_Hash_Table *t; Scheme_Object **keys, **vals, *val, *key; - int i; + int i, size; if (SCHEME_NP_CHAPERONEP(obj)) t = (Scheme_Hash_Table *)SCHEME_CHAPERONE_VAL(obj); @@ -937,7 +938,8 @@ static void setup_graph_table(Scheme_Object *obj, int for_write, Scheme_Hash_Tab keys = t->keys; vals = t->vals; - for (i = 0; i < t->size; i++) { + size = t->size; + for (i = 0; i < size; i++) { if (vals[i]) { key = keys[i]; if (!SAME_OBJ((Scheme_Object *)t, obj)) @@ -2360,7 +2362,7 @@ print(Scheme_Object *obj, int notdisplay, int compact, Scheme_Hash_Table *ht, Scheme_Hash_Table *t; Scheme_Hash_Tree *tr; Scheme_Object **keys, **vals, *val, *key, *orig, **sorted_keys; - intptr_t i, size, count; + intptr_t i, size, count, vals_size; int did_one = 0; mzlonglong pos; @@ -2417,10 +2419,12 @@ print(Scheme_Object *obj, int notdisplay, int compact, Scheme_Hash_Table *ht, keys = t->keys; vals = t->vals; size = t->size; + vals_size = size; count = t->count; } else { keys = NULL; vals = NULL; + vals_size = 0; size = tr->count; count = size; } @@ -2451,7 +2455,7 @@ print(Scheme_Object *obj, int notdisplay, int compact, Scheme_Hash_Table *ht, if (!SAME_OBJ(obj, orig)) val = scheme_chaperone_hash_traversal_get(orig, key, &key); } else { - if (i < t->size) { + if (i < vals_size) { val = vals[i]; key = keys[i]; if (!SAME_OBJ(obj, orig))