1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 11:50:28 +02:00

fix bug in compilation of and' and or'; more robust underflow detection.

* module/system/il/compile.scm (codegen): Rewrite handling of `and' and
  `or' ghil compilation, because it was broken if drop was #t. Tricky
  bug, this one! Took me days to track down!

* module/system/repl/repl.scm: Export call-with-backtrace, which probably
  should go in some other file.

* src/vm.c (scm_vm_save_stack): Handle the fp==0 case for errors before
  we have a frame.

* src/vm_engine.h (NEW_FRAME, FREE_FRAME): Stricter underflow checking,
  raising the stack base to the return address, in an attempt to prevent
  inadvertant stack smashing (the symptom of the and/or miscompilation
  bug).
  (CHECK_IP): A check that the current IP is within the bounds of the
  current program. Not normally compiled in. Perhaps it should be?

* src/vm_system.c (halt): Set vp->ip to NULL. Paranoia, I know.
  (return): Call CHECK_IP(), if such a thing is compiled in.

* testsuite/Makefile.am (vm_test_files):
* testsuite/t-catch.scm:
* testsuite/t-map.scm:
* testsuite/t-or.scm: New tests.
This commit is contained in:
Andy Wingo 2008-08-11 18:35:58 +02:00
parent 67c4505e7a
commit 7e4760e413
9 changed files with 110 additions and 33 deletions

View file

@ -217,20 +217,22 @@
;; (br L2) ;; (br L2)
;; L1: (const #f) ;; L1: (const #f)
;; L2: ;; L2:
(cond ((null? exps) (return-object! loc #t))
((null? (cdr exps)) (comp-tail (car exps)))
(else
(let ((L1 (make-label)) (L2 (make-label))) (let ((L1 (make-label)) (L2 (make-label)))
(if (null? exps) (let lp ((exps exps))
(return-object! loc #t) (cond ((null? (cdr exps))
(do ((exps exps (cdr exps)))
((null? (cdr exps))
(comp-tail (car exps)) (comp-tail (car exps))
(if (not tail) (push-branch! #f 'br L2)) (push-branch! #f 'br L2)
(push-label! L1) (push-label! L1)
(return-object! #f #f) (return-object! #f #f)
(if (not tail) (push-label! L2)) (push-label! L2)
(maybe-drop)
(maybe-return)) (maybe-return))
(else
(comp-push (car exps)) (comp-push (car exps))
(push-branch! #f 'br-if-not L1))))) (push-branch! #f 'br-if-not L1)
(lp (cdr exps)))))))))
((<ghil-or> env loc exps) ((<ghil-or> env loc exps)
;; EXP ;; EXP
@ -240,19 +242,21 @@
;; ... ;; ...
;; TAIL ;; TAIL
;; L1: ;; L1:
(cond ((null? exps) (return-object! loc #f))
((null? (cdr exps)) (comp-tail (car exps)))
(else
(let ((L1 (make-label))) (let ((L1 (make-label)))
(if (null? exps) (let lp ((exps exps))
(return-object! loc #f) (cond ((null? (cdr exps))
(do ((exps exps (cdr exps)))
((null? (cdr exps))
(comp-tail (car exps)) (comp-tail (car exps))
(push-label! L1) (push-label! L1)
(maybe-drop)
(maybe-return)) (maybe-return))
(else
(comp-push (car exps)) (comp-push (car exps))
(push-call! #f 'dup '()) (push-call! #f 'dup '())
(push-branch! #f 'br-if L1) (push-branch! #f 'br-if L1)
(push-call! #f 'drop '()))))) (push-call! #f 'drop '())
(lp (cdr exps)))))))))
((<ghil-begin> env loc exps) ((<ghil-begin> env loc exps)
;; EXPS... ;; EXPS...

View file

@ -29,7 +29,7 @@
:use-module (system vm vm) :use-module (system vm vm)
:use-module (system vm debug) :use-module (system vm debug)
:use-module (ice-9 rdelim) :use-module (ice-9 rdelim)
:export (start-repl)) :export (start-repl call-with-backtrace))
(define meta-command-token (cons 'meta 'command)) (define meta-command-token (cons 'meta 'command))

View file

@ -560,8 +560,18 @@ SCM_DEFINE (scm_vm_save_stack, "vm-save-stack", 1, 0, 0,
SCM *dest; SCM *dest;
SCM_VALIDATE_VM (1, vm); SCM_VALIDATE_VM (1, vm);
vp = SCM_VM_DATA (vm); vp = SCM_VM_DATA (vm);
if (vp->fp)
{
vp->last_frame = vm_heapify_frames_1 (vp, vp->fp, vp->sp, &dest); vp->last_frame = vm_heapify_frames_1 (vp, vp->fp, vp->sp, &dest);
vp->last_ip = vp->ip; vp->last_ip = vp->ip;
}
else
{
vp->last_frame = SCM_BOOL_F;
}
return vp->last_frame; return vp->last_frame;
} }
#undef FUNC_NAME #undef FUNC_NAME

View file

@ -141,6 +141,13 @@
vp->fp = fp; \ vp->fp = fp; \
} }
#ifdef IP_PARANOIA
#define CHECK_IP() \
do { if (ip < bp->base || ip - bp->base > bp->size) abort (); } while (0)
#else
#define CHECK_IP()
#endif
/* Get a local copy of the program's "object table" (i.e. the vector of /* Get a local copy of the program's "object table" (i.e. the vector of
external bindings that are referenced by the program), initialized by external bindings that are referenced by the program), initialized by
`load-program'. */ `load-program'. */
@ -234,7 +241,7 @@
#define CHECK_UNDERFLOW() \ #define CHECK_UNDERFLOW() \
if (sp < stack_base) \ if (sp < stack_base) \
goto vm_error_stack_underflow goto vm_error_stack_underflow;
#define PUSH(x) do { sp++; CHECK_OVERFLOW (); *sp = x; } while (0) #define PUSH(x) do { sp++; CHECK_OVERFLOW (); *sp = x; } while (0)
#define DROP() do { sp--; CHECK_UNDERFLOW (); } while (0) #define DROP() do { sp--; CHECK_UNDERFLOW (); } while (0)
@ -428,6 +435,7 @@ do { \
p[2] = dl; \ p[2] = dl; \
p[1] = SCM_BOOL_F; \ p[1] = SCM_BOOL_F; \
p[0] = external; \ p[0] = external; \
stack_base = p + 3; \
} }
#define FREE_FRAME() \ #define FREE_FRAME() \
@ -454,6 +462,9 @@ do { \
*sp++ = *p++; \ *sp++ = *p++; \
sp--; \ sp--; \
} \ } \
stack_base = fp ? \
SCM_FRAME_UPPER_ADDRESS (fp) - 1 \
: vp->stack_base; \
} }
#define CACHE_EXTERNAL() external = fp[bp->nargs + bp->nlocs] #define CACHE_EXTERNAL() external = fp[bp->nargs + bp->nlocs]

View file

@ -60,6 +60,7 @@ VM_DEFINE_INSTRUCTION (halt, "halt", 0, 0, 0)
POP (ret); POP (ret);
FREE_FRAME (); FREE_FRAME ();
SYNC_ALL (); SYNC_ALL ();
vp->ip = NULL;
scm_dynwind_end (); scm_dynwind_end ();
return ret; return ret;
} }
@ -657,6 +658,7 @@ VM_DEFINE_INSTRUCTION (return, "return", 0, 0, 1)
program = SCM_FRAME_PROGRAM (fp); program = SCM_FRAME_PROGRAM (fp);
CACHE_PROGRAM (); CACHE_PROGRAM ();
CACHE_EXTERNAL (); CACHE_EXTERNAL ();
CHECK_IP ();
NEXT; NEXT;
} }

View file

@ -6,12 +6,15 @@ GUILE_VM = $(top_builddir)/src/guile-vm
vm_test_files = \ vm_test_files = \
t-basic-contructs.scm \ t-basic-contructs.scm \
t-global-bindings.scm \ t-global-bindings.scm \
t-catch.scm \
t-closure.scm \ t-closure.scm \
t-closure2.scm \ t-closure2.scm \
t-closure3.scm \ t-closure3.scm \
t-do-loop.scm \ t-do-loop.scm \
t-macros.scm \ t-macros.scm \
t-macros2.scm \ t-macros2.scm \
t-map.scm \
t-or.scm \
t-proc-with-setter.scm \ t-proc-with-setter.scm \
t-values.scm \ t-values.scm \
t-records.scm \ t-records.scm \

10
testsuite/t-catch.scm Normal file
View file

@ -0,0 +1,10 @@
;; Test that nonlocal exits of the VM work.
(begin
(define (foo thunk)
(catch #t thunk (lambda args args)))
(foo
(lambda ()
(let ((a 'one))
(1+ a)))))

10
testsuite/t-map.scm Normal file
View file

@ -0,0 +1,10 @@
; Currently, map is a C function, so this is a way of testing that the
; VM is reentrant.
(begin
(define (square x)
(* x x))
(map (lambda (x) (square x))
'(1 2 3)))

27
testsuite/t-or.scm Normal file
View file

@ -0,0 +1,27 @@
;; all the different permutations of or
(list
;; not in tail position, no args
(or)
;; not in tail position, one arg
(or 'what)
(or #f)
;; not in tail position, two arg
(or 'what 'where)
(or #f 'where)
(or #f #f)
(or 'what #f)
;; in tail position (within the lambdas)
((lambda ()
(or)))
((lambda ()
(or 'what)))
((lambda ()
(or #f)))
((lambda ()
(or 'what 'where)))
((lambda ()
(or #f 'where)))
((lambda ()
(or #f #f)))
((lambda ()
(or 'what #f))))