Skip to content

[SR-7526] Foundation.NSPathUtilities.resolvingSymlinksInPath crashes for "/" #3715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
swift-ci opened this issue Apr 25, 2018 · 2 comments
Closed

Comments

@swift-ci
Copy link
Contributor

Previous ID SR-7526
Radar None
Original Reporter felix91gr (JIRA User)
Type Bug
Status Resolved
Resolution Done
Environment

Linux, Elementary OS 0.4 Loki

Ubuntu Upstream: 16.04 LTS

Additional Detail from JIRA
Votes 0
Component/s Foundation
Labels Bug, Linux, RunTimeCrash
Assignee felix91gr (JIRA)
Priority Medium

md5: aaf24b7256119e57a9c2578dec9efffb

Issue Description:

Hi. Open the `REPL` on linux and try this:

  1> import Foundation

  2> NSString(string: "/").resolvingSymlinksInPath

  3> NSString(string: "/lib/").resolvingSymlinksInPath

It crashes on the first attempt but not the second one. Prying open the implementation on the `corelibs` repo:

public var resolvingSymlinksInPath: String {
    var components = pathComponents
    guard !components.isEmpty else {
        return _swiftObject
    }
    
    // TODO: pathComponents keeps final path separator if any. Check that logic.
    if components.last == "/" {
        components.removeLast()
    }
    
    let isAbsolutePath = components.first == "/"
    
    var resolvedPath = components.removeFirst()
    for component in components {
        switch component {
            
        case "", ".":
            break
            
        case ".." where isAbsolutePath:
            resolvedPath = resolvedPath._bridgeToObjectiveC().deletingLastPathComponent
            
        default:
            resolvedPath = resolvedPath._bridgeToObjectiveC().appendingPathComponent(component)
            if let destination = FileManager.default._tryToResolveTrailingSymlinkInPath(resolvedPath) {
                resolvedPath = destination
            }
        }
    }
    
    let privatePrefix = "/private"
    resolvedPath = resolvedPath._tryToRemovePathPrefix(privatePrefix) ?? resolvedPath
    
    return resolvedPath
}

And then running it on the `REPL`:

Welcome to Swift version 4.1 (swift-4.1-RELEASE). Type :help for assistance.
  1> import Foundation
  2> let source = NSString(string: "/")
source: Foundation.NSString = {
  Foundation.NSObject = {}
  _cfinfo = {
    info = 1920
    pad = 0
  }
  _storage = "/"
}
  3> var components = source.pathComponents
components: [String] = 1 value {
  [0] = "/"
}
  4> components.last
$R0: String? = "/"
  5> components.removeLast()
$R1: String = "/"
  6> components.first == "/"
$R2: Bool = false
  7> components.removeFirst()
Fatal error: Can't remove first element from an empty collection
Current stack trace:
0    libswiftCore.so                    0x00007ffff7ce2750 _swift_stdlib_reportFatalError + 171
1    libswiftCore.so                    0x00007ffff7a50ad6 <unavailable> + 1366742
2    libswiftCore.so                    0x00007ffff7c8b383 <unavailable> + 3703683
3    libswiftCore.so                    0x00007ffff7a50ad6 <unavailable> + 1366742
4    libswiftCore.so                    0x00007ffff7bbcd80 <unavailable> + 2858368
5    libswiftCore.so                    0x00007ffff7a3e000 RangeReplaceableCollection.removeFirst() + 398
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
Process 15909 stopped
* thread #&#8203;1, name = 'repl_swift', stop reason = signal SIGILL: illegal instruction operand
    frame #&#8203;0: 0x00007ffff7bbcd80 libswiftCore.so`function signature specialization <Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage(Swift.StaticString, Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 96
libswiftCore.so`function signature specialization <Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage(Swift.StaticString, Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never:
->  0x7ffff7bbcd80 <+96>: ud2    
    0x7ffff7bbcd82:       nopw   %cs:(%rax,%rax)
libswiftCore.so`function signature specialization <Arg[0] = Owned To Guaranteed and Exploded, Arg[1] = Exploded> of Swift.String._compareDeterministicUnicodeCollation(Swift.String) -> Swift.Int:
    0x7ffff7bbcd90 <+0>:  pushq  %rbp
    0x7ffff7bbcd91 <+1>:  movq   %rsp, %rbp
Target 0: (repl_swift) stopped.
  8>  

Clearly this part is the problem:

if components.last == "/" {
    components.removeLast()
}

For a path like "/" which has only one character, "resolvingSymlinksInPath" should return "/" or whatever the symlink for it might be (can "/" have symlinks? Anyway...). Therefore the code should look something like this:

if components.last == "/" && components.count > 1 {
    components.removeLast()
}
@swift-ci
Copy link
Contributor Author

Comment by Félix Fischer (JIRA)

PR: #1536

@swift-ci
Copy link
Contributor Author

Comment by Félix Fischer (JIRA)

Fixed at #1536

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from swiftlang/swift May 5, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant