Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The crash seems to be in CoreText. CoreText is embedded/linked in Messages, Spotlight, Springboard, etc. CoreText is written in C.

The fix would be to rewrite CoreText in a memory safe language like Swift. This would be “hard”. Or put CoreText in an XPC container. This would both be “hard” and result in terrible performance.

For more details on how hard C, memory management, systems programming, and operating system development is please refer to your local copy of Modern Operating Systems by Andy Tannenbaum.



Or just move CoreText into its own process, and restart it when it crashes.

The big issue is that when CoreText crashes right now the kernel panics and the device restarts. If CoreText itself could crash safely, get restarted, and the OS continue running then these bugs would go from "significant" to "annoying." Even if CoreText crashing caused individual apps to also crash, that would be a big improvement the current situation.

Obviously we'd all like bug free fonts and text rendering, but if we call that goal aspirational (read: impossible), the best we can hope for today is handling the fault cases better than they're handled today. Bootloops are a pretty lame user experience.


From what I gathered so far this doesn't hit the kernel but the process. It seems to turn out that on iOS one of such processes happens to be Springboard, hence the UI (but not the kernel) gets a kick and restarts.

Maybe I missed something though.


Yeah, Springboard is responsible for not just the home screen but also notifications. So if that sequence of characters arrives in a notification, Springboard will restart… and try to show the notification again… and restart…


That's right, there's no kernel crash AFAICT.

Pissed my niece the hell off though, that I could remotely disable her Messenger.


Well she's right to be pissed if you did that.

On the other hand I had the same thought (but better self-restraint than you) - however I was thinking "nah, scrubbing bad messages server side is just an s/badstring// and I am sure the major non-encrypted messenger apps (where the server knows the strings) added that server-side, so people couldn't crash their contacts' apps, which the app company might get blamed for. This kind of hotfix shouldn't have negative effects, I'm sure there are already a few server-side manipulations of text (stuff like adding a space to very long lines, maybe a blacklist of certain malicious URL's that sort of thing.)

So I'm surprised your message was delivered as sent (if it's not encrypted end to end), unless you did this right when the news broke.


Though it seems like providers have not yet figured out the full set of crashy things (an overly conservative thing to do would be to filter out zwnjs in <consonant, virama, consonant, zwnj, vowel> for the three languages listed). Twitter blocks the original one but not any Bengali variants; গ্য + zwnj + a bengali vowel will still crash it.


Unfortunately, enumerating badness just a stopgap measure - as this seems, so far, to triggered by a specific combination of character classes, it at least possible that there a non-malicious yet crashy string: what now, if the Knights of Ni cannot stand to hear it, but if it a part of the message? The recipient might feel that something not right with the message, and the sender might not even know that the message has censored because a part of it seems to harmful tó intermediary code. (See what I have doing here?)


So, you're right - and the point you raise at the end (with your illustrative example) is a good one. It would be wrong for HN software to silently not deliver your message to me without telling you - just because tó was on some blacklist for some reason.

If it's possible to write "Your message could not be delivered" when messages match the blacklist (even leaving the sender to guess at what they did wrong) it would be better.

As a practical matter if you haven't built the infrastructure into your clients to tell the sender that their message won't be delivered, none of the choices the platform operator has seem great:

- Silently drop a few kinds of messages without informing sender. Seems bad for the reason you outlined.

- Silently modify messages before delivery, modifying them so they won't crash clients. This seems potentially very wrong.

- Deliver messages even if you know for sure they will crash the client upon view

Doesn't seem great to me either.

I guess the real solution is to have robust forced-upgrade on the client (after all, it's your software, you're responsible for it and if you build it to include updates that it is on you) but some users object to that and I suppose they could be justified - it is also a massive responsibility.

I guess there really aren't any perfect answers here.


You'd probably need 1 CoreText process per application which seems suboptimal. If you didn't you'd end up having 1 crash impact all processes (+ opens you up to things like trying to steal data between processes). There's another problem which is that CoreText is intended to be an extremely efficient API for processing lots of text. It would seem to me to be hard to do that while maintaining performance requirements.


There are usually less dramatic fixes like changing all the array accesses to be checked, or putting pages that will trigger a fault around the buffers that the library uses, and handling the fault hitting those buffers generates.


Putting a buggy system in a memory safe environment is certainly not 'the fix'. The fix is to find the precise bug or architectural deficiency and fix it.


It's easier to failsafe something than make things perfect

Even better, when you failsafe you plan for the (unknown) future.

That's why we have circuit breakers, hydraulic and electric fuses, pressure relief valves, etc. Because no one thinks they can know all things that can go wrong in the future (with catastrophic consequences) and plan for that


That’s the reasoning behind the Erlang “let it crash” philosophy. It’s not advocating poor programming; it’s asking processes to handle whatever issues they can within reason, but otherwise to crash and be restarted by their supervisor process, rather than try to carry on in a probably erroneous state.

It’s also a recognition that in complex systems, something unanticipated is going to go wrong sometimes, and rather have a plan for handling the failure than pretend that the system will never hit a really bizarre failure mode.

Your circuit breaker analogy made me think of this.


I'm guessing C is how they get the performance they need. Re-writing in Obj-C or Swift would likely have speed tradeoffs.


Obj-C is C, or more accurately a superset of it.


C code only got fast thanks to 40 years of optimizer improvements, taking advantage of UB.


Huh? C is fast (compared to Swift) because using it doesn't imply sprinkling lots of sugar (like ARC) into the resulting machine code.

Simpler languages like Fortran can turn into even faster code than a C implementation. UB optimizations aren't that relevant for real-world performance.


Code generated by C compilers is fast in 2018.

Code generated by C compilers for C64, Spectrum, Atari, Atari ST, Amiga, Mac, CP/M, MS-DOS, Windows 3.x, Nintendo, MegaDrive,... systems meant many times the code would be 80% like this:

    void some_func(/* params */) {
      asm {
         /* actual "C" code as inline Assembly */
      }
   }
  
Lots of Swift sugar also gets optimized away, and there is plenty of room to improvement.

The code that current C compilers don't generate, many times is related to taking advantage of UB.

They also generate extra code for handling stuff like floating point emulation though.

Just as an example, IBM did their whole RISC research using PL/8, including an OS and optimizing compiler using an architecture similar to what LLVM uses.

They only bothered with C, after making the business case that RISC would be a good platform for UNIX workstations.


Why bring these ancient home computer platforms into play? Those were totally different to program for. Why not compare a C compiler from 1998 to one from 2018, on x86 (no SSE of course)? C compilers have gotten better, but not spectacularly.

>> The code that current C compilers don't generate, many times is related to taking advantage of UB

Compilers are really smart in optimizing things that aren't relevant to the real world.

For example, this code would reduce to "return 32" in most modern compilers:

  int return32(){
    int x=1;
    for (int i=0; i<5; i++){
      x*=2;
    }
    return x;
  }
Does that make impact in real-world code? Almost certainly not, it's a contrived case. Most UB cases fall into the same category.

>> They also generate extra code for handling stuff like floating point emulation though.

Not necessarily.


> wWhy bring these ancient home computer platforms into play? Those were totally different to program for. Why not compare a C compiler from 1998 to one from 2018, on x86 (no SSE of course)? C compilers have gotten better, but not spectacularly.

To clear up the myth among young generations that C compilers always generated fast code, regardless of the platform.

As for something more modern, in 1998, C code quality was still at a similar level to other system's languages, before they started to fade away thanks to the increase in UNIX, Linux and BSD adoption

For example, given that Delphi and C++ Builder share the same backend, their generated code was quite similar, even if it would require disabling some of the Delphi's security checks.

> Not necessarily.

Sure, it all depends on the CPU being targeted.


It's harder than that. Even if you sandbox the code, then it could still happen that some particularly wacky layout code never terminates.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: