|
| 1 | +// |
| 2 | +// Fortify.swift |
| 3 | +// Fortify |
| 4 | +// |
| 5 | +// Created by John Holdsworth on 19/09/2017. |
| 6 | +// Copyright © 2017 John Holdsworth. All rights reserved. |
| 7 | +// |
| 8 | +// Currently requires patched Swift toolchain from here: |
| 9 | +// http://johnholdsworth.com/swift-LOCAL-2017-09-20-a-osx.tar.gz |
| 10 | +// |
| 11 | + |
| 12 | +import Foundation |
| 13 | + |
| 14 | +openclassThreadLocal{ |
| 15 | +publicrequiredinit(){ |
| 16 | +} |
| 17 | + |
| 18 | +publicclassfunc getThreadLocal<T:ThreadLocal>(ofClass:T.Type, |
| 19 | + keyVar:UnsafeMutablePointer<pthread_key_t>)->T{ |
| 20 | +letneedsKey= keyVar.pointee==0 |
| 21 | +if needsKey{ |
| 22 | +letret=pthread_key_create(keyVar,{ |
| 23 | +#if os(Linux) |
| 24 | +Unmanaged<ThreadLocal>.fromOpaque($0!).release() |
| 25 | +#else |
| 26 | +Unmanaged<ThreadLocal>.fromOpaque($0).release() |
| 27 | +#endif |
| 28 | +}) |
| 29 | +if ret!=0{ |
| 30 | +NSLog("Could not pthread_key_create: %s",strerror(ret)) |
| 31 | +} |
| 32 | +} |
| 33 | +iflet existing=pthread_getspecific(keyVar.pointee){ |
| 34 | +returnUnmanaged<T>.fromOpaque(existing).takeUnretainedValue() |
| 35 | +} |
| 36 | +else{ |
| 37 | +letunmanaged=Unmanaged.passRetained(T()) |
| 38 | +letret=pthread_setspecific(keyVar.pointee, unmanaged.toOpaque()) |
| 39 | +if ret!=0{ |
| 40 | +NSLog("Could not pthread_setspecific: %s",strerror(ret)) |
| 41 | +} |
| 42 | +return unmanaged.takeUnretainedValue() |
| 43 | +} |
| 44 | +} |
| 45 | +} |
| 46 | + |
| 47 | +@_silgen_name("setjmp") |
| 48 | +publicfunc setjump(_:UnsafeMutablePointer<jmp_buf>!)-> Int32 |
| 49 | + |
| 50 | +@_silgen_name("longjmp") |
| 51 | +publicfunc longjump(_:UnsafeMutablePointer<jmp_buf>!, _:Int32)-> Never |
| 52 | + |
| 53 | +privatelet empty_buf=[UInt8](repeating:0, count: MemoryLayout<jmp_buf>.size) |
| 54 | + |
| 55 | +openclass Fortify: ThreadLocal{ |
| 56 | + |
| 57 | + static privatevar pthreadKey: pthread_key_t=0 |
| 58 | + |
| 59 | +openclassvarthreadLocal:Fortify{ |
| 60 | +returngetThreadLocal(ofClass:Fortify.self, keyVar:&pthreadKey) |
| 61 | +} |
| 62 | + |
| 63 | +privatevarstack=[jmp_buf]() |
| 64 | +publicvarerror:Error? |
| 65 | + |
| 66 | + // Required as Swift assumes it has control of the stack |
| 67 | +openclassfunc disableExclusivityChecking(){ |
| 68 | +#if os(Android) |
| 69 | +letlibName="libswiftCore.so" |
| 70 | +#else |
| 71 | +letlibName:String?=nil |
| 72 | +#endif |
| 73 | +iflet stdlibHandle=dlopen(libName,Int32(RTLD_LAZY | RTLD_NOLOAD)), |
| 74 | +let disableExclusivity=dlsym(stdlibHandle,"_swift_disableExclusivityChecking"){ |
| 75 | + disableExclusivity.assumingMemoryBound(to:Bool.self).pointee=true |
| 76 | +} |
| 77 | +else{ |
| 78 | +NSLog("Could not disable exclusivity, failure likely...") |
| 79 | +} |
| 80 | +} |
| 81 | + |
| 82 | +publicstaticletinstallHandlerOnce:Void={ |
| 83 | +// _swift_stdlib_errorHandler = { |
| 84 | +// (prefix: StaticString, msg: String, file: StaticString, |
| 85 | +// line: UInt, flags: UInt32, config: Int32) in |
| 86 | +// escape(msg: msg, file: file, line: line) |
| 87 | +// } |
| 88 | + |
| 89 | +disableExclusivityChecking() |
| 90 | +}() |
| 91 | + |
| 92 | +openclassfunc exec<T>( block:()throws->T)throws->T{ |
| 93 | + _= installHandlerOnce |
| 94 | +letlocal= threadLocal |
| 95 | + |
| 96 | + empty_buf.withUnsafeBytes{ |
| 97 | +letbuf_ptr= $0.baseAddress!.assumingMemoryBound(to: jmp_buf.self) |
| 98 | + local.stack.append(buf_ptr.pointee) |
| 99 | +} |
| 100 | + |
| 101 | +defer{ |
| 102 | + local.stack.removeLast() |
| 103 | +} |
| 104 | + |
| 105 | +ifsetjump(&local.stack[local.stack.count-1])!=0{ |
| 106 | +throw local.error??NSError(domain:"Error not available", code:-1, userInfo:nil) |
| 107 | +} |
| 108 | + |
| 109 | +returntryblock() |
| 110 | +} |
| 111 | + |
| 112 | +openclassfunc escape(msg:String, file:StaticString= #file, line:UInt= #line)->Never{ |
| 113 | +escape(withError:NSError(domain: msg, code:-1, userInfo:[ |
| 114 | + NSLocalizedDescriptionKey:"\(msg):\(file):\(line)", |
| 115 | +"msg": msg,"file": file,"line": line |
| 116 | +])) |
| 117 | +} |
| 118 | + |
| 119 | +openclassfunc escape(withError error:Error)->Never{ |
| 120 | +letlocal= threadLocal |
| 121 | + local.error= error |
| 122 | + |
| 123 | +if local.stack.count==0{ |
| 124 | +NSLog("escape without matching exec call:\(error)") |
| 125 | +#if !os(Linux) |
| 126 | + // pthread_exit(nil) just crashes |
| 127 | +varoldState:Int32=0 |
| 128 | +pthread_setcancelstate(Int32(PTHREAD_CANCEL_ENABLE),&oldState) |
| 129 | +pthread_setcanceltype(Int32(PTHREAD_CANCEL_DEFERRED),&oldState) |
| 130 | + // pthread_cancel() never seems to be implemented |
| 131 | +letcancelled=pthread_cancel(pthread_self()) |
| 132 | +if cancelled!=0{ |
| 133 | +NSLog("pthread_cancel() failed: %s",strerror(cancelled)) |
| 134 | +} |
| 135 | +sleep(1) |
| 136 | +#endif |
| 137 | +NSLog("cancel/exit not available/implemented or crashes, parking thread") |
| 138 | +Thread.sleep(until:Date.distantFuture) |
| 139 | +} |
| 140 | + |
| 141 | +longjump(&local.stack[local.stack.count-1],1) |
| 142 | +NSLog("longjmp() failed, should not get here") |
| 143 | +} |
| 144 | +} |