Skip to content
Snippets Groups Projects
  • Damien George's avatar
    e1fb03f3
    py: Fix VM crash with unwinding jump out of a finally block. · e1fb03f3
    Damien George authored
    This patch fixes a bug in the VM when breaking within a try-finally.  The
    bug has to do with executing a break within the finally block of a
    try-finally statement.  For example:
    
        def f():
            for x in (1,):
                print('a', x)
                try:
                    raise Exception
                finally:
                    print(1)
                    break
                print('b', x)
        f()
    
    Currently in uPy the above code will print:
    
        a 1
        1
        1
        segmentation fault (core dumped)  micropython
    
    Not only is there a seg fault, but the "1" in the finally block is printed
    twice.  This is because when the VM executes a finally block it doesn't
    really know if that block was executed due to a fall-through of the try (no
    exception raised), or because an exception is active.  In particular, for
    nested finallys the VM has no idea which of the nested ones have active
    exceptions and which are just fall-throughs.  So when a break (or continue)
    is executed it tries to unwind all of the finallys, when in fact only some
    may be active.
    
    It's questionable whether break (or return or continue) should be allowed
    within a finally block, because they implicitly swallow any active
    exception, but nevertheless it's allowed by CPython (although almost never
    used in the standard library).  And uPy should at least not crash in such a
    case.
    
    The solution here relies on the fact that exception and finally handlers
    always appear in the bytecode after the try body.
    
    Note: there was a similar bug with a return in a finally block, but that
    was previously fixed in b7352084
    e1fb03f3
    History
    py: Fix VM crash with unwinding jump out of a finally block.
    Damien George authored
    This patch fixes a bug in the VM when breaking within a try-finally.  The
    bug has to do with executing a break within the finally block of a
    try-finally statement.  For example:
    
        def f():
            for x in (1,):
                print('a', x)
                try:
                    raise Exception
                finally:
                    print(1)
                    break
                print('b', x)
        f()
    
    Currently in uPy the above code will print:
    
        a 1
        1
        1
        segmentation fault (core dumped)  micropython
    
    Not only is there a seg fault, but the "1" in the finally block is printed
    twice.  This is because when the VM executes a finally block it doesn't
    really know if that block was executed due to a fall-through of the try (no
    exception raised), or because an exception is active.  In particular, for
    nested finallys the VM has no idea which of the nested ones have active
    exceptions and which are just fall-throughs.  So when a break (or continue)
    is executed it tries to unwind all of the finallys, when in fact only some
    may be active.
    
    It's questionable whether break (or return or continue) should be allowed
    within a finally block, because they implicitly swallow any active
    exception, but nevertheless it's allowed by CPython (although almost never
    used in the standard library).  And uPy should at least not crash in such a
    case.
    
    The solution here relies on the fact that exception and finally handlers
    always appear in the bytecode after the try body.
    
    Note: there was a similar bug with a return in a finally block, but that
    was previously fixed in b7352084