Archive for the ‘Apple’ Category

Safari extension: autocomplete

Thursday, June 10th, 2010

Safari has a feature called form autocompletion, or AutoFill, that reads the username and password you type into a web form, saves them to your keychain, and automatically fills them in from the keychain the next time you visit the web form. This feature is completely opt-in: you can enable and disable it in Safari’s preferences, and even when it’s enabled, Safari will ask you before saving the username and password from each web form. Many other web browsers have a similar feature; it appears to have been introduced by Internet Explorer.

Unfortunately, a number of web sites (including my bank, for example) choose to disable autocompletion in a misguided attempt at security. Autocompletion can be disabled by using the attribute autocomplete=off in a web form. The idea behind disabling autocompletion seems to be that it leaves the account holder vulnerable to someone else accessing the computer and logging into the account. I believe that this is misguided for at least two reasons. First, autocompletion is opt-in, so the user can decide whether to save passwords on a particular machine. Anyone who chooses to save their passwords on a public terminal is an idiot. Indeed, anyone who logs in to their bank account on a public terminal deserves to be hacked and lose all their money, because who knows what manner of keyloggers or other malware could be running on the machine? I feel safe turning on AutoFill on my computer because I’m the only person who ever has access to it.

Another reason that disabling autocompletion is misguided is that it encourages the use of weak passwords. For security, I generate very long, random passwords for web sites and save them to my keychain. There’s no way I could memorize even one of my web site passwords, much less all of them. It’s difficult for almost anyone to memorize a bunch of web site passwords. Disabling autocompletion forces the user to type them in manually every time, and this encourages the use of short, easy to remember passwords. Worse, it encourages password sharing among different web sites. Thus, if an attacker can guess or brute-force one password, the attacker suddenly has access of all of a persons’s web site accounts. That’s terrible security.

WebKit, the web engine underlying Safari, respects the autocomplete attribute, and there’s no preference or API to make WebKit ignore the attribute. However, I discovered an excellent script written by Michael Kisor called Autocomplete Always On! that actually patches the WebKit framework itself on your system so that it ignores autocomplete. It works by changing one byte in the file


transforming the string autocomplete into xutocomplete. After that change, WebKit looks for xutocomplete=off but never finds it in the web form, which means autocomplete never gets disabled. WebKit is open source, so we can verify the consequences of the patch.

The only downside of the Autocomplete Always On! script is that it needs to be re-run after any software update to the WebKit framework on your system. I’ve been using the script for years with no trouble … until Safari 5. After installing Safari 5, I discovered that the script was no longer effective in re-enabling autocompletion. This was no flaw in the script, however. Autocomplete Always On! still works as designed on the version of WebKit shipped with Safari 5. The problem is that the Safari 5 binary itself seems to include a new check for the autocomplete attribute in web forms. I’ve verified this behavior in the debugger. If the form contains autocomplete=off, then Safari 5 never calls the (private) WebKit method -[WebHTMLRepresentation elementDoesAutoComplete:], so Safari doesn’t even ask WebKit whether autocomplete is enabled for the web form element. It is possible to patch the Safari binary just like the WebCore binary, after which Safari 5 will call -[WebHTMLRepresentation elementDoesAutoComplete:], making the WebCore patch effective again. Unfortunately, patching the Safari binary breaks codesigning for the application, and the keychain uses codesigning to determine whether an application can access saved passwords.

That’s the bad news. The good news is that Safari 5 also introduced extensions. A Safari extension is like a plug-in, because it can run code inside the browser, but unlike a plug-in, an extension doesn’t run native C or Objective-C code but rather HTML or Javascript code. For example, an extension can specify some Javascript to run on page load. I found a script written by Andreas Huber that removes any autocomplete attributes from a web form, and I cleaned it up a bit for inclusion in a Safari extension. With my autocomplete extension installed, you don’t have to patch WebKit or Safari, because the autocomplete attributes are simply removed from the web page before the browser checks for their existence.

I’m making my autocomplete Safari extension available — in September. No, today! You can download the extension here. To install the extension on your computer, you first need to check “Show Develop menu in menu bar” at the bottom of Safari’s Advanced preferences. Then in the Develop menu, you need to check “Enable Extensions”. After extensions are enabled, you can simply double-click the Safari extension in Finder to install.

Enjoy! In lieu of donations, I am accepting sexual favors. Or an icon.

Beware DIY component replacement

Wednesday, February 17th, 2010

I own a 17-inch 2.33GHz Intel Core 2 Duo MacBook Pro. When I purchased it, I chose the largest hard drive available, the 200GB 4200rpm option. Over the years, that hard drive had become quite full, and it’s also very slow compared to newer hard drive models. Thus, I decided recently to replace it with a 500GB 7200rpm Seagate Momentus hard drive.

My original plan was to pay a local computer repair shop to swap the drives. I’m a software guy, not a hardware guy, and my time and sanity are (somewhat) valuable. However, some people who shall remain nameless shamed me into doing it myself, arguing that a proper computer geek should be able to replace components easily. (Sure, right, just like the midplane was a ‘user-serviceable’ part of the iMac G5.)

I found several online videos with instructions for replacing my MacBook Pro’s hard drive. For example, Other World Computing has a video here, which specifically mentions my model: MacBookPro2,1. After studying the instructions carefully, I set about to do it myself

The first problem was that the OWC video mentions the requirement of a #00 Phillips screwdriver, which I did not have but which I acquired for the purpose of this hard drive replacement. Nonetheless, it turns out that the #00 was not actually the right size for the screws. Fortunately, I did already happen to have a screwdriver that fit the screws in my MacBook Pro. Otherwise, I would have been forced to scrub the replacement at the start.

This screwdriver mixup was but a minor blip compared to the next and worst problem. The videos completely failed to show or mention that there was a very short and easily snapped wire on the far left side of the machine, running from the bottom board to the top case. I did not discover that this wire existed until I opened the top case, and after a few seconds of attempting to lift the top case, the wire indeed snapped.

Here’s a photo of the broken black wire:
Photo of broken black wire

And here’s a photo of where it was attached on the top case:
Photo of top case

At the time, I had no idea what the wire was for. I had to take the machine in to the genius bar at my local Apple Store. The genius examined it and informed me, to my great relief, that the wire was for the built-in microphone. My microphone is now broken and inoperative, but it could have been much worse.

Another thing the videos failed to mention is that the metal tabs with screw holes on the top case are extremely fragile. When I put the top case back on, one of the tabs broke off. It’s not a big deal, there are enough other screws around the case to keep it securely in place, but it’s annoying, and there is a little area of the bottom case on the right side that is sticking out slightly and bent.

In the end, I’m happy with my new hard drive. My advice, though, is to pay a professional to perform the replacement, don’t try to do it yourself. Ignore the DIY demons whispering in your ear. I don’t think so, Tim.

Local variables are free

Saturday, December 19th, 2009

This is part II of my irregularly scheduled series on compiler optimization. In part I, I explained how the compiler can optimize away return statements, resulting in missed breakpoints. My given workaround to that problem, though effective, was very ugly and architecture-dependent, much like Cowboys Stadium.

(gdb) break *0x00001fc5 if $eax != 0

Although there’s not much we can do to prevent the compiler optimization, we can greatly simplify our conditional breakpoint. I had suggested rewriting the source code, which was awe-inspiringly prescient, because that’s what I’m going to do now. Here’s the original code:

8	if (ShouldReturn())
9		return;

And here’s the revised code:

8	int localVar = ShouldReturn();
9	if (localVar)
10		return;

The return at line 10 will still be optimized away. However, the revised code allows us to set a simple breakpoint at line 9 that will stop when we want:

(gdb) break 9 if localVar != 0

No knowledge of the architecture, machine registers, or assembly language is required.

From the beginning of time (January 1970, of course), programmers have struggled over coding style. Objective-C programmers, for example, expend undue effort arranging their brackets. (I have [NSMutableArray array] going to the Final Four.) For some, bracket-making becomes a kind of game or contest.

[[[[[[[[[[[[[See how] many] method] calls] we] can] fit] on] one] line] of] source] code];

I’ve changed my coding style over the years, but I’ve settled on one fundamental principle: write your code so that it’s easy to debug. All your fancy margin-aligning isn’t going to help when you need to figure out why your app keeps exploding. If you have nested method calls on one line of code, you can’t easily set a breakpoint in the middle. That’s why I prefer as much as possible to have only one method call per line of code, and create a local variable to store the return value.

There is a misconception that local variables are expensive, in terms of either computation or memory. The truth is that local variables are very cheap, the value meals of the computing world. (Would you like trans fat with your saturated fat?) It only takes one machine instruction to store a pointer address to a local variable. One machine instruction is really quite fast, about as fast as you can get — at least with restrictor plates. With regard to memory, local variables only take up stack space. To create a local variable, you simply move the stack a little. When the method or function returns, the stack is moved back, and thereby the space reserved for local variables is automatically recovered. Of course, you don’t want to create large C arrays on the stack, but a pointer to an Objective-C object only takes 4 bytes on the stack for 32-bit, 8 bytes for 64-bit. The default 32-bit stack size is 8MB, so you’re not going to run out of space unless you have deeply recursive calls.

Even these small costs are only relevant in the context of your app’s unoptimized, debug configuration. For your customers, on the other hand, local variables are free. As in Mumia, or Bird. When you compile your app using the release configuration, the local variables disappear, the compiler optimizes them away. (By the way, this is one of the reasons that debugging the release build of your app can be a frustrating and/or wacky experience.) To see the optimization in action, let’s consider some sample code:

1  #import <Foundation/Foundation.h>
3  @interface MyObject : NSObject {}
4  @end
6  @implementation MyObject
8  -(NSString *)myDirectProcessName {
9  	return [[[NSProcessInfo processInfo] processName] lowercaseString];
10 }
12 -(NSString *)myRoundaboutProcessName {
13 	NSString *myRoundaboutProcessName = nil;
14 	NSProcessInfo *processInfo = [NSProcessInfo processInfo];
15 	NSString *processName = [processInfo processName];
16 	NSString *lowercaseString = [processName lowercaseString];
17 	myRoundaboutProcessName = lowercaseString;
18 	return myRoundaboutProcessName;
19 }
21 @end
23 int main(int argc, const char *argv[]) {
24 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
25 	MyObject *myObject = [[[MyObject alloc] init] autorelease];
26 	NSLog(@"My direct process name: %@", [myObject myDirectProcessName]);
27 	NSLog(@"My roundabout process name: %@", [myObject myRoundaboutProcessName]);
28 	[pool release];
29 	return 0;
30 }

The above code is obviously contrived and useless. It only has value for explanatory purposes, and perhaps in the app store for $0.99. The methods -myRoundaboutProcessName and -myDirectProcessName do the same thing, the former with and the latter without local variables. Here’s the i386 disassembly for the methods when compiled using the debug configuration:

-[MyObject myDirectProcessName]:
00001d2a	nop
00001d2b	nop
00001d2c	nop
00001d2d	nop
00001d2e	nop
00001d2f	nop
00001d30	pushl	%ebp
00001d31	movl	%esp,%ebp
00001d33	pushl	%ebx
00001d34	subl	$0x14,%esp
00001d37	calll	0x00001d3c
00001d3c	popl	%ebx
00001d3d	leal	0x000012e8(%ebx),%eax
00001d43	movl	(%eax),%eax
00001d45	movl	%eax,%edx
00001d47	leal	0x000012e4(%ebx),%eax
00001d4d	movl	(%eax),%eax
00001d4f	movl	%eax,0x04(%esp)
00001d53	movl	%edx,(%esp)
00001d56	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d5b	movl	%eax,%edx
00001d5d	leal	0x000012e0(%ebx),%eax
00001d63	movl	(%eax),%eax
00001d65	movl	%eax,0x04(%esp)
00001d69	movl	%edx,(%esp)
00001d6c	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d71	movl	%eax,%edx
00001d73	leal	0x000012dc(%ebx),%eax
00001d79	movl	(%eax),%eax
00001d7b	movl	%eax,0x04(%esp)
00001d7f	movl	%edx,(%esp)
00001d82	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001d87	addl	$0x14,%esp
00001d8a	popl	%ebx
00001d8b	leave
00001d8c	ret
-[MyObject myRoundaboutProcessName]:
00001d8d	nop
00001d8e	nop
00001d8f	nop
00001d90	nop
00001d91	nop
00001d92	nop
00001d93	pushl	%ebp
00001d94	movl	%esp,%ebp
00001d96	pushl	%ebx
00001d97	subl	$0x24,%esp
00001d9a	calll	0x00001d9f
00001d9f	popl	%ebx
00001da0	movl	$0x00000000,0xe8(%ebp)
00001da7	leal	0x00001285(%ebx),%eax
00001dad	movl	(%eax),%eax
00001daf	movl	%eax,%edx
00001db1	leal	0x00001281(%ebx),%eax
00001db7	movl	(%eax),%eax
00001db9	movl	%eax,0x04(%esp)
00001dbd	movl	%edx,(%esp)
00001dc0	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dc5	movl	%eax,0xec(%ebp)
00001dc8	movl	0xec(%ebp),%edx
00001dcb	leal	0x0000127d(%ebx),%eax
00001dd1	movl	(%eax),%eax
00001dd3	movl	%eax,0x04(%esp)
00001dd7	movl	%edx,(%esp)
00001dda	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001ddf	movl	%eax,0xf0(%ebp)
00001de2	movl	0xf0(%ebp),%edx
00001de5	leal	0x00001279(%ebx),%eax
00001deb	movl	(%eax),%eax
00001ded	movl	%eax,0x04(%esp)
00001df1	movl	%edx,(%esp)
00001df4	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001df9	movl	%eax,0xf4(%ebp)
00001dfc	movl	0xf4(%ebp),%eax
00001dff	movl	%eax,0xe8(%ebp)
00001e02	movl	0xe8(%ebp),%eax
00001e05	addl	$0x24,%esp
00001e08	popl	%ebx
00001e09	leave
00001e0a	ret

As expected, -myRoundaboutProcessName makes more room on the stack than -myDirectProcessName:

00001d34	subl	$0x14,%esp
00001d97	subl	$0x24,%esp

At 00001da0, -myRoundaboutProcessName sets the value of the local variable to nil, as in line 13 of the source code. The interesting differences, though, are immediately after the calls to objc_msgSend(). By the standard ABI, the register eax contains the return value of objc_msgSend(). In -myDirectProcessName, the value in eax is simply moved to the register edx:

00001d5b	movl	%eax,%edx

In contrast, -myRoundaboutProcessName first stores the value on the stack before moving it to edx. The address on the stack is the space reserved for the local variable:

00001dc5	movl	%eax,0xec(%ebp)
00001dc8	movl	0xec(%ebp),%edx

After the final objc_msgSend() call, -myDirectProcessName doesn’t bother to do much, because the return value in eax will become the return value of the whole method. In -myRoundaboutProcessName, it needs to store values in local variables as in lines 16 and 17 of the source code:

00001df9	movl	%eax,0xf4(%ebp)
00001dfc	movl	0xf4(%ebp),%eax
00001dff	movl	%eax,0xe8(%ebp)
00001e02	movl	0xe8(%ebp),%eax

So that’s how the methods differ in the unoptimized build. Now let’s see what happens when we use the release configuration. Here’s the optimized disassembly for -myDirectProcessName:

-[MyObject myDirectProcessName]:
00001dce	pushl	%ebp
00001dcf	movl	%esp,%ebp
00001dd1	subl	$0x18,%esp
00001dd4	movl	0x00003000,%eax
00001dd9	movl	%eax,0x04(%esp)
00001ddd	movl	0x0000302c,%eax
00001de2	movl	%eax,(%esp)
00001de5	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dea	movl	0x00003004,%edx
00001df0	movl	%edx,0x04(%esp)
00001df4	movl	%eax,(%esp)
00001df7	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001dfc	movl	0x00003008,%edx
00001e02	movl	%edx,0x0c(%ebp)
00001e05	movl	%eax,0x08(%ebp)
00001e08	leave
00001e09	jmpl	0x0000400a	; symbol stub for: _objc_msgSend

The optimized method is significantly shorter, as expected from the compiler option -Os. First, you’ll notice that all those pesky nop instructions have been deleted. Stallman put them in unoptimized builds just to annoy us. (Or they may have been for Fix and Continue, but I always assume the worst.) There are additional optimizations as well that I won’t belabor here, because I’m eager to get to the climax. (Sorry, dear.) For your enlightenment and enjoyment, here’s the optimized disassembly for -myRoundaboutProcessName:

-[MyObject myRoundaboutProcessName]:
00001e0e	pushl	%ebp
00001e0f	movl	%esp,%ebp
00001e11	subl	$0x18,%esp
00001e14	movl	0x00003000,%eax
00001e19	movl	%eax,0x04(%esp)
00001e1d	movl	0x0000302c,%eax
00001e22	movl	%eax,(%esp)
00001e25	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001e2a	movl	0x00003004,%edx
00001e30	movl	%edx,0x04(%esp)
00001e34	movl	%eax,(%esp)
00001e37	calll	0x0000400a	; symbol stub for: _objc_msgSend
00001e3c	movl	0x00003008,%edx
00001e42	movl	%edx,0x0c(%ebp)
00001e45	movl	%eax,0x08(%ebp)
00001e48	leave
00001e49	jmpl	0x0000400a	; symbol stub for: _objc_msgSend

Identical! Ah, that’s nice. Smoke ‘em if you got ‘em.

In conclusion, feel free to sprinkle, pepper, dash, or even drown your code with local variables. And with the engineering hours of debugging time you save, get me a nice (not free) present. I’m partial to flavored coffee and unflavored MacBooks.

Gruber does a snow job on Snow Leopard

Saturday, September 5th, 2009

John Gruber holds Apple blameless for shipping Mac OS X 10.6.0 with an outdated version of Adobe Flash Player, a version with known vulnerabilities that have been exploited in the wild. The essence of his absolution is the following timeline: “Adobe released version of Flash on July 30. Snow Leopard went GM on Friday August 7″.

It is true that Adobe released Flash Player to the public on July 30. However, with regard to the version of Flash Player included with Snow Leopard, that date is largely irrelevant. On July 21, Adobe released a security bulletin to the public: “Adobe is aware of reports of a potential vulnerability in Adobe Reader and Acrobat 9.1.2 and Adobe Flash Player 9 and 10.” The next day, there was a public follow-up: “A critical vulnerability exists in the current versions of Flash Player (v9.0.159.0 and v10.0.22.87) for Windows, Macintosh and Linux operating systems, and the authplay.dll component that ships with Adobe Reader and Acrobat v9.x for Windows, Macintosh and UNIX operating systems. This vulnerability (CVE-2009-1862) could cause a crash and potentially allow an attacker to take control of the affected system. There are reports that this vulnerability is being actively exploited in the wild via limited, targeted attacks against Adobe Reader v9 on Windows. We are in the process of developing a fix for the issue, and expect to provide an update for Flash Player v9 and v10 for Windows, Macintosh, and Linux by July 30, 2009″.

It is crucial to note that these were all public releases. Since Apple ships Adobe Flash Player with Mac OS X, Apple and Adobe undoubtedly have a private relationship. One would presume that Adobe privately discloses security vulnerabilities in the OS X version of Flash Player to Apple in advance of public announcements and that Adobe privately provides Apple with versions of Flash Player to test on OS X prior to public release. If these things do not occur privately, then both companies ought to be blamed for failing to follow industry best practices.

I’m not sure where Gruber gets the August 7 date. (Perhaps an email from Phil?) Snow Leopard build 10A432 was seeded to eligible ADC members on August 12. If Apple had already declared build 10A432 the GM before seeding it to developers for testing, that would be completely irresponsible (though sadly, not unprecedented). In any case, if the 10A432 seed had turned up a show-stopping bug, Apple could have un-declared it GM. Is allowing an attacker to take control of a system via a web browser not a show-stopper? Gruber asks, “Should Apple have postponed Snow Leopard for another month?” Despite the rhetorical nature of the question, I’ll answer: maybe they should have. Or at least, they should have postponed it 4 days. At WWDC, Apple told us that Snow Leopard would be released in September. Last time I checked, August 28 is not in September. Even delaying Snow Leopard a month would have allowed Apple to ship on time, in September.

Setting aside these more relevant dates, let’s just accept Gruber’s 8-day window for the sake of argument. “Does anyone really think that Apple should have replaced the single-crashiest piece of software in Mac OS X with a new untested version just eight days before going GM?” Yes, I do. We’re not talking about a major update here — obviously Apple should not switch from Flash 9 to Flash 10 eight days before GM. Apple had already pulled the trigger on Flash 10 for Snow Leopard. When security vulnerabilities come to light, fixes must be released quickly. Eight days of testing the update from Flash Player to really should have been sufficient for Apple. If a critical vulnerability was discovered in Mac OS X, Apple should be able to ship a fix within 8 days, if not sooner. Indeed, there were 8 days between Adobe’s security bulletin and the release of the updated Flash Player.

Apple’s record for shipping timely fixes to security vulnerabilities is poor. For example, Apple ‘distinguished’ themselves by being the only vendor in the world to fail to join the coordinated effort to release a fix for the Kaminsky DNS vulnerability on all platforms on the same day. Instead, Apple took its own sweet time, which was particularly egregious for Mac OS X Server customers. (Though I use OS X on my personal computers, I’m glad my web host does not use OS X on their servers.) I personally was aware of the ‘Safari RSS’ vulnerability for months while it remained unpatched and could have easily exploited any visitor to my web site if I had wanted. Fortunately for you, I’m not malicious. Not criminally malicious, anyway.

Apple hot-swapped Mac OS X 10.5.8

Tuesday, September 1st, 2009

There has been some confusion in the net-o-sphere over the existence of two Mac OS X 10.5.8 builds: 9L30 and 9L31a. I think it’s time to clear up that confusion, now that Max OS X 10.6.0 has been released and nobody cares anymore about 10.5.x.

The Max OS X build version is stored in the following file on your hard drive:


On my machine, the build is 9L30. I installed 10.5.8, as usual, via the combo updater (because I’m a paranoid superfreak who also repairs permissions and offers sacrifices to Demeter). This was a prudent three days after 10.5.8 was released, though the combo updater was downloaded two days after release.

Lately (in the Holocene epoch), the Mac OS X installers have come in ‘flat’ package format (i.e, “smooth and even; without marked lumps or indentations” or “lacking interest or emotion; dull and lifeless”). This makes them opaque to the casual observer. Fortunately, I am Klondike Kat. The installer .pkg file is actually a xar archive that can be read and extracted with /usr/bin/xar. To extract the package contents into the current working directory:

$ xar -xf /Volumes/Mac\ OS\ X\ Update\ Combined/MacOSXUpdCombo10.5.8.pkg

This gives us an ‘old-style’ (Pleistocene) .pkg file whose contents we can view in Finder. The package contains, among other things, a Payload. Be careful not to ignite it, otherwise you may require intensive care, if not AppleCare. The Payload is a gzip archive, so I slapped a .gz extension on the file and gunzip‘ed it. After extracting that archive, you’re left with … yet another archive. (Apparently the Matryoshka method of software distribution.) The new Payload can be read and extracted with /bin/pax:

$ pax -f Payload -r *SystemVersion.plist

The SystemVersion.plist from my original installer is for build 9L30, but the one from the installer I downloaded today is for build 9L31a. Thus, we have to conclude that Apple ‘hot swapped’ Mac OS X 10.5.8. That is, they switched Mac OS X builds after release without bumping the version number.

Why Apple did this remains a mystery. Usually software developers do it when they discover an issue shortly after release but don’t want to go to the trouble of making a public announcement of a new version. What was the issue, and do those of us who have build 9L30 installed still suffer from the issue? For the answer to those questions, you’ll have to read the release notes. ;-)

Snow Leopard hidden Dock preference

Friday, August 28th, 2009

In Mac OS X 10.5 and earlier, clicking and holding on an item in the Dock would bring up a contextual menu for that Dock item. In Mac OS X 10.6, popularly known as Snow Leopard, unpopularly known as Leopard Service Pack 1 or “What am I supposed to do with my Power Mac G5 Quad?”, this behavior has changed. One of the new non-Exchange non-features in Snow Leopard is Dock Exposé. Clicking and holding on an application icon in the Snow Leopard Dock invokes Exposé for that application. This is the same effect you see when pressing the F10 key in Tiger and later.

If for some bizarre reason you prefer the old behavior (you backward, reactionary, Obama-hating Luddite), it is possible to bring it back. This is a Lap Cat Software exclusive — you heard it here first, folks! Launch the Terminal application and enter the following:

defaults write show-expose-menus -bool no; killall Dock

You’re welcome. Remember me fondly in your will.

It’s over

Thursday, August 6th, 2009

I figured I’d cruise, at least through the Spring. However, the wheels on the bus go round and round.


I am still master of my domain. Although I need to renew before it expires in three weeks.

Boycott Radar

Wednesday, August 5th, 2009

Until further notice, I’m boycotting Radar. No more filing bugs, no more responding to bugs. For me, Radar is both frustrating beyond belief and also a waste of time. I recommend that my fellow Mac developers join my boycott, if for no other reason than to preserve whatever sanity and mental health you have remaining. I’ve come to the conclusion that life without Radar will be happier and more productive.

In order of importance (and annoyance), here are my major complaints about Radar:

  1. Mindless responses to bugs from Apple zombies … err, employees
    I expect a knowledgeable person to read and evaluate my bugs carefully. I’m sick and tired of getting stupid, sometimes irrelevant responses. It’s clear in many cases that the Apple employee was basically skimming for keywords and didn’t bother to actually read the bug. And I’m far from alone here: I’ve heard numerous examples (otherwise known as horror stories) from other developers of the same kind of maddening response to their bugs. We developers spend a lot of time discovering, investigating, and reproducing these bugs for Apple, without receiving any compensation. Inexplicably, though, Apple employees are dismissive of our help. They seem to care more about closing the Radar than fixing the bug that the Radar reports.
  2. Duplicate bugs are second class citizens
    If your bug gets marked as a dupe, you’re doomed. Don’t expect to ever hear about it again, not even if it’s fixed. Apple’s canned response says, “To request the status of the original bug, please update your report directly via the Apple Bug Reporter”, which is ridiculous, because you could have dozens or even hundreds of duplicates, and it can sometimes take years for a bug to get fixed, so how often are you supposed to make status requests?
  3. No searchable bug database
    If you’re lucky, an Apple engineer on a mailing list may tell you that your problem is a known issue. If not, you could flail around for days trying to figure out why your code that should work doesn’t work, because of a Mac OS X bug. A number of other companies provide searchable bug databases to their developers, why can’t Apple? It’s true that sometimes your bug reports contain confidential information that you don’t want to share with other developers (your competitors, for example), but often they don’t, and it would be nice to have an ‘opt in’ option to allow other developers to see your bug. It’s also true that Apple needs to protect its secrets; however, Apple should realize that not everything is or needs to be secret, and as ADC members we’re already bound by Non-Disclosure Agreements, so what’s the point of being under an NDA with Apple if Apple never discloses anything to us? The existence of Open Radar demonstrates how ludicrous it is that Apple does not provide a searchable bug database themselves. Although I don’t post my bugs on Open Radar because I don’t have a Google account, I do have a list here.
  4. Wasting my time asking me to verify unfixed bugs
    Apple employees seem to think third party developers have nothing better to do than perform unpaid QA work for Apple. A number of times, I’ve gotten requests to verify that a bug still exists in software update X, and indeed it does still exist in software update X, as demonstrated by the very steps to reproduce that I listed in my bug report. Did anyone at Apple even bother to follow my steps? (That’s a rhetorical question — obviously, no.) What were you thinking here, that my bug would magically disappear without having to do anything? Sorry, your deus ex machina failed to show up, stop wasting my time and start fixing the bug. If Apple is understaffed, and its employees are overworked and don’t have enough time to do this themselves, that’s not my fault. If I hear one more excuse about Apple not having the resources, I’m going to puke. Or punch someone. Or puke on someone’s fist. Apple makes more than a billion dollars a quarter in profit. My company makes slightly less than that.

What I’ve come to realize is that we developers don’t need Radar. Apple needs us, but we don’t need them (for this, anyway). The time between filing a bug and seeing a fix for the bug shipped in a Mac OS X software update is usually quite long, sometimes infinitely long. If I discover a Mac OS X bug that affects my software, I can’t wait for a fix from Apple, I have to write a workaround immediately. Thus, by the time I file a bug, I don’t really need a fix for it. The sole purpose of filing the bug is to help other developers and to make the Mac OS X platform better. Essentially, it’s charity work. If Apple makes charity work for them really difficult and annoying, then I’m going to find something better to do, like adopt a cat, or a highway.

Vienna 2.3.2 security update

Monday, July 27th, 2009

I’ve just released the security update Vienna 2.3.2. Security is no laughing matter, so all Vienna users need to update to version 2.3.2. Release notes are here.

The technical details of the vulnerability are instructive. The problem was this line of code:

returnCode = NSRunAlertPanel(alertTitle, alertBody, NSLocalizedString(@"Delete", nil), NSLocalizedString(@"Cancel", nil), nil);

Well, that’s not entirely true. The problem was that line combined with certain assignments of alertBody, for example:

alertBody = [NSString stringWithFormat:@"Are you sure you want to unsubscribe from \"%@\"? This operation will delete all cached articles.", [folder name]];

By default, the value of [folder name] comes from a most trustworthy source: the internet. If, by chance or design, the name contains conversion specifiers, you’re in big trouble. For a discussion of this kind of vulnerability, see Apple’s Secure Coding Guide. The Secure Coding Guide is a must read for Mac Developers. (I also recommend this. Tell them Jeff sent you.)

One simple fix for the vulnerability is as follows:

returnCode = NSRunAlertPanel(alertTitle, @"%@", NSLocalizedString(@"Delete", nil), NSLocalizedString(@"Cancel", nil), nil, alertBody);

With this fix, the alertBody string — which contains the unvalidated folder name — is no longer treated as a format string.

You need to be very careful about using NSRunAlertPanel() and similar functions. Unfortunately, the API and documentation are quite bad. The Discussion doesn’t even mention that the second argument is a format string; for that, you have to read the documentation for another function. Furthermore, it’s absolutely insane to put three unrelated items between the format string and the optional arguments for the format string.

As you know, however, you go to war with the API you have, not the API you might want or wish to have at a later time.

WWDC: Busted

Tuesday, June 16th, 2009

WWDC is over, and I’m now home safe, somewhat sound. I truly enjoyed my time at DEN, as well as the brief visit to San Francisco in between. I met some people, failed to meet a lot of people, saw some old friends and old enemies (I’ll let you decide who’s who), fell in love, got married, got divorced, killed a man, and won the NBA championship. Ok, maybe not all that, but I did pay too much for brunch.

For me, the most useful part of WWDC was the labs. At no other time of the year do you get unfiltered, one-on-one contact with Apple engineers. Surprisingly, I even managed to avoid punching any of them. I spent hours in the lab talking with the QuickTime, WebKit, and CFNetwork teams about various issues I’ve encountered. One engineer even volunteered to exercise his Gdb Fu on my MBP.

There’s much more to WWDC than just the technical side, though. In addition to the valuable information I learned, I was also able to bring home a backpack, three shirts, a Red Sweater button, and the common cold.

I do have one complaint about WWDC (not the food). We stood in line for over an hour — outside in the chilly wind — to get into the keynote, but it wasn’t until 15 minutes after the keynote started that we finally got in … to the overflow room. I understand that there’s not enough space for everyone in the main room, but Apple knew well in advance both what time the keynote starts and how many people were attending WWDC, so there is absolutely no excuse for failing to open the doors in time for everyone to get in the building and sit down. Really, it’s shameful. I would like to hear an apology from Apple for this major logistical screwup. It gave me a bad impression at the very beginning of my first WWDC and first keynote. Not to mention that tickets are quite expensive, yet non-attendees following on the internet had better access to the keynote than me. WWDC organizers, you suck!

I’m not allowed to say anything else about the conference, because of the NDA. I may be breaking it just by telling you I was there. Nonetheless, I’m going to share one little Snow Leopard secret with you. To distinguish it from Leopard, the latest WWDC seed has a new default Desktop background image: Hello Kitty.