[BUG] Crash in moveExtractionContents: — URLByAppendingPathComponent: with nil lastPathComponent (v1.6.7, macOS 27)
- 主要语言
- PHP
- 星标
- 7.3k
- 派生
- 294
- 平均合并
- 14 天 16 小时
- 30 天内合并 PR
- 1
描述
### Brief description
Keka **crashes (SIGABRT)** when finishing an extraction. The uncaught exception is raised inside `-[NSURL URLByAppendingPathComponent:]` because the path component argument is **nil**.
This happens after extraction, while moving/finalizing extraction contents — not during the archive tool process itself.
### Keka version
- **Keka 1.6.7 (5729)** — latest GitHub release as of 2026-06-30
- Distribution: notarized Developer ID build from keka.io (`TeamIdentifier=4FG648TM2A`)
- Universal binary, crash on **arm64** slice
### macOS version
- **macOS 27.0 (26A5406e)** (Apple Silicon, Mac17,8)
- Hardened Runtime enabled
### Steps to reproduce
1. Install Keka 1.6.7 (fresh / sandboxed install with little or no File Access bookmarks configured).
2. Double-click an archive (or open with Keka) so extraction starts immediately.
3. Keka aborts shortly after launch / during finalize (seen within ~9s of process start).
Observed 4 identical crashes in a few minutes (`Keka-2026-08-12-182916` … `183033.ips`).
### Expected behaviour
Extraction completes (or fails with a recoverable error / access prompt). Keka should not abort.
### Actual behaviour
Process terminates:
- `EXC_CRASH` / `SIGABRT`
- `asi: abort() called` (uncaught Objective-C exception)
- Exception originates from:
`-[NSURL(NSURLPathUtilities) URLByAppendingPathComponent:]`
Apple raises when the **component argument is nil**:
`*** -[NSURL URLByAppendingPathComponent:]: component, components, or pathExtension cannot be nil.`
### Evidence (crash stack)
`lastExceptionBacktrace` / faulting main thread (summarized):
```
objc_exception_throw
-[NSURL URLByAppendingPathComponent:]
Keka moveExtractionContents: (+0xc8 / imageOffset 123752)
Keka finalizeTask (+0x1b0 / imageOffset 259608)
Keka (caller around applyQuarantine path / finalize, imageOffset 245828)
_dispatch_client_callout / main queue
```
Four consecutive reports share the **same** exception site and offsets.
### Binary analysis (v1.6.7 arm64)
ObjC method mapping from the binary:
| Image offset | Method |
|---|---|
| `0x10001e2a0` | `-moveExtractionContents:` |
| `0x10003f468` | `-finalizeTask` |
| Crash return addr `0x10001e368` | inside `moveExtractionContents:` right after the throwing call |
Reconstructed call sequence at the throw site in `-moveExtractionContents:` (selectors resolved from `__objc_stubs` / selrefs):
```objc
// package = [task package] (or equivalent receiver chain)
NSURL *destinationPath = package.destinationPath; // base (non-nil in crash path)
// ...
NSString *component =
package.sourcePath
.URLByDeletingPathExtension
.lastPathComponent; // can be nil
NSURL *url = [destinationPath URLByAppendingPathComponent:component]; // THROWS if component == nil
```
Relevant selectors confirmed on the path:
- `package`
- `packageTemporaryPath`
- `destinationPath`
- `sourcePath`
- `URLByDeletingPathExtension`
- `lastPathComponent`
- `URLByAppendingPathComponent:` ← throw
- also nearby: `useInputNameAsIntermediateFolder`, `tarballWithTwoExtensions`
So the crash is **not** a random memory corruption; it is a missing nil-check on the intermediate folder / output name derived from `sourcePath`.
### Likely conditions for `component == nil`
From Foundation behavior / sandbox setup on this machine:
1. **`sourcePath` is nil** (bookmark / security-scoped access failed; then the whole chain returns nil, and `URLByAppendingPathComponent:` throws).
2. Or `sourcePath` is an `NSURL` with **no path**, so `lastPathComponent` is nil after `URLByDeletingPathExtension`.
On the crashing system:
- Keka is sandboxed (`com.apple.security.app-sandbox`).
- App Group container `4FG648TM2A.group.com.aone.keka` was effectively **empty** (no persisted File Access bookmarks).
- Preferences only had Sparkle keys — no saved extract destination.
That matches “open archive with little file access → finalize still calls `moveExtractionContents:` with a bad/missing `sourcePath`”.
### Suggested fix (for maintainers)
In `-moveExtractionContents:` (and any similar builders):
```objc
NSURL *source = package.sourcePath;
NSString *component = source.URLByDeletingPathExtension.lastPathComponent;
if (component.length == 0) {
// fallback: temporary package name, UUID, or skip rename and surface a file-access error
return; // or handle error without throwing
}
NSURL *url = [destinationPath URLByAppendingPathComponent:component];
```
Also guard:
- `destinationPath == nil`
- failed security-scoped bookmark resolve
Optional: prefer `URLByAppendingPathComponent:isDirectory:` and avoid building paths when File Access / source URL is unavailable — ask for access instead of finalizing.
### Workaround for users
1. Open Keka first → **Preferences → File Access** → grant Home / Full Disk access.
2. Prefer drag-and-drop into the Keka window over double-click until fixed.
3. Or extract with Archive Utility / `ditto` as a temporary bypass.
### Notes
- Latest release notes for 1.6.x fix other macOS 26 issues (default uncompressor, BSDTAR, ISO, etc.) but **do not** mention this finalize / `moveExtractionContents:` crash.
- This repository does not contain the app sources, so a code PR is not possible from the community side; filing here for maintainers.
### Attachments
Happy to attach full `.ips` reports (`Keka-2026-08-12-*.ips`) if useful. Key fields:
- `app_version`: 1.6.7 / `build_version`: 5729
- `exception`: `EXC_CRASH (SIGABRT)`
- `termination`: Abort trap: 6 by Keka
- `cpuType`: ARM-64
Thanks!
贡献指南
这个仓库没有索引到贡献指南
调研方向
The repository does not contain app sources, so start with the supplied crash reports and the binary entry point moveExtractionContents:, then trace finalizeTask and the sourcePath/destinationPath inputs. Done means extraction no longer aborts when access or source-path data is missing and instead completes or surfaces a recoverable error.
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- macos, objective-c
- 领域
- desktop
- Issue 类型
- 缺陷
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100