Alex headshot

AlBlue’s Blog

Macs, Modularity and More

Swift - classes

2014, mac, swift

In this episode of the swift introduction series we’ll be looking at classes in swift.

Swift is a language that supports both functional and object oriented concepts. Classes are defined with the class keyword and the body is surrounded in braces.

Objects can have instance variables specified as either var or constants with let inside the block. As with other variables, the type can be explicitly declared or can be inferred by an initial value:

Instance variables
1
2
3
4
5
Welcome to Swift!  Type :help for assistance.
  1> class Person {
  2.   var firstName = ""
  3.   var lastName:String?
  4. }

Non-optional values need to be initialized in-line, or in an initializer.

Once the class is defined then can be created using a type constructor. Instance properties can be accessed using dot syntax:

Instantiating classes
1
2
3
4
5
6
7
8
9
10
11
  5> var alex = Person()
alex: Person = {
  firstName = ""
  lastName = nil
}
  6> alex.firstName = "Alex"
  7> alex
$R0: Person = {
  firstName = "Alex"
  lastName = nil
}

Class types use pass-by-reference semantics; two variables can point to the same instance, so if one variable is used to change the value, then it is seen through the other variable as well:

Class types are pass by reference
1
2
3
4
5
6
7
8
  8> var alblue = alex
alblue: Person = {
  firstName = "Alex"
  lastName = nil
}
  9> alblue.firstName = "AlBlue"
 10> alex.firstName
$R1: String = "AlBlue"

Instance functions can be added to a class. As a result, it’s possible to add a fullName method that returns the concatenation of the first and last names:

Instance methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 11> class Person {
 12.   var firstName = ""
 13.   var lastName:String?
 14.   func fullName() -> String {
 15.     return "\(firstName) \(lastName!)"
 16.   }
 17. }

 18> var alex = Person()
$R2: Person = {
  firstName = ""
  lastName = nil
}

 19> alex.firstName = "Alex"
 20> alex.lastName = "Blewitt"
 21> alex.fullName()
$R3: String = "Alex Blewitt"

A special kind of function can be created which is used at class creation time, called the initializer. Fields that are assigned in the initializer do not need to be initialized at the declaration. However, they do need to be declared for a type perspective:

Initializer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 22> class Person {
 23.   var firstName:String
 24.   var lastName:String
 25.   init(first:String, last:String) {
 26.     firstName = first
 27.     lastName = last
 28.   }
 29.   func fullName() -> String {
 30.     return "\(firstName) \(lastName)"
 31.   }
 32. }

 33> Person()
error: missing argument for parameter 'first' in call
Person()
      ^

 33> Person(first:"Alex", last:"Blewitt")
$R4: Person = {
  firstName = "Alex"
  lastName = "Blewitt"
}

The arguments are implicitly named; if you try and call it without passing in names (such as Person("Alex", "Blewitt")) then swift will complain that the labels are missing. This can be fixed by using an underscore _ in front of the labels of the init, which instructs swift to not consider them named:

Dealing with positional instance initializers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 34> Person("Alex", "Blewitt")
error: missing argument labels 'first:last:' in call

 34> class Person {
 35.   var firstName:String
 36.   var lastName:String
 37.   init(_ first:String, _ last:String) {
 38.     firstName = first
 39.     lastName = last
 40.   }
 41.   func fullName() -> String {
 42.     return "\(firstName) \(lastName)"
 43.   }
 44. }

 45> Person(first:"Alex", last:"Blewitt")
error: extraneous argument labels 'first:last:' in call
Person(first:"Alex", last:"Blewitt")
      ^~~~~~~        ~~~~~

 45> Person("Alex", "Blewitt")
$R5: Person = {
  firstName = "Alex"
  lastName = "Blewitt"
}

We’ll go into more detail about classes in future episodes. In next week’s episode, we’ll look at how to create structs in Swift. To subscribe to this series, add the Swift tag feed to your reader.

Swift - functions

2014, mac, swift

In this new series, we’ll be looking at Apple’s new Swift programming language, and how you can start mastering Swift. No prior experience of Apple technologies is assumed, though you will have to have a recent Mac OSX and Xcode 6.0.1 or above installed to follow through the exercises. You can also subscribe to just these posts by following the Swift tag feed.

Defining functions

Swift is an object oriented language that builds upon Objective-C and C++, but it also blends functions and functional programming techniques. It’s a little bit like Scala, except that Swift will become massively successful.

Swift functions can be created and assigned to variables, as they can in JavaScript. Unlike JavaScript, Swift uses the func keyword to introduce a function.

Defining and calling functions
1
2
3
4
5
6
7
$ swift
Welcome to Swift!  Type :help for assistance.
  1> func helloWorld() {
  2.     println("Hello World")
  3. }
  4> helloWorld()
Hello World

As with other programming languages, variables defined within the block of a function live for the lifetime of that function:

Variables live for lifetime of a function
1
2
3
4
5
6
7
8
  5> func helloWorld() {
  6.     let name = "World"
  7.     println("Hello \(name)")
  8. }
  9> helloWorld()
Hello World
 10> name
error: use of unresolved identifier 'name'

Swift has positional, named and default arguments. These are specified within the parentheses and are comma separated. Unlike variables (which use type inference to determine what the right type is) function arguments must be typed.

Positional arguments in functions
1
2
3
4
5
 11> func hello(name:String) {
 12.     println("Hello, \(name)")
 13. }
 14> hello("World")
Hello, World

Default arguments can be declared by placing a default value afterwards:

Default arguments
1
2
3
4
5
6
7
 15> func greet(name:String, greeting:String = "Hello") {
 16.     println("\(greeting), \(name)")
 17. }
 18> greet("World")
Hello, World
 19> greet("World", "Hi")
error: missing argument label 'greeting:' in call

This error occurs because default arguments are implicitly named arguments. A named argument is one that must be called with a name instead of its position. Names are specified with the same label as the function name:

Calling named arguments
1
2
 20> greet("World", greeting:"Hi")
Hi, World

Named arguments don’t have to have a default value though. They can be declared as named arguments by placing a label in front of the argument. Named arguments are said to have an ”external name”, which is the name callers must use to supply the function, and an ”internal name”, which is used in the implementation of the body itself. This allows functions to be refactored without affecting their API:

External named arguments
1
2
3
4
5
 21> func greet(name:String, greeting salutation:String = "Hi") {
 22.     println("\(salutation), \(name)")
 23. }
 24> greet("World", greeting:"Yello")
Yello, World

As a special short-cut, it is possible to declare that an argument be named with the same name as the argument name using the # symbol.

Named arguments
1
2
3
4
 25> func greet(#name:String, #greeting:String) {
 26.     println("\(greeting), \(name)")
 27. }
 28> greet(name:"World", greeting:"Hello")

Note that even though the arguments are named, they have an implied order. It is not possible to miss out the arguments or re-order them, as is possible in other languages with named arguments:

Named arguments out-of-order
1
2
3
4
 29> greet(greeting:"Hello", name:"World")
error: argument 'name' must precede argument 'greeting'
greet(greeting:"Hello", name:"World")
      ~~~~~~~~~~~~~~~~  ^    ~~~~~~~

[Side note: Error reporting in Swift is really great. The ASCII graphics at the bottom highlight the erroneous expression (the bit in the parentheses) and the caret shows where the error was found. This is especially useful in a REPL where there isn’t a defined source file or line numbers.]

Named arguments are a throwback to Objective-C, and to allow Swift to interoperate with Objective-C classes (which we’ll look at in a future post). In a few cases they can make an API clearer, but in a lot of cases they add extra noise where none is really required.

Finally, return types and values. As with other languages, the return keyword is used to return from a function and pass a value back. As with arguments, the return type of the function must be specified (it defaults to no return type if not) except that instead of using the : syntax, it invents a new -> syntax instead. This is one of the several annoyances in the language where consistency goes out of the window because not enough code has been written to find out these edge cases.

Adding two numbers
1
2
3
4
5
 29> func add(i:Int, j:Int) -> Int {
 30.     return i+j
 31. }
 32> add(1,2)
$R0: Int = 3

In next week’s episode, we’ll look at how to create classes in Swift. To subscribe to this series, add the Swift tag feed to your reader.

Bash remote vulnerability

2014, bash, mac, osx, security

As you’ve probably heard, there’s a remote vulnerability in Bash, which means that an attacker can supply a malicious code by virtue of passing in an environment variable with a specially crafted value that is then executed by Bash when a new shell starts up. This could potentially give an attacker control over an account running requests, which could include any HTTP request (variables such as REMOTE_HOST are passed through to CGI scripts by default) as well as certain environment variables in SSH (such as TERM).

I have written about this more at InfoQ, and most major operating system vendors have published updates to their versions of Bash. I have also written a piece on ShellShocked — Behind the Bug and created the popular StackExchange question and answer on ShellShock.

Apple typically take time to fix these issues, so in the meantime, if you have an OSX server estate you are advised to upgrade to a new version of bash immediately.

If you have the Xcode developer tools available, you can compile it yourself as follows:

Fixing bash on OSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ # If you want to disable auto-imported functions, uncomment the following
$ # export ADD_IMPORT_FUNCTIONS_PATCH=YES
$ mkdir bash-fix
$ cd bash-fix
$ curl https://opensource.apple.com/tarballs/bash/bash-92.tar.gz | tar zxf -
$ cd bash-92/bash-3.2
$ curl https://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-052 | patch -p0
$ curl https://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-053 | patch -p0
$ [ "$ADD_IMPORT_FUNCTIONS_PATCH" == "YES" ] && curl http://alblue.bandlem.com/import_functions.patch | patch -p0
$ [ "$ADD_IMPORT_FUNCTIONS_PATCH" == "YES" ] || curl https://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-054 | patch -p0
$ [ "$ADD_IMPORT_FUNCTIONS_PATCH" == "YES" ] || curl https://ftp.gnu.org/pub/gnu/bash/bash-3.2-patches/bash32-055 | patch -p0
$ cd ..
$ xcodebuild
$ build/Release/bash --version # GNU bash, version 3.2.55(1)-release
$ build/Release/sh --version   # GNU bash, version 3.2.55(1)-release
$ sudo cp /bin/bash /bin/bash.old
$ sudo cp /bin/sh /bin/sh.old
$ sudo cp build/Release/bash /bin
$ sudo cp build/Release/sh /bin

You are advised to take backups before you make these changes and test that you can log in before you quit the shell you have started (because if there are problems you may not be able to fix this afterwards). When Apple release a fix you are recommended to apply that and verify that the new Apple version is used instead.

To test the fix is effective, run:

Original Vulnerability test
1
2
3
4
$ env x='() { :;}; echo vulnerable' bash -c 'echo hello'
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
hello
New vulnerability test
1
2
3
4
5
$ env X='() { (a)=>\' sh -c "echo date"; cat echo
sh: X: line 1: syntax error near unexpected token `='
sh: X: line 1: `'
sh: error importing function definition for `X'
Thu 25 Sep 2014 08:54:13 BST

To permanently disable this class of bug by disabling all auto-imported functions, this patch can be applied (to 3.2p53). Either this or bash 3.2p54 will protect against the following vulnerability:

Disabling auto-imported functions
1
2
$ env ls="() { echo 'Game over'; }" bash -c ls
Game over

Once you’re happy, make sure the old versions are no longer executable (or better still, remove them) and then reboot your system. Otherwise any running processes might be tricked for log processing or other routine tasks.

Removing executable bit
1
2
3
$ sudo chmod a-x /bin/bash.old /bin/sh.old
$ # or just sudo rm /bin/bash.old /bin/sh.old
$ # sudo reboot as well

You should also check for bash or sh installs in other locations, such as /sbin, /usr/sbin or /usr/local/bin. If you use a ports manager such as Homebrew or Macports then you should follow the upgrade instructions given by those package managers.

This post was also made to apple.stackexchange.com where it has received several hundred votes - so my thanks are due to everyone there, and of course, Chet for fixing the issues as quickly as they were.

Update

Apple have released their fix for this issue, providing bash-3.2p53. They appear to have fixed the above bugs including the Game over bug, though without using the upstream bash-3.2p54 patch listed here.

Separate patches are available for different versions of OSX:

  • https://support.apple.com/kb/DL1769 - Mavericks (10.9.5 and above)
  • https://support.apple.com/kb/DL1768 - Mountain Lion (10.8.5)
  • https://support.apple.com/kb/DL1767 - Lion (10.7.5)

Note that the official Apple patch is still vulnerable to a variant of the Game Over bug described above, as noted by @ake_____ on twitter.

Game Over in Apple Bash fix 1.0
1
2
$ env '__BASH_FUNC<ls>()'="() { echo Game Over; }" ./bash -c ls
Game Over

Cautions administrators are invited to re-consider applying the ‘disable auto-import functions patch’ listed above, or rebuild to bash 3.2.55.