[macOS] Crash with Signal 6 on double-clicking layout files with non-ASCII paths (missing exception handling in GuiApplication::event)

Open
#2,442 5 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
cpp, macos
Domain
desktop

Research direction

Start with src/lay/lay/layApplication.cc and compare the macOS QEvent::FileOpen handler with the protected calls in MainWindow::dropEvent and MainWindow::cm_open. Then inspect src/tl/tl/tlInternational.cc, including the Q_WS_MAC conditional. Done means Finder-launched files with non-ASCII paths no longer abort KLayout and failures show the existing error dialog; verify on macOS with Qt5/Qt6.

Written by the indexing model from the issue text.

Description

Summary

Double-clicking a layout file (.gds, .oas, etc.) in macOS Finder crashes KLayout with Signal number: 6 (SIGABRT) if the file path contains non-ASCII characters (such as CJK characters, e.g. the default Microsoft OneDrive directory OneDrive-个人).

When the same file is dragged into an already running KLayout instance, KLayout does not crash; instead, it safely shows an error dialog:
Unable to open file: ... (errno=2)

Environment
  • KLayout Version: 0.30.12 (and earlier versions on Qt5/Qt6)
  • OS: macOS (Apple Silicon / arm64)
Crash Backtrace
Signal number: 6
Address: 0x18af2c5e8
Program Version: KLayout 0.30.12

Backtrace:
libklayout_lay.0.dylib _ZN3lay25enable_signal_handler_guiEb
libsystem_platform.dylib _sigtramp
libsystem_pthread.dylib pthread_kill
libsystem_c.dylib abort
libc++abi.dylib _ZN10__cxxabiv116__shim_type_infoD1Ev
libc++abi.dylib _ZSt14set_unexpectedPFvvE
libobjc.A.dylib _ZL15_objc_terminatev
libc++abi.dylib _ZSt11__terminatePFvvE
libc++abi.dylib _ZN10__cxxabiv1L13is_class_typeEPKNS_16__shim_type_infoE
libklayout_lay.0.dylib _ZN3lay10MainWindow21create_or_load_layoutEPKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEPKN2db17LoadLayoutOptionsERS8_i
libklayout_lay.0.dylib _ZN3lay14GuiApplication5eventEP6QEvent
QtWidgets _ZN19QApplicationPrivate13notify_helperEP7QObjectP6QEvent
QtWidgets _ZN12QApplication6notifyEP7QObjectP6QEvent
libklayout_lay.0.dylib _ZN3lay14GuiApplication9do_notifyEP7QObjectP6QEvent
QtCore _ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent
QtGui _ZN22QGuiApplicationPrivate20processFileOpenEventEPN29QWindowSystemInterfacePrivate13FileOpenEventE
QtGui _ZN22QWindowSystemInterface19handleFileOpenEventERK7QString
libqcocoa.dylib qt_plugin_instance
AppKit -[NSApplication(NSAppleEventHandling) _openURLs:requestedBySourceApp:completionHandler:]
AppKit __69-[NSApplication(NSAppleEventHandling) _handleAEOpenDocumentsForURLs:]_block_invoke
AppKit __102-[NSApplication _reopenWindowsAsNecessaryIncludingRestorableState:withFullFidelity:completionHandler:]_block_invoke
AppKit -[NSApplication run]
...
Root Cause Analysis

There are two interrelated issues causing this bug:

1. Missing Exception Handling in GuiApplication::event for QEvent::FileOpen

In src/lay/lay/layApplication.cc:

bool
GuiApplication::event (QEvent *event)
{
#if defined(__APPLE__)
  if (event->type() == QEvent::FileOpen) {
      QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
      if (mp_mw)
      {
        const std::string tech = mp_mw->initial_technology();
        const std::string file = tl::to_string (openEvent->file());
        const int mode = 1; // open in new window
        mp_mw->load_layout (file, tech, mode);
        mp_mw->add_mru (file, tech);
      }
  }
#endif
  return QApplication::event(event);
}

In MainWindow::dropEvent and MainWindow::cm_open, calls to load_layout are protected with BEGIN_PROTECTED ... END_PROTECTED.
In GuiApplication::event, however, mp_mw->load_layout(...) is called without BEGIN_PROTECTED or try...catch.
When load_layout fails for any reason (e.g. non-existent path, permission error, corrupted file), create_or_load_layout re-throws tl::Exception. The uncaught C++ exception escapes the Qt Cocoa plugin boundary into AppKit's Objective-C runloop (_openURLs:requestedBySourceApp:completionHandler:), triggering _objc_terminatev -> std::terminate() -> abort() -> SIGABRT (Signal 6).

2. Locale and Encoding Fallback on macOS GUI Launch

Why does load_layout fail with errno=2 (ENOENT) in the first place when files reside in paths with non-ASCII characters?
In src/tl/tl/tlInternational.cc:

void initialize_codecs ()
{
  // determine encoder for system strings
#ifdef _WIN32
  ms_system_codec = 0;
#elif defined(Q_WS_MAC)
  ms_system_codec = QTextCodec::codecForName ("UTF-8");
#else
  setlocale (LC_ALL, "");
  ms_system_codec = QTextCodec::codecForName (nl_langinfo (CODESET));
  if (! ms_system_codec) {
    ms_system_codec = QTextCodec::codecForName ("Latin-1");
  }
#endif
  • Q_WS_MAC is obsolete and was removed in Qt 5 (Qt 5+ uses Q_OS_MAC or Q_OS_MACOS). Therefore, the #elif defined(Q_WS_MAC) branch is never compiled under Qt 5 / Qt 6.
  • On macOS, applications launched from Finder (via LaunchServices) do not inherit LANG or LC_* environment variables from the user shell.
  • When setlocale(LC_ALL, "") runs without LANG/LC_ALL set, nl_langinfo(CODESET) returns US-ASCII.
  • As a result, ms_system_codec is set to US-ASCII. When converting the path with string_to_system(source) (or tl::to_local), any non-ASCII character (such as 个人 in ~/Library/CloudStorage/OneDrive-个人/...) is replaced with ? or stripped, causing POSIX open() / gzopen() to fail with errno=2 (ENOENT).
  • (When run directly from Terminal where LANG=...UTF-8 is present, nl_langinfo(CODESET) is UTF-8, which is why the command-line launch does not trigger this error).
Suggested Fix
  1. In src/lay/lay/layApplication.cc, wrap the FileOpen handler in BEGIN_PROTECTED ... END_PROTECTED:
  if (event->type() == QEvent::FileOpen) {
      QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
      if (mp_mw)
      {
        BEGIN_PROTECTED
          const std::string tech = mp_mw->initial_technology();
          const std::string file = tl::to_string (openEvent->file());
          const int mode = 1; // open in new window
          mp_mw->load_layout (file, tech, mode);
          mp_mw->add_mru (file, tech);
        END_PROTECTED
      }
  }
  1. In src/tl/tl/tlInternational.cc, update Q_WS_MAC to Q_OS_MAC:
#ifdef _WIN32
  ms_system_codec = 0;
#elif defined(Q_OS_MAC)
  ms_system_codec = QTextCodec::codecForName ("UTF-8");
#else
  setlocale (LC_ALL, "");
  ms_system_codec = QTextCodec::codecForName (nl_langinfo (CODESET));
  if (! ms_system_codec) {
    ms_system_codec = QTextCodec::codecForName ("Latin-1");
  }
#endif
Dominant language
C++
Stars
1.2k
Forks
305
Avg merge
3d 2h
Merged PRs (30d)
11

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from KLayout/klayout

All issues in KLayout/klayout

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.