Bug in exception chaining results in exceptions escaping

Yuxuan Shui via Digitalmars-d digitalmars-d at puremagic.com
Tue Dec 13 16:14:24 PST 2016


Test case originally by Ali Çehreli, modified by me to expose a 
more serious bug:

import core.stdc.stdio;

class TestException : Exception {
     this(string msg) {
         super(typeof(this).stringof~": "~msg);
     }
}

class UnrelatedException: Exception {
     this() {
         super("You should've caught me!!");
     }
}

class TestError : Error {
     this(string msg) {
         super(typeof(this).stringof~": "~msg);
     }
}

// Causes an exception chain where the node at index errorIndex 
is an
// Error (others are all Exceptions).
void causeExceptionChain(size_t chainLength, size_t errorIndex) {
     void throws(size_t n) {
         scope (exit) {
             string msg = [ cast(char)('a'+n) ].idup;
             if (n == errorIndex) {
                 throw new TestError(msg);
             }
             else {
                 throw new TestException(msg);
             }
         }

         if (n != 0) {
             // Redundant 'return' keyword due to
             // https://issues.dlang.org/show_bug.cgi?id=16960
             return throws(n - 1);
         }
     }

     throws(chainLength - 1);
}

void main() {
     // Step 1: Setup a corrupted thread exception chain
     version(bug) {
         try {
             size_t errorIndex = 1;
             causeExceptionChain(2, errorIndex);
         }
         catch (Error original) {
             printf("Caught\n");
             string prefix = "";
             for ({ size_t i; Throwable ex = original; } ex; ex = 
ex.next, ++i) {
                 printf("%.*s%.*s\n", prefix.length, prefix.ptr, 
ex.msg.length, ex.msg.ptr);
                 prefix = prefix~" ";
             }
             printf("Bypassed chain was:\n");
             prefix = "";
             // This loop should print something, but nope.
             for ({ size_t i; Throwable ex = 
original.bypassedException; } ex; ex = ex.next, ++i) {
                 printf("%.*s%.*s\n", prefix.length, prefix.ptr, 
ex.msg.length, ex.msg.ptr);
                 prefix = prefix~" ";
             }
         }
     }

     // Now there's leftover exceptions unhandled

     try {
         // Step 2
         throw new UnrelatedException();
     } catch (UnrelatedException x) {
         // Should be caught here
     }
     // But it escaped!!
     // It even has some ghost exceptions chained to it.
}

Bug is fixed in https://github.com/dlang/druntime/pull/1712, 
please review.


More information about the Digitalmars-d mailing list