Swift null safety questions

For bridged legacy code without nullability specifiers, IUOs basically save a few characters at the cost of potential crashing bugs. I’m believe there would have been a much stronger push to eliminate IUOs from the language before Swift 3 if this were the only use.

For legacy properties, there is null_resettable. These properties can never read nil, but they can be written with a nil to ‘reset’ them to some default value. We likely would need to replace this with some future property behaviors specification.

The most common use is in view controllers and other UI code due to the multi-phase initialization. The system will create the objects, then wire outlets, then once wiring is complete signal the view is ready. The outlets can all be nil during the wiring phase, but each wired outlet will be non-nil after wiring during actual usage. Dispersing unwrapping throughout the code for these outlets (and other properties initialized once the outlets are wired) would be a significant burden to clarity.

-DW

···

On Mar 7, 2017, at 2:29 PM, Elijah Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Correct me if I am wrong, but it seems like the main intended use of optionals would be to work with legacy Obj-C code that is missing nullability specifiers. In the rest of the cases, a forced-unwrap is just "syntactic sugar" for a fatal error block or a way to indicate that you have already checked the type using "as" or "!= nil" (ie. that they wrote the code in a non-Swift style and haven't updated it yet - just the perfect situation for a warning).

Another use is interoperability with "C" code, where the user knows that the code does not return a "null" pointer but has no way of annotating that in C. Or with arrays where the result is optional, but never is when the index is right. Maybe an @allowsForceUnwrap annotation on the function could silence this warning. Definitely a much more serious a condition that where the result of the function call goes unused.

A simple condition that a warning is emitted, except when unwrapping NSObject and its subclasses, or other unsafe operations like UnsafePointer, should cover all of its non-syntax uses. I think it is fair to say that developers would want to see the fatalError() written out.

Or you should use a prefork server. As long as the supervisor process—which should be pretty small and simple—doesn't crash, the worker processes can trip assertions all day and only the single request will be affected.

Processes are good. Processes are nice. Learn to love processes.

(I suspect that, if we ever do support recovering from assertions, the mechanism will be fairly process-like: Very little data sharing between the thread/section permitted to crash and the outside world, with post-crash cleanup being handled by simply releasing all the memory allocated by the thread/section.)

···

On Mar 12, 2017, at 12:20 PM, Austin Zheng via swift-evolution <swift-evolution@swift.org> wrote:

Otherwise, if you really need to build a web server framework that can swallow any sort of application logic without choking, perhaps Swift as it currently stands isn't the best tool for the job.

--
Brent Royal-Gordon
Architechies

Or when an operation might fail, but it isn’t worth going through a full do/try/catch dance. Indexing into dictionaries with invalid keys, for example… There’s only one way it can fail (no value associated with that key), and there’s no simple way to prove an index will be valid ahead of time, so the lookup returns an optional. There was a discussion probably about 2-4 months back that went into it a fair bit, but I don’t recall what the thread’s actual topic was.

If Swift gets dependent types, I suspect the next day there'll be roughly 417 proposals suggesting we use them to create non-optional versions of nearly every stdlib function that currently returns an optional :-)

- Dave Sweeris

···

On Mar 7, 2017, at 1:29 PM, Elijah Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Correct me if I am wrong, but it seems like the main intended use of optionals would be to work with legacy Obj-C code that is missing nullability specifiers. In the rest of the cases, a forced-unwrap is just "syntactic sugar" for a fatal error block or a way to indicate that you have already checked the type using "as" or "!= nil" (ie. that they wrote the code in a non-Swift style and haven't updated it yet - just the perfect situation for a warning).

Another use is interoperability with "C" code, where the user knows that the code does not return a "null" pointer but has no way of annotating that in C. Or with arrays where the result is optional, but never is when the index is right. Maybe an @allowsForceUnwrap annotation on the function could silence this warning. Definitely a much more serious a condition that where the result of the function call goes unused.

>
> Hi,
>
> Note that this thread has branched off a bit, so my latest proposal and the “head” of this thread can be found at [swift-evolution] Swift null safety questions
>
>
>
> Can you give an example of this corruption which would be exploitable in a web server context? An example where, having caught the force-unwrap or other precondition fatalError, that the web server would face more danger from continued execution that it would loose by crashing?
>
> The optional has been triggered, the request has been stopped, and incoming requests are going to a new process, but there are a few threads open still finishing what they were doing - I’d like to see a good example of the danger caused by a real “inconsistency”. Lets assume also that all objects shared between threads are Swift objects and not “UnsafePointers”, which is a very fair assumtion for a web server. Java even allows native access and they don’t even crash there.

When considering security threats, we have to take the presence of an exploit as given until we prove otherwise. A precondition failure indicates a logic error somewhere in the program, and while safe Swift abstractions are intended not to lead to memory unsafety in the face of precondition failures, there's plenty of relatively young, unsafe code below the hood in the implementations of those abstractions, written by humans who make mistakes. Whatever lurking security bugs there may be, they're more likely to be exploitable if you're already in a situation where assumptions have been violated.

> I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or relaunch their app. Security breaches or data corruption are disasters that can have unbounded impact and cost—you can't undisclose sensitive data.

> I’d be interested to know who these web server developers (ex. Kitura, Vapor) are targeting without any form of crash handling and if they read these threads. It really limits the pool of developers more so than on apps because a single crash on the app ie equivelant to a failed request, not a crashed web server. I realize you are not competing with Java, but I do not see yet the compelling reason for no crash handling.
>
> The “actors” with thread pool and proccesses is one idea, but I think these servers are aiming also for speed and scaleability - don’t know if they would accept this model. I know that it does get used, and do work for a company that uses it on their backend (just plain Java, not sep. processes). Their front-end probably runs like a pre-fork server, but Java is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some cost. At Swift's current maturity level, that's really probably the only responsible isolation strategy. The goal of "actors" would be to get most of the benefits of processes through language-level isolation without paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I would just emphasize that the webservers in existence for Swift (and quite well funded) are right now on a “crash the whole thing” model AFAIK. I don’t know if they will be able to get by with catching all the precondition failures in testing - maybe they will - maybe not, but I see people either wisely turning down Swift for web development or getting their first crash as a “zero-day” system down event where some guy has to ssh in and actually start the server back up.

I haven't worked directly with server people that much personally, but on the opportunities when I have been able to talk to people working in this space, it sounds like they definitely want something better.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura, Vapor, etc) will have to like it and implement it. If they’re not interested, I’m not starting a development company to compete.

I don't see a problem with people developing frameworks to the best of the platform's ability today and refining their approach as new capabilities get developed.

And also the development time is so short on my idea that you basically just have to approve it and the development time for it is hours not months or years. Just add a fatalError function override and any c developer could have it done in minutes like this guy GitHub - ankurp/unsafe-swift: C experiments in Swift (not endorsing the post, just showing evidence that setjmp and longjmp work fine with swift).

setjmp and longjmp do *not* work well with Swift since they need compiler support to implement their semantics, and since Swift does not provide this support, setjmp-ing from Swift is undefined behavior. Empirical evidence that small examples appear to work is not a good way of evaluating UB, since any changes to Swift or LLVM optimizations may break it. Ease of implementation is also not a good criterion for designing things. *Supporting* a trap hook is not easy; it still has serious language semantics and runtime design issues, and may limit our ability to do something better.

-Joe

···

On Mar 22, 2017, at 7:25 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:
On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com <mailto:jgroff@apple.com>) wrote:

> On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com <mailto:ejrx7753@gmail.com>> wrote:

This might be a better discussion to have on the swift-server-dev list at https://lists.swift.org/mailman/listinfo/swift-server-dev where people involved with the web frameworks are discussing such issues in relation to server uptime.

At the moment, it is impossible to have Swift code that doesn't contain potentially segfaulting code (even when the developer of such a framework goes out of their way to ensure that the framework doesn't) and so the only way of ensuring that this doesn't occur is with a cgi-bin style model, with fresh processes on each request or a pool of worker processes, and a load balancer in front of the instances, each only handling a single connection at a time.

It also becomes necessary to store any session state out-of-process in a data store/cache, so that the server processes are seen as temporary disposable instances.

An advantage of that model is that it becomes easier to scale out to a distributed system because all of the pieces that are present are there.

Alex

···

On 23 Mar 2017, at 02:25, Elijah Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com <mailto:jgroff@apple.com>) wrote:

> On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com <mailto:ejrx7753@gmail.com>> wrote:
>
> Hi,
>
> Note that this thread has branched off a bit, so my latest proposal and the “head” of this thread can be found at [swift-evolution] Swift null safety questions
>
>
>
> Can you give an example of this corruption which would be exploitable in a web server context? An example where, having caught the force-unwrap or other precondition fatalError, that the web server would face more danger from continued execution that it would loose by crashing?
>
> The optional has been triggered, the request has been stopped, and incoming requests are going to a new process, but there are a few threads open still finishing what they were doing - I’d like to see a good example of the danger caused by a real “inconsistency”. Lets assume also that all objects shared between threads are Swift objects and not “UnsafePointers”, which is a very fair assumtion for a web server. Java even allows native access and they don’t even crash there.

When considering security threats, we have to take the presence of an exploit as given until we prove otherwise. A precondition failure indicates a logic error somewhere in the program, and while safe Swift abstractions are intended not to lead to memory unsafety in the face of precondition failures, there's plenty of relatively young, unsafe code below the hood in the implementations of those abstractions, written by humans who make mistakes. Whatever lurking security bugs there may be, they're more likely to be exploitable if you're already in a situation where assumptions have been violated.

> I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or relaunch their app. Security breaches or data corruption are disasters that can have unbounded impact and cost—you can't undisclose sensitive data.

> I’d be interested to know who these web server developers (ex. Kitura, Vapor) are targeting without any form of crash handling and if they read these threads. It really limits the pool of developers more so than on apps because a single crash on the app ie equivelant to a failed request, not a crashed web server. I realize you are not competing with Java, but I do not see yet the compelling reason for no crash handling.
>
> The “actors” with thread pool and proccesses is one idea, but I think these servers are aiming also for speed and scaleability - don’t know if they would accept this model. I know that it does get used, and do work for a company that uses it on their backend (just plain Java, not sep. processes). Their front-end probably runs like a pre-fork server, but Java is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some cost. At Swift's current maturity level, that's really probably the only responsible isolation strategy. The goal of "actors" would be to get most of the benefits of processes through language-level isolation without paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I would just emphasize that the webservers in existence for Swift (and quite well funded) are right now on a “crash the whole thing” model AFAIK. I don’t know if they will be able to get by with catching all the precondition failures in testing - maybe they will - maybe not, but I see people either wisely turning down Swift for web development or getting their first crash as a “zero-day” system down event where some guy has to ssh in and actually start the server back up.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura, Vapor, etc) will have to like it and implement it. If they’re not interested, I’m not starting a development company to compete. And also the development time is so short on my idea that you basically just have to approve it and the development time for it is hours not months or years. Just add a fatalError function override and any c developer could have it done in minutes like this guy GitHub - ankurp/unsafe-swift: C experiments in Swift (not endorsing the post, just showing evidence that setjmp and longjmp work fine with swift).

For my own part, I never want to use “!” in my code, and I’m sticking to it pretty well.

It has been said many times that we don’t want to create different dialects of Swift, and I agree. Would it be considered a different dialect to add a compiler option that forbids the use of “!” in my own code? So someone else looking at or compiling my code, they could not tell that I used such an option, so I think it fits within the definition of non-dialectedness.

-Kenny

Otherwise, if you really need to build a web server framework that can swallow any sort of application logic without choking, perhaps Swift as it currently stands isn't the best tool for the job.

Or you should use a prefork server. As long as the supervisor process—which should be pretty small and simple—doesn't crash, the worker processes can trip assertions all day and only the single request will be affected.

Processes are good. Processes are nice. Learn to love processes.

(I suspect that, if we ever do support recovering from assertions, the mechanism will be fairly process-like: Very little data sharing between the thread/section permitted to crash and the outside world, with post-crash cleanup being handled by simply releasing all the memory allocated by the thread/section.)

I suspect you're right about this for the most part but I think we will want the ability for "failable processes" (or whatever they're called) to have read-only access to shared state that outlives them. What I can't imagine is allowing them write access to state that is shared.

···

Sent from my iPad
On Mar 13, 2017, at 6:35 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 12, 2017, at 12:20 PM, Austin Zheng via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi,

Note that this thread has branched off a bit, so my latest proposal and

the “head” of this thread can be found at
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034189.html

Can you give an example of this corruption which would be exploitable in

a web server context? An example where, having caught the force-unwrap or
other precondition fatalError, that the web server would face more danger
from continued execution that it would loose by crashing?

The optional has been triggered, the request has been stopped, and

incoming requests are going to a new process, but there are a few threads
open still finishing what they were doing - I’d like to see a good example
of the danger caused by a real “inconsistency”. Lets assume also that all
objects shared between threads are Swift objects and not “UnsafePointers”,
which is a very fair assumtion for a web server. Java even allows native
access and they don’t even crash there.

When considering security threats, we have to take the presence of an
exploit as given until we prove otherwise. A precondition failure indicates
a logic error somewhere in the program, and while safe Swift abstractions
are intended not to lead to memory unsafety in the face of precondition
failures, there's plenty of relatively young, unsafe code below the hood in
the implementations of those abstractions, written by humans who make
mistakes. Whatever lurking security bugs there may be, they're more likely
to be exploitable if you're already in a situation where assumptions have
been violated.

I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or
relaunch their app. Security breaches or data corruption are disasters that
can have unbounded impact and cost—you can't undisclose sensitive data.

I’d be interested to know who these web server developers (ex. Kitura,

Vapor) are targeting without any form of crash handling and if they read
these threads. It really limits the pool of developers more so than on apps
because a single crash on the app ie equivelant to a failed request, not a
crashed web server. I realize you are not competing with Java, but I do not
see yet the compelling reason for no crash handling.

The “actors” with thread pool and proccesses is one idea, but I think

these servers are aiming also for speed and scaleability - don’t know if
they would accept this model. I know that it does get used, and do work for
a company that uses it on their backend (just plain Java, not sep.
processes). Their front-end probably runs like a pre-fork server, but Java
is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some
cost. At Swift's current maturity level, that's really probably the only
responsible isolation strategy. The goal of "actors" would be to get most
of the benefits of processes through language-level isolation without
paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I
would just emphasize that the webservers in existence for Swift (and quite
well funded) are right now on a “crash the whole thing” model AFAIK. I
don’t know if they will be able to get by with catching all the
precondition failures in testing - maybe they will - maybe not, but I see
people either wisely turning down Swift for web development or getting
their first crash as a “zero-day” system down event where some guy has to
ssh in and actually start the server back up.

I haven't worked directly with server people that much personally, but on
the opportunities when I have been able to talk to people working in this
space, it sounds like they definitely want something better.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura,
Vapor, etc) will have to like it and implement it. If they’re not
interested, I’m not starting a development company to compete.

I don't see a problem with people developing frameworks to the best of the
platform's ability today and refining their approach as new capabilities
get developed.

And also the development time is so short on my idea that you basically
just have to approve it and the development time for it is hours not months
or years. Just add a fatalError function override and any c developer could
have it done in minutes like this guy
GitHub - ankurp/unsafe-swift: C experiments in Swift (not
endorsing the post, just showing evidence that setjmp and longjmp work fine
with swift).

setjmp and longjmp do *not* work well with Swift since they need compiler
support to implement their semantics, and since Swift does not provide this
support, setjmp-ing from Swift is undefined behavior. Empirical evidence
that small examples appear to work is not a good way of evaluating UB,
since any changes to Swift or LLVM optimizations may break it. Ease of
implementation is also not a good criterion for designing things.
*Supporting* a trap hook is not easy; it still has serious language
semantics and runtime design issues, and may limit our ability to do
something better.

-Joe

Yeah, I suppose. Sounds like it is still about 2-3 years off.

···

On March 23, 2017 at 12:01:48 PM, Joe Groff (jgroff@apple.com) wrote:
On Mar 22, 2017, at 7:25 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:
On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com) wrote:

On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:

Could we do something useful here without setjmp/longjmp? Suppose we had a function in the standard library that was roughly equivalent to this:

  typealias UnsafeFatalErrorCleanupHandler = () -> Void
  
  // Note the imaginary @_thread_local property behavior
  @_thread_local var _fatalErrorCleanupHandlers: [UnsafeFatalErrorCleanupHandler] =
  
  func withUnsafeFatalErrorCleanupHandler<R>(_ handler: UnsafeFatalErrorCleanupHandler, do body: () throws -> R) rethrows -> R {
    _fatalErrorCleanupHandlers.append(handler)
    defer { _fatalErrorCleanupHandlers.removeLast() }
    return try body()
  }

And then, just before we trap, we do something like this:

  // Mutating it this way ensures that, if we reenter fatalError() in one of the handlers,
  // it will pick up with the next handler.
  while let handler = _fatalErrorCleanupHandlers.popLast() {
    handler()
  }

I think that would allow frameworks to do something like:

  class Worker {
    let queue = DispatchQueue(label: "Worker")
    typealias RequestCompletion = (RequestStatus) -> Void

    enum RequestStatus {
      case responded
      case crashing
    }
    
    func beginRequest(from conn: IOHandle, then completion: RequestCompletion) {
      queue.async {
        withUnsafeFatalErrorCleanupHandler(fatalErrorHandler(completion)) {
          // Handle the request, potentially crashing
        }
      }
    }

    func fatalErrorHandler(_ completion: RequestCompletion) -> UnsafeFatalErrorCleanupHandler {
      return { completion(.crashing) }
    }
  }

  class Supervisor {
    let queue = DispatchQueue(label: "Supervisor")
    var workerPool: Pool<Worker>

    func startListening(to sockets: [IOHandle]) { … }
    func stopListening() { … }
    
    func bootReplacement() { … }
    
    func connected(by conn: IOHandle) {
      dispatchPrecondition(condition: .onQueue(queue))
      
      let worker = workerPool.reserve()
      
      worker.beginRequest(from: conn) { status in
        switch status {
        case .responded:
          conn.close()
          self.queue.sync {
            self.workerPool.relinquish(worker)
          }
        
        case .crashing:
          // Uh oh, we're in trouble.
          //
          // This process is toast; it will not survive long beyond this stack frame.
          // We want to close our listening socket, start a replacement server,
          // and then just try to hang on until the other workers have finished their
          // current requests.
          //
          // It'd be nice to send an error message and close the connection,
          // but we shouldn't. We don't know what state the connection or the
          // worker are in, so we don't want to do anything to them.We risk a
          // `!==` check because it only involves a memory address stored in
          // our closure context, not the actual object being referenced by it.
          
          // Go exclusive on the supervisor's queue so we don't try to handle
          // two crashes at once (or a crash and something else, for that matter).
          self.queue.sync {
            self.stopListening()
            self.bootReplacement()
            
            // Try to keep the process alive until all of the surviving workers have
            // finished their current requests. To do this, we'll perform barrier blocks
            // on all of their queues, attached to a single dispatch group, and then
            // wait for the group to complete.
            let group = DispatchGroup()
            
            for otherWorker in self.workerPool.all where otherWorker !== worker {
              // We run this as `.background` to try to let anything else the request might
              // enqueue run first, and `.barrier` to make sure we're the only thing running.
              otherWorker.queue.async(group: group, qos: .background, flags: .barrier) {
                // Make sure we don't do anything else.
                otherWorker.queue.suspend()
              }
            }
            
            // We do not use `notify` because we need this stack frame to keep
            // running so we don't trap yet.
            group.wait(timeout: .now() + .seconds(15))
          }
          
          // Okay, we can now return, and probably crash.
        }
      }
    }
  }

It's definitely not a full actor model, and you have to be very careful, but it might be a useful subset.

···

On Mar 23, 2017, at 9:01 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

setjmp and longjmp do *not* work well with Swift since they need compiler support to implement their semantics, and since Swift does not provide this support, setjmp-ing from Swift is undefined behavior. Empirical evidence that small examples appear to work is not a good way of evaluating UB, since any changes to Swift or LLVM optimizations may break it. Ease of implementation is also not a good criterion for designing things. *Supporting* a trap hook is not easy; it still has serious language semantics and runtime design issues, and may limit our ability to do something better.

--
Brent Royal-Gordon
Architechies

There are linters that will do this for you. In particular swiftlint has a force_unwrapping rule that you can enable. You can add a script build phase to your Xcode project and e.g. have it run only when archiving the project, etc.

···

On Mar 13, 2017, at 5:59 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org> wrote:

For my own part, I never want to use “!” in my code, and I’m sticking to it pretty well.

It has been said many times that we don’t want to create different dialects of Swift, and I agree. Would it be considered a different dialect to add a compiler option that forbids the use of “!” in my own code? So someone else looking at or compiling my code, they could not tell that I used such an option, so I think it fits within the definition of non-dialectedness.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

The problem there is that shared data in Swift always has a retain count, so we'd need some way to track when code in one of these "mini-processes" retains an external object so we can release it if it crashes. Perhaps you'd need to wrap a proxy type around shared objects (or instances containing shared objects, like Array and String) and the proxies would register themselves with the runtime as needing emergency cleanup beyond unceremonious deallocation of their "mini-process"'s memory space. This is all doable; it just needs to be designed and done.

···

On Mar 13, 2017, at 5:54 PM, Matthew Johnson <matthew@anandabits.com> wrote:

I suspect you're right about this for the most part but I think we will want the ability for "failable processes" (or whatever they're called) to have read-only access to shared state that outlives them. What I can't imagine is allowing them write access to state that is shared.

--
Brent Royal-Gordon
Sent from my iPhone

Hi,

Note that this thread has branched off a bit, so my latest proposal and

the “head” of this thread can be found at
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034189.html

Can you give an example of this corruption which would be exploitable in

a web server context? An example where, having caught the force-unwrap or
other precondition fatalError, that the web server would face more danger
from continued execution that it would loose by crashing?

The optional has been triggered, the request has been stopped, and

incoming requests are going to a new process, but there are a few threads
open still finishing what they were doing - I’d like to see a good example
of the danger caused by a real “inconsistency”. Lets assume also that all
objects shared between threads are Swift objects and not “UnsafePointers”,
which is a very fair assumtion for a web server. Java even allows native
access and they don’t even crash there.

When considering security threats, we have to take the presence of an
exploit as given until we prove otherwise. A precondition failure indicates
a logic error somewhere in the program, and while safe Swift abstractions
are intended not to lead to memory unsafety in the face of precondition
failures, there's plenty of relatively young, unsafe code below the hood in
the implementations of those abstractions, written by humans who make
mistakes. Whatever lurking security bugs there may be, they're more likely
to be exploitable if you're already in a situation where assumptions have
been violated.

I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or
relaunch their app. Security breaches or data corruption are disasters that
can have unbounded impact and cost—you can't undisclose sensitive data.

I’d be interested to know who these web server developers (ex. Kitura,

Vapor) are targeting without any form of crash handling and if they read
these threads. It really limits the pool of developers more so than on apps
because a single crash on the app ie equivelant to a failed request, not a
crashed web server. I realize you are not competing with Java, but I do not
see yet the compelling reason for no crash handling.

The “actors” with thread pool and proccesses is one idea, but I think

these servers are aiming also for speed and scaleability - don’t know if
they would accept this model. I know that it does get used, and do work for
a company that uses it on their backend (just plain Java, not sep.
processes). Their front-end probably runs like a pre-fork server, but Java
is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some
cost. At Swift's current maturity level, that's really probably the only
responsible isolation strategy. The goal of "actors" would be to get most
of the benefits of processes through language-level isolation without
paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I
would just emphasize that the webservers in existence for Swift (and quite
well funded) are right now on a “crash the whole thing” model AFAIK. I
don’t know if they will be able to get by with catching all the
precondition failures in testing - maybe they will - maybe not, but I see
people either wisely turning down Swift for web development or getting
their first crash as a “zero-day” system down event where some guy has to
ssh in and actually start the server back up.

I haven't worked directly with server people that much personally, but on
the opportunities when I have been able to talk to people working in this
space, it sounds like they definitely want something better.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura,
Vapor, etc) will have to like it and implement it. If they’re not
interested, I’m not starting a development company to compete.

I don't see a problem with people developing frameworks to the best of the
platform's ability today and refining their approach as new capabilities
get developed.

And also the development time is so short on my idea that you basically
just have to approve it and the development time for it is hours not months
or years. Just add a fatalError function override and any c developer could
have it done in minutes like this guy
GitHub - ankurp/unsafe-swift: C experiments in Swift (not
endorsing the post, just showing evidence that setjmp and longjmp work fine
with swift).

setjmp and longjmp do *not* work well with Swift since they need compiler
support to implement their semantics, and since Swift does not provide this
support, setjmp-ing from Swift is undefined behavior. Empirical evidence
that small examples appear to work is not a good way of evaluating UB,
since any changes to Swift or LLVM optimizations may break it. Ease of
implementation is also not a good criterion for designing things.
*Supporting* a trap hook is not easy; it still has serious language
semantics and runtime design issues, and may limit our ability to do
something better.

-Joe

Yeah, I suppose. Sounds like it is still about 2-3 years off.

But…. what happens when a shared or shared sub-process trips? If something
about the process was shared, then it will end up tripping every other
process except some master process which will re-generate them.

Concerning longjmp, it is possible to call from Swift into C, and then from
C into Swift (not in the example, but in general). Therefore, assuming that
the execution state of the C function is still on the stack at the moment
that the exception trips, the jump won’t necessarily involve the Swift
runtime or compiler at all, so far as I can see, aside from leaked memory.
Granted I don’t know that much about the runtime, but an example might be
useful.

The linked example is was done inline, so a bit different than I was
thinking.

BTW, not sure why no one mentioned it, but apparently the whole idea
proposed is already possible without any proposal actually needed. A bit
too complex for me and dangerous looking, but heres a nice post with it
already done.

···

On March 25, 2017 at 10:24:23 AM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 23, 2017 at 12:01:48 PM, Joe Groff (jgroff@apple.com) wrote:
On Mar 22, 2017, at 7:25 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:
On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com) wrote:

On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:

setjmp and longjmp do *not* work well with Swift since they need compiler
support to implement their semantics, and since Swift does not provide this
support, setjmp-ing from Swift is undefined behavior. Empirical evidence
that small examples appear to work is not a good way of evaluating UB,
since any changes to Swift or LLVM optimizations may break it. Ease of
implementation is also not a good criterion for designing things.
*Supporting* a trap hook is not easy; it still has serious language
semantics and runtime design issues, and may limit our ability to do
something better.

Could we do something useful here without setjmp/longjmp? Suppose we had a
function in the standard library that was roughly equivalent to this:

typealias UnsafeFatalErrorCleanupHandler = () -> Void
// Note the imaginary @_thread_local property behavior
@_thread_local var _fatalErrorCleanupHandlers:
[UnsafeFatalErrorCleanupHandler] =
func withUnsafeFatalErrorCleanupHandler<R>(_ handler:
UnsafeFatalErrorCleanupHandler, do body: () throws -> R) rethrows -> R {
_fatalErrorCleanupHandlers.append(handler)
defer { _fatalErrorCleanupHandlers.removeLast() }
return try body()
}

And then, just before we trap, we do something like this:

// Mutating it this way ensures that, if we reenter fatalError() in one of
the handlers,
// it will pick up with the next handler.
while let handler = _fatalErrorCleanupHandlers.popLast() {
handler()
}

I think that would allow frameworks to do something like:

class Worker {
let queue = DispatchQueue(label: "Worker")
typealias RequestCompletion = (RequestStatus) -> Void

enum RequestStatus {
case responded
case crashing
}
func beginRequest(from conn: IOHandle, then completion: RequestCompletion) {
queue.async {
withUnsafeFatalErrorCleanupHandler(fatalErrorHandler(completion)) {
// Handle the request, potentially crashing
}
}
}

func fatalErrorHandler(_ completion: RequestCompletion) ->
UnsafeFatalErrorCleanupHandler {
return { completion(.crashing) }
}
}

class Supervisor {
let queue = DispatchQueue(label: "Supervisor")
var workerPool: Pool<Worker>

func startListening(to sockets: [IOHandle]) { … }
func stopListening() { … }
func bootReplacement() { … }
func connected(by conn: IOHandle) {
dispatchPrecondition(condition: .onQueue(queue))
let worker = workerPool.reserve()
worker.beginRequest(from: conn) { status in
switch status {
case .responded:
conn.close()
self.queue.sync {
self.workerPool.relinquish(worker)
}
case .crashing:
// Uh oh, we're in trouble.
//
// This process is toast; it will not survive long beyond this stack frame.
// We want to close our listening socket, start a replacement server,
// and then just try to hang on until the other workers have finished their
// current requests.
//
// It'd be nice to send an error message and close the connection,
// but we shouldn't. We don't know what state the connection or the
// worker are in, so we don't want to do anything to them.We risk a
// `!==` check because it only involves a memory address stored in
// our closure context, not the actual object being referenced by it.
// Go exclusive on the supervisor's queue so we don't try to handle
// two crashes at once (or a crash and something else, for that matter).
self.queue.sync {
self.stopListening()
self.bootReplacement()
// Try to keep the process alive until all of the surviving workers have
// finished their current requests. To do this, we'll perform barrier
blocks
// on all of their queues, attached to a single dispatch group, and then
// wait for the group to complete.
let group = DispatchGroup()
for otherWorker in self.workerPool.all where otherWorker !== worker {
// We run this as `.background` to try to let anything else the request
might
// enqueue run first, and `.barrier` to make sure we're the only thing
running.
otherWorker.queue.async(group: group, qos: .background, flags: .barrier) {
// Make sure we don't do anything else.
otherWorker.queue.suspend()
}
}
// We do not use `notify` because we need this stack frame to keep
// running so we don't trap yet.
group.wait(timeout: .now() + .seconds(15))
}
// Okay, we can now return, and probably crash.
}
}
}
}

It's definitely not a full actor model, and you have to be very careful,
but it might be a useful subset.

···

On March 26, 2017 at 12:09:55 AM, Brent Royal-Gordon (brent@architechies.com) wrote:

On Mar 23, 2017, at 9:01 AM, Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

Well, it seems like it would work pretty well since a thread would be
needed anyway to do the cleanup. At the end it still boils down to a
fatalError handling proposal and the web framework developer(s) put the
implmentation.

If the actor model sufficiently replaces it, then it makes still a good
bridge between now and then ie. Swift 5-7. If a shared actor trips, then it
also trips the rest of the non-shared actors who use it, so it still has
some potential to hold that shared actor open until the requests using it
finish execution.

We might as well go all the way and have actors and some form of software transactional memory, or at least the low-level building blocks to create something of the sort.

···

On Mar 13, 2017, at 9:18 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

On Mar 13, 2017, at 5:54 PM, Matthew Johnson <matthew@anandabits.com> wrote:

I suspect you're right about this for the most part but I think we will want the ability for "failable processes" (or whatever they're called) to have read-only access to shared state that outlives them. What I can't imagine is allowing them write access to state that is shared.

The problem there is that shared data in Swift always has a retain count, so we'd need some way to track when code in one of these "mini-processes" retains an external object so we can release it if it crashes. Perhaps you'd need to wrap a proxy type around shared objects (or instances containing shared objects, like Array and String) and the proxies would register themselves with the runtime as needing emergency cleanup beyond unceremonious deallocation of their "mini-process"'s memory space. This is all doable; it just needs to be designed and done.

--
Brent Royal-Gordon
Sent from my iPhone

Sounds like a good idea. I assume that Swift would need to compile these
proxies in advance to be sub-classes ie. binary compatible objects that
will forward calls and return proxies. “Value” types can be copied as they
normally are, or wrapped by the user (along with “final” objects which
could be wrapped manually or made not-final). Then you’d just need a way to
get your first proxy object and a way to register the process’s object.
Something like

    func openSharedObject(pid:String) throws -> Any? // cast to expected
value

    func registerSharedProxyObject(object:Any) // for this process

I suppose the caller would have to be the one to compile these proxy
objects and locate them in its own module.

The advantage here is that it would be not too difficult for existing
servers to make use of this, as they are already written as a single
process in Swift and so they would just need to manage and pre-fork
processes instead of threads. Seems like it would also have numerous uses
in the systems programming domain.

Then one just needs, ideally, to get a stack trace and fatal error message
to the parent process if a child process crashes.

I suspect you're right about this for the most part but I think we will

want the ability for "failable processes" (or whatever they're called) to
have read-only access to shared state that outlives them. What I can't
imagine is allowing them write access to state that is shared.

The problem there is that shared data in Swift always has a retain count,
so we'd need some way to track when code in one of these "mini-processes"
retains an external object so we can release it if it crashes. Perhaps
you'd need to wrap a proxy type around shared objects (or instances
containing shared objects, like Array and String) and the proxies would
register themselves with the runtime as needing emergency cleanup beyond
unceremonious deallocation of their "mini-process"'s memory space. This is
all doable; it just needs to be designed and done.

···

On March 14, 2017 at 12:18:37 AM, Brent Royal-Gordon (brent@architechies.com) wrote:

On Mar 13, 2017, at 5:54 PM, Matthew Johnson <matthew@anandabits.com> wrote:

--
Brent Royal-Gordon
Sent from my iPhone

Hi,

Note that this thread has branched off a bit, so my latest proposal and

the “head” of this thread can be found at
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034189.html

Can you give an example of this corruption which would be exploitable in

a web server context? An example where, having caught the force-unwrap or
other precondition fatalError, that the web server would face more danger
from continued execution that it would loose by crashing?

The optional has been triggered, the request has been stopped, and

incoming requests are going to a new process, but there are a few threads
open still finishing what they were doing - I’d like to see a good example
of the danger caused by a real “inconsistency”. Lets assume also that all
objects shared between threads are Swift objects and not “UnsafePointers”,
which is a very fair assumtion for a web server. Java even allows native
access and they don’t even crash there.

When considering security threats, we have to take the presence of an
exploit as given until we prove otherwise. A precondition failure indicates
a logic error somewhere in the program, and while safe Swift abstractions
are intended not to lead to memory unsafety in the face of precondition
failures, there's plenty of relatively young, unsafe code below the hood in
the implementations of those abstractions, written by humans who make
mistakes. Whatever lurking security bugs there may be, they're more likely
to be exploitable if you're already in a situation where assumptions have
been violated.

I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or
relaunch their app. Security breaches or data corruption are disasters that
can have unbounded impact and cost—you can't undisclose sensitive data.

I’d be interested to know who these web server developers (ex. Kitura,

Vapor) are targeting without any form of crash handling and if they read
these threads. It really limits the pool of developers more so than on apps
because a single crash on the app ie equivelant to a failed request, not a
crashed web server. I realize you are not competing with Java, but I do not
see yet the compelling reason for no crash handling.

The “actors” with thread pool and proccesses is one idea, but I think

these servers are aiming also for speed and scaleability - don’t know if
they would accept this model. I know that it does get used, and do work for
a company that uses it on their backend (just plain Java, not sep.
processes). Their front-end probably runs like a pre-fork server, but Java
is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some
cost. At Swift's current maturity level, that's really probably the only
responsible isolation strategy. The goal of "actors" would be to get most
of the benefits of processes through language-level isolation without
paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I
would just emphasize that the webservers in existence for Swift (and quite
well funded) are right now on a “crash the whole thing” model AFAIK. I
don’t know if they will be able to get by with catching all the
precondition failures in testing - maybe they will - maybe not, but I see
people either wisely turning down Swift for web development or getting
their first crash as a “zero-day” system down event where some guy has to
ssh in and actually start the server back up.

I haven't worked directly with server people that much personally, but on
the opportunities when I have been able to talk to people working in this
space, it sounds like they definitely want something better.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura,
Vapor, etc) will have to like it and implement it. If they’re not
interested, I’m not starting a development company to compete.

I don't see a problem with people developing frameworks to the best of the
platform's ability today and refining their approach as new capabilities
get developed.

And also the development time is so short on my idea that you basically
just have to approve it and the development time for it is hours not months
or years. Just add a fatalError function override and any c developer could
have it done in minutes like this guy
GitHub - ankurp/unsafe-swift: C experiments in Swift (not
endorsing the post, just showing evidence that setjmp and longjmp work fine
with swift).

setjmp and longjmp do *not* work well with Swift since they need compiler
support to implement their semantics, and since Swift does not provide this
support, setjmp-ing from Swift is undefined behavior. Empirical evidence
that small examples appear to work is not a good way of evaluating UB,
since any changes to Swift or LLVM optimizations may break it. Ease of
implementation is also not a good criterion for designing things.
*Supporting* a trap hook is not easy; it still has serious language
semantics and runtime design issues, and may limit our ability to do
something better.

-Joe

Yeah, I suppose. Sounds like it is still about 2-3 years off.

But…. what happens when a shared or shared sub-process trips? If something
about the process was shared, then it will end up tripping every other
process except some master process which will re-generate them.

Concerning longjmp, it is possible to call from Swift into C, and then from
C into Swift (not in the example, but in general). Therefore, assuming that
the execution state of the C function is still on the stack at the moment
that the exception trips, the jump won’t necessarily involve the Swift
runtime or compiler at all, so far as I can see, aside from leaked memory.
Granted I don’t know that much about the runtime, but an example might be
useful.

The linked example is was done inline, so a bit different than I was
thinking.

BTW, not sure why no one mentioned it, but apparently the whole idea
proposed is already possible without any proposal actually needed. A bit
too complex for me and dangerous looking, but heres a nice post with it
already done.

How about going half way on a proposal for a “fatalError” callback, which
would take a “c” function (or be accessible only from c) to prevent
recursion. ie. if the handler was in Swift the callback would then be
liable to induce another fatalError.

The purpose of which would be to log the fatal error string to a file.

int fatalErrorCallback(char* message, char* stacktrace);

Any other usages would be unsupported and something to discuss in the dev
channel (no bearing on the ABI or compiler design, only the reverse). If it
returns “0” then Swift omits its own std output, but in either case it
exits.

One can see easily from the error printout that Swift is running its own
callback, even in Swift:

fatal error: unexpectedly found nil while unwrapping an Optional value

Current stack trace:

0 TestJmp1 0x0000000104b11410
swift_reportError + 132

1 TestJmp1 0x0000000104b2e7c0
_swift_stdlib_reportFatalError + 61

2 TestJmp1 0x000000010489c550 specialized
specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) ->
A) -> A + 355

3 TestJmp1 0x0000000104a9e7f0 partial apply
for (_fatalErrorMessage(StaticString, StaticString, StaticString, UInt,
flags : UInt32) -> Never).(closure #2) + 109

4 TestJmp1 0x000000010489c550 specialized
specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) ->
A) -> A + 355

5 TestJmp1 0x0000000104a59170 specialized
_fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags :
UInt32) -> Never + 96

6 TestJmp1 0x00000001047f87f0 (closure #1) +
221

7 TestJmp1 0x00000001047f8a30 @objc (closure
#1) + 9

8 TestJmp1 0x00000001047f8ae0 runCode + 49

9 TestJmp1 0x00000001047f8700 main + 87

10 libdyld.dylib 0x00007fffe1deb254 start + 1

Illegal instruction: 4

···

On March 25, 2017 at 7:15:59 PM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 25, 2017 at 10:24:23 AM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 23, 2017 at 12:01:48 PM, Joe Groff (jgroff@apple.com) wrote:
On Mar 22, 2017, at 7:25 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:
On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com) wrote:

On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:

Also the proxies would need to implement some protocol so that they can be
de-proxied when sent back to a shared method, and the proxy object exposed
to the user with a new declaration that accepts only proxies. Like using
MyClass -> MyClassSharedProxy for the proxy class and

class MyClass {

    func testReturnsObject() -> SomeClass
    func call(a:MyClass) -> MyClass

}

would become

class MyClassSharedProxy : MyClass, SharedProxyProtocol {

    func testReturnsObject() -> SomeClassSharedProxy
    func call(a:MyClassSharedProxy) -> MyClassSharedProxy

}

for example.

Sounds like a good idea. I assume that Swift would need to compile these
proxies in advance to be sub-classes ie. binary compatible objects that
will forward calls and return proxies. “Value” types can be copied as they
normally are, or wrapped by the user (along with “final” objects which
could be wrapped manually or made not-final). Then you’d just need a way to
get your first proxy object and a way to register the process’s object.
Something like

    func openSharedObject(pid:String) throws -> Any? // cast to expected
value

    func registerSharedProxyObject(object:Any) // for this process

I suppose the caller would have to be the one to compile these proxy
objects and locate them in its own module.

The advantage here is that it would be not too difficult for existing
servers to make use of this, as they are already written as a single
process in Swift and so they would just need to manage and pre-fork
processes instead of threads. Seems like it would also have numerous uses
in the systems programming domain.

Then one just needs, ideally, to get a stack trace and fatal error message
to the parent process if a child process crashes.

I suspect you're right about this for the most part but I think we will

want the ability for "failable processes" (or whatever they're called) to
have read-only access to shared state that outlives them. What I can't
imagine is allowing them write access to state that is shared.

The problem there is that shared data in Swift always has a retain count,
so we'd need some way to track when code in one of these "mini-processes"
retains an external object so we can release it if it crashes. Perhaps
you'd need to wrap a proxy type around shared objects (or instances
containing shared objects, like Array and String) and the proxies would
register themselves with the runtime as needing emergency cleanup beyond
unceremonious deallocation of their "mini-process"'s memory space. This is
all doable; it just needs to be designed and done.

···

On March 14, 2017 at 7:15:08 PM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 14, 2017 at 12:18:37 AM, Brent Royal-Gordon (brent@architechies.com) wrote:

On Mar 13, 2017, at 5:54 PM, Matthew Johnson <matthew@anandabits.com> wrote:

--
Brent Royal-Gordon
Sent from my iPhone

If all you can do is log an alternative to what Swift would output anyway, what's the use case? And certainly permitting more things to happen in that callback falls under the same caveats as an in-process crash reporter - compromised context, uncertain data state, very few safe operations.

-- Gwynne Raskind

···

On Mar 25, 2017, at 19:27, Elijah Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On March 25, 2017 at 7:15:59 PM, Elijah Johnson (ejrx7753@gmail.com <mailto:ejrx7753@gmail.com>) wrote:

On March 25, 2017 at 10:24:23 AM, Elijah Johnson (ejrx7753@gmail.com <mailto:ejrx7753@gmail.com>) wrote:

On March 23, 2017 at 12:01:48 PM, Joe Groff (jgroff@apple.com <mailto:jgroff@apple.com>) wrote:

On Mar 22, 2017, at 7:25 PM, Elijah Johnson <ejrx7753@gmail.com <mailto:ejrx7753@gmail.com>> wrote:

On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com <mailto:jgroff@apple.com>) wrote:

> On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com <mailto:ejrx7753@gmail.com>> wrote:
>
> Hi,
>
> Note that this thread has branched off a bit, so my latest proposal and the “head” of this thread can be found at [swift-evolution] Swift null safety questions
>
>
>
> Can you give an example of this corruption which would be exploitable in a web server context? An example where, having caught the force-unwrap or other precondition fatalError, that the web server would face more danger from continued execution that it would loose by crashing?
>
> The optional has been triggered, the request has been stopped, and incoming requests are going to a new process, but there are a few threads open still finishing what they were doing - I’d like to see a good example of the danger caused by a real “inconsistency”. Lets assume also that all objects shared between threads are Swift objects and not “UnsafePointers”, which is a very fair assumtion for a web server. Java even allows native access and they don’t even crash there.

When considering security threats, we have to take the presence of an exploit as given until we prove otherwise. A precondition failure indicates a logic error somewhere in the program, and while safe Swift abstractions are intended not to lead to memory unsafety in the face of precondition failures, there's plenty of relatively young, unsafe code below the hood in the implementations of those abstractions, written by humans who make mistakes. Whatever lurking security bugs there may be, they're more likely to be exploitable if you're already in a situation where assumptions have been violated.

> I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or relaunch their app. Security breaches or data corruption are disasters that can have unbounded impact and cost—you can't undisclose sensitive data.

> I’d be interested to know who these web server developers (ex. Kitura, Vapor) are targeting without any form of crash handling and if they read these threads. It really limits the pool of developers more so than on apps because a single crash on the app ie equivelant to a failed request, not a crashed web server. I realize you are not competing with Java, but I do not see yet the compelling reason for no crash handling.
>
> The “actors” with thread pool and proccesses is one idea, but I think these servers are aiming also for speed and scaleability - don’t know if they would accept this model. I know that it does get used, and do work for a company that uses it on their backend (just plain Java, not sep. processes). Their front-end probably runs like a pre-fork server, but Java is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some cost. At Swift's current maturity level, that's really probably the only responsible isolation strategy. The goal of "actors" would be to get most of the benefits of processes through language-level isolation without paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I would just emphasize that the webservers in existence for Swift (and quite well funded) are right now on a “crash the whole thing” model AFAIK. I don’t know if they will be able to get by with catching all the precondition failures in testing - maybe they will - maybe not, but I see people either wisely turning down Swift for web development or getting their first crash as a “zero-day” system down event where some guy has to ssh in and actually start the server back up.

I haven't worked directly with server people that much personally, but on the opportunities when I have been able to talk to people working in this space, it sounds like they definitely want something better.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura, Vapor, etc) will have to like it and implement it. If they’re not interested, I’m not starting a development company to compete.

I don't see a problem with people developing frameworks to the best of the platform's ability today and refining their approach as new capabilities get developed.

And also the development time is so short on my idea that you basically just have to approve it and the development time for it is hours not months or years. Just add a fatalError function override and any c developer could have it done in minutes like this guy GitHub - ankurp/unsafe-swift: C experiments in Swift (not endorsing the post, just showing evidence that setjmp and longjmp work fine with swift).

setjmp and longjmp do *not* work well with Swift since they need compiler support to implement their semantics, and since Swift does not provide this support, setjmp-ing from Swift is undefined behavior. Empirical evidence that small examples appear to work is not a good way of evaluating UB, since any changes to Swift or LLVM optimizations may break it. Ease of implementation is also not a good criterion for designing things. *Supporting* a trap hook is not easy; it still has serious language semantics and runtime design issues, and may limit our ability to do something better.

-Joe

Yeah, I suppose. Sounds like it is still about 2-3 years off.

But…. what happens when a shared or shared sub-process trips? If something about the process was shared, then it will end up tripping every other process except some master process which will re-generate them.

Concerning longjmp, it is possible to call from Swift into C, and then from C into Swift (not in the example, but in general). Therefore, assuming that the execution state of the C function is still on the stack at the moment that the exception trips, the jump won’t necessarily involve the Swift runtime or compiler at all, so far as I can see, aside from leaked memory. Granted I don’t know that much about the runtime, but an example might be useful.

The linked example is was done inline, so a bit different than I was thinking.

BTW, not sure why no one mentioned it, but apparently the whole idea proposed is already possible without any proposal actually needed. A bit too complex for me and dangerous looking, but heres a nice post with it already done.

Partial functions in Swift, Part 2: Catching precondition failures

How about going half way on a proposal for a “fatalError” callback, which would take a “c” function (or be accessible only from c) to prevent recursion. ie. if the handler was in Swift the callback would then be liable to induce another fatalError.

The purpose of which would be to log the fatal error string to a file.

int fatalErrorCallback(char* message, char* stacktrace);
Any other usages would be unsupported and something to discuss in the dev channel (no bearing on the ABI or compiler design, only the reverse). If it returns “0” then Swift omits its own std output, but in either case it exits.

One can see easily from the error printout that Swift is running its own callback, even in Swift:

fatal error: unexpectedly found nil while unwrapping an Optional value

Current stack trace:

0 TestJmp1 0x0000000104b11410 swift_reportError + 132

1 TestJmp1 0x0000000104b2e7c0 _swift_stdlib_reportFatalError + 61

2 TestJmp1 0x000000010489c550 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355

3 TestJmp1 0x0000000104a9e7f0 partial apply for (_fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never).(closure #2) + 109

4 TestJmp1 0x000000010489c550 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355

5 TestJmp1 0x0000000104a59170 specialized _fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never + 96

6 TestJmp1 0x00000001047f87f0 (closure #1) + 221

7 TestJmp1 0x00000001047f8a30 @objc (closure #1) + 9

8 TestJmp1 0x00000001047f8ae0 runCode + 49

9 TestJmp1 0x00000001047f8700 main + 87

10 libdyld.dylib 0x00007fffe1deb254 start + 1

Illegal instruction: 4

But - that would require a dedicated thread or a thread pool to run
function calls on. Definitely it lacks many of the advantages of shared
objects directly in the process. I would definitely prefer a small memory
leak or inconsistent state. The server could handle existing requests and
restart itself if it were really an issue. A memory leak can be estimated
and the server restarted after every “x” times, while an inconsistent state
is something that happens in any shared server environment. The developers
just don’t modify the shared state until they have ready the final object,
and their array/dictionary put function call itself isn’t likely to be the
fail that happened.

Also the proxies would need to implement some protocol so that they can be
de-proxied when sent back to a shared method, and the proxy object exposed
to the user with a new declaration that accepts only proxies. Like using
MyClass -> MyClassSharedProxy for the proxy class and

class MyClass {

    func testReturnsObject() -> SomeClass
    func call(a:MyClass) -> MyClass

}

would become

class MyClassSharedProxy : MyClass, SharedProxyProtocol {

    func testReturnsObject() -> SomeClassSharedProxy
    func call(a:MyClassSharedProxy) -> MyClassSharedProxy

}

for example.

Sounds like a good idea. I assume that Swift would need to compile these
proxies in advance to be sub-classes ie. binary compatible objects that
will forward calls and return proxies. “Value” types can be copied as they
normally are, or wrapped by the user (along with “final” objects which
could be wrapped manually or made not-final). Then you’d just need a way to
get your first proxy object and a way to register the process’s object.
Something like

    func openSharedObject(pid:String) throws -> Any? // cast to expected
value

    func registerSharedProxyObject(object:Any) // for this process

I suppose the caller would have to be the one to compile these proxy
objects and locate them in its own module.

The advantage here is that it would be not too difficult for existing
servers to make use of this, as they are already written as a single
process in Swift and so they would just need to manage and pre-fork
processes instead of threads. Seems like it would also have numerous uses
in the systems programming domain.

Then one just needs, ideally, to get a stack trace and fatal error message
to the parent process if a child process crashes.

I suspect you're right about this for the most part but I think we will

want the ability for "failable processes" (or whatever they're called) to
have read-only access to shared state that outlives them. What I can't
imagine is allowing them write access to state that is shared.

The problem there is that shared data in Swift always has a retain count,
so we'd need some way to track when code in one of these "mini-processes"
retains an external object so we can release it if it crashes. Perhaps
you'd need to wrap a proxy type around shared objects (or instances
containing shared objects, like Array and String) and the proxies would
register themselves with the runtime as needing emergency cleanup beyond
unceremonious deallocation of their "mini-process"'s memory space. This is
all doable; it just needs to be designed and done.

···

On March 14, 2017 at 7:29:28 PM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 14, 2017 at 7:15:08 PM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 14, 2017 at 12:18:37 AM, Brent Royal-Gordon (brent@architechies.com) wrote:

On Mar 13, 2017, at 5:54 PM, Matthew Johnson <matthew@anandabits.com> wrote:

--
Brent Royal-Gordon
Sent from my iPhone

Hi,

Note that this thread has branched off a bit, so my latest proposal and

the “head” of this thread can be found at
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170320/034189.html

Can you give an example of this corruption which would be exploitable in

a web server context? An example where, having caught the force-unwrap or
other precondition fatalError, that the web server would face more danger
from continued execution that it would loose by crashing?

The optional has been triggered, the request has been stopped, and

incoming requests are going to a new process, but there are a few threads
open still finishing what they were doing - I’d like to see a good example
of the danger caused by a real “inconsistency”. Lets assume also that all
objects shared between threads are Swift objects and not “UnsafePointers”,
which is a very fair assumtion for a web server. Java even allows native
access and they don’t even crash there.

When considering security threats, we have to take the presence of an
exploit as given until we prove otherwise. A precondition failure indicates
a logic error somewhere in the program, and while safe Swift abstractions
are intended not to lead to memory unsafety in the face of precondition
failures, there's plenty of relatively young, unsafe code below the hood in
the implementations of those abstractions, written by humans who make
mistakes. Whatever lurking security bugs there may be, they're more likely
to be exploitable if you're already in a situation where assumptions have
been violated.

I’m sorry, but I completely fail to grasp the danger as being > crashing.

Crashing is inconvenient. The user may have to retry their request or
relaunch their app. Security breaches or data corruption are disasters that
can have unbounded impact and cost—you can't undisclose sensitive data.

I’d be interested to know who these web server developers (ex. Kitura,

Vapor) are targeting without any form of crash handling and if they read
these threads. It really limits the pool of developers more so than on apps
because a single crash on the app ie equivelant to a failed request, not a
crashed web server. I realize you are not competing with Java, but I do not
see yet the compelling reason for no crash handling.

The “actors” with thread pool and proccesses is one idea, but I think

these servers are aiming also for speed and scaleability - don’t know if
they would accept this model. I know that it does get used, and do work for
a company that uses it on their backend (just plain Java, not sep.
processes). Their front-end probably runs like a pre-fork server, but Java
is so stable that there is simply no need for that.

You can use processes for fault isolation today, and that will have some
cost. At Swift's current maturity level, that's really probably the only
responsible isolation strategy. The goal of "actors" would be to get most
of the benefits of processes through language-level isolation without
paying the full cost of processes.

-Joe

I have no personal objection to actors or processes or anything else. I
would just emphasize that the webservers in existence for Swift (and quite
well funded) are right now on a “crash the whole thing” model AFAIK. I
don’t know if they will be able to get by with catching all the
precondition failures in testing - maybe they will - maybe not, but I see
people either wisely turning down Swift for web development or getting
their first crash as a “zero-day” system down event where some guy has to
ssh in and actually start the server back up.

I haven't worked directly with server people that much personally, but on
the opportunities when I have been able to talk to people working in this
space, it sounds like they definitely want something better.

But whatever the fix is, or nothing, they (developers of Perfect, Kitura,
Vapor, etc) will have to like it and implement it. If they’re not
interested, I’m not starting a development company to compete.

I don't see a problem with people developing frameworks to the best of the
platform's ability today and refining their approach as new capabilities
get developed.

And also the development time is so short on my idea that you basically
just have to approve it and the development time for it is hours not months
or years. Just add a fatalError function override and any c developer could
have it done in minutes like this guy
GitHub - ankurp/unsafe-swift: C experiments in Swift (not
endorsing the post, just showing evidence that setjmp and longjmp work fine
with swift).

setjmp and longjmp do *not* work well with Swift since they need compiler
support to implement their semantics, and since Swift does not provide this
support, setjmp-ing from Swift is undefined behavior. Empirical evidence
that small examples appear to work is not a good way of evaluating UB,
since any changes to Swift or LLVM optimizations may break it. Ease of
implementation is also not a good criterion for designing things.
*Supporting* a trap hook is not easy; it still has serious language
semantics and runtime design issues, and may limit our ability to do
something better.

-Joe

Yeah, I suppose. Sounds like it is still about 2-3 years off.

But…. what happens when a shared or shared sub-process trips? If something
about the process was shared, then it will end up tripping every other
process except some master process which will re-generate them.

Concerning longjmp, it is possible to call from Swift into C, and then from
C into Swift (not in the example, but in general). Therefore, assuming that
the execution state of the C function is still on the stack at the moment
that the exception trips, the jump won’t necessarily involve the Swift
runtime or compiler at all, so far as I can see, aside from leaked memory.
Granted I don’t know that much about the runtime, but an example might be
useful.

The linked example is was done inline, so a bit different than I was
thinking.

BTW, not sure why no one mentioned it, but apparently the whole idea
proposed is already possible without any proposal actually needed. A bit
too complex for me and dangerous looking, but heres a nice post with it
already done.

How about going half way on a proposal for a “fatalError” callback, which
would take a “c” function (or be accessible only from c) to prevent
recursion. ie. if the handler was in Swift the callback would then be
liable to induce another fatalError.

The purpose of which would be to log the fatal error string to a file.
int fatalErrorCallback(char* message, char* stacktrace);

Any other usages would be unsupported and something to discuss in the dev
channel (no bearing on the ABI or compiler design, only the reverse). If it
returns “0” then Swift omits its own std output, but in either case it
exits.

One can see easily from the error printout that Swift is running its own
callback, even in Swift:

fatal error: unexpectedly found nil while unwrapping an Optional value

Current stack trace:

0 TestJmp1 0x0000000104b11410
swift_reportError + 132

1 TestJmp1 0x0000000104b2e7c0
_swift_stdlib_reportFatalError + 61

2 TestJmp1 0x000000010489c550 specialized
specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) ->
A) -> A + 355

3 TestJmp1 0x0000000104a9e7f0 partial apply
for (_fatalErrorMessage(StaticString, StaticString, StaticString, UInt,
flags : UInt32) -> Never).(closure #2) + 109

4 TestJmp1 0x000000010489c550 specialized
specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) ->
A) -> A + 355

5 TestJmp1 0x0000000104a59170 specialized
_fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags :
UInt32) -> Never + 96

6 TestJmp1 0x00000001047f87f0 (closure #1) +
221

7 TestJmp1 0x00000001047f8a30 @objc (closure
#1) + 9

8 TestJmp1 0x00000001047f8ae0 runCode + 49

9 TestJmp1 0x00000001047f8700 main + 87

10 libdyld.dylib 0x00007fffe1deb254 start + 1

Illegal instruction: 4

If all you can do is log an alternative to what Swift would output anyway,
what's the use case? And certainly permitting more things to happen in that
callback falls under the same caveats as an in-process crash reporter -
compromised context, uncertain data state, very few safe operations.

-- Gwynne Raskind

By “in process crash reporter” are you referring to the link above (

)?

The difference between that, and using longjmp (defering the judgment of
what the side effects are to another channel than evolution) would be that
one has specificly caught a "fatalError" and not something else. It
contains the error message and stack trace easily avaiable, and is not
confusable with another "EXE bad instruction”.

As to the supported use case, it would be to write the stack trace to a
file and then exit, or similar operation using isolated C functions. We can
see that the current handler allocates memory and runs instructions on them
in the same thread before it exits. The supported use would be to do the
same, ideally in C which does not compile fatalError instructions (causing
recursion), but uses its own signals to crash.

Not every process wants to pipe its output and error somewhere besides the
console or /dev/null. I’m not aware exactly what currently exists for crash
reporting on Swift, so maybe this is redundant also.

Then, certain developers can discuss the runtime impact of unwiding the
stack via a c longjmp, and weigh its impact in their particular use case,
but not in the forum of swift evolution.

Lets see, in Errors.cpp, you have a function called reportNow (called by
swift_fatalError) with this line:

asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", message);

but it is only for Apple platforms. So you will need a user-defined
callback for linux platforms, in case their linux distro has a system log
they want to write to. Or whatever they want to do with logging.

···

On March 25, 2017 at 9:56:03 PM, Gwynne Raskind (gwynne@darkrainfall.org) wrote:
On Mar 25, 2017, at 19:27, Elijah Johnson via swift-evolution < swift-evolution@swift.org> wrote:
On March 25, 2017 at 7:15:59 PM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 25, 2017 at 10:24:23 AM, Elijah Johnson (ejrx7753@gmail.com) wrote:
On March 23, 2017 at 12:01:48 PM, Joe Groff (jgroff@apple.com) wrote:
On Mar 22, 2017, at 7:25 PM, Elijah Johnson <ejrx7753@gmail.com> wrote:
On March 22, 2017 at 8:41:25 PM, Joe Groff (jgroff@apple.com) wrote:

On Mar 22, 2017, at 4:07 PM, Elijah Johnson <ejrx7753@gmail.com> wrote: