Basic facts about MacOS X kernel extensions

written on Tue 21 April 2015

Here are some facts about OS X's kernel extensions (kexts) that I've managed to aggregate.

Installation basics

Normally, whole directory that contains the kext must be owned by the root:wheel user. If it's not, you will get errors during loading phase of the kext.

Additionally, your kext must be writable by root only. If it's not, you will get loading errors!

Be aware that you need to use the -R option when performing the chmod command. This means that you need to fix the privileges for all the files and directories inside the kext bundle.

You can dynamically check for conformance to these rules by using the kextutil tool to load your kext (you can also examine the -tn -- test and do not load -- option). It's usage is the same as kextload, so I assume you won't have any problems here.

Development deployment

When writing a new kernel extension it is often a good idea to test it on multiple systems. Fastest way for the developer to perform such tests is to use multiple virtual machines, where each machine is controlled by a different version of the operating system.

Different versions of MacOS X have different policies when it comes to loading thirdparty (i.e. your own) kernel extensions. It is sane to assume that you can't codesign each compilation of your kext, so it's important to know if a system will allow loading unsigned kexts or not.

OS X 10.8 (Mountain Lion, and earlier)

Digitally signed kexts are not allowed. Yes, they're not supported. If you digitally sign a kext file by using an embedded digital sign inside the main kext MachO binary file, the system will refuse to load your kext. So, as far as development requirements goes, you're good to go already!

OS X 10.9 (Mavericks)

Respects codesign, but not enforces it. If your kext is not digitally signed, it still can be loaded (at least by using the kextload utility). The system can emit a warning with a message that it can't verify the origin of your kext, but it will load it anyway. If you're OK with this message, you're good to go, as a developer. But there's a small catch: if you install inside /Library/Extensions, you need to have your kext digitally signed. If it's not signed, it will not load.

So, in order to be protected from tampering of your kext in Mavericks, you need to install your kext inside /Library/Extensions.

OS X 10.10 (Yosemite)

Complete enforcement of codesign. Your kext will not be loaded if it's not digitally signed. As a developer, you're screwed.

If you're doing your development on a VM, you can force your system to disable checking of digital signs of every kext. Important thing to note is that it will literally disable the digital sign checking code across the whole system, so don't do it on the computers of your clients!

Disabling is easy. You need to append the kext-dev-mode=1 option to the boot args of your kernel, by using the nvram utility:

sudo nvram boot-args=kext-dev-mode=1

Please be aware that you need to append this option to your existing boot-args, not overwrite those args (who knows what's in there).

Also, nvram doesn't work in VirtualBox. If you're using VirtualBox, try this command on your host machine:

VBoxManage setextradata <name_of_your_vm> "VBoxInternal2/EfiBootArgs" "kext-dev-mode=1"

If you're using a Hackintosh virtual machine inside VirtualBox that doesn't use EFI (not that I'm using it), and you still can't get it to work, you can also try this one:

You can also insert this option to the Kernel Flags entry inside this file:

/Library/Preferences/SystemConfiguration/com.apple.Boot.plist

Whatever will be more convinient for you.

OS X 10.11 (El Capitan)

Like Yosemite, complete enforcement of codesign. However, there is a twist: codesign policy on El Capitan is different than on Yosemite. El Capitan introduced something that is called System Integrity Protection, which seems to wrap up some things that were previously used by codesign subsystem alone.

The kext-dev-mode=1 option that was previously used to disable kext digital sign enforcement doesn't work anymore. At the time of writing this post, El Capitan is still not fully released, so the method below can change, but it seems that SIP can be disabled by following these steps:

  • Booting into Recovery OS (hit Command+R on boot)

  • Enter the Security Configuration option, which can be found inside Utilities menu,

  • Uncheck the System Integrity Protection checkbox,

  • Hit Apply and reboot the machine.

Theoretically, it should work. The setting is still saved inside NVRAM, so i'm not sure why they didn't use the same NVRAM option as before, but they seem to had their reasons (I hope).

If, by chance, this won't work, try another method.

Enter Utilities/Terminal, and use csrutil command to disable SIP:

# csrutil status
System Integrity Protection status: enabled.
# csrutil disable
Successfully disabled System Integrity Protection. Please restart the
machine for changes to take effect.

That's it.

However, if you're using VirtualBox, this won't work. You can't simply "enter recovery OS". Instead, you will want to enter "installation OS", or whatever the name is. Follow this article in order to find out how to do it.

Problems with codesign in Mountain Lion (10.8) and earlier OS X versions

Apple suggests that if you would want to retain backward compatibility before Mountain Lion release (10.8), you need to install a signed kext inside /Library/Extensions, as well as unsigned kext inside /System/Library/Extensions. OS X versions before Mavericks (10.9) will ignore the signed kext, and load only unsigned kext.

It appears that you can also use a different technique of codesigning of a kext by embedding an external digital sign. To do this, examine the --no-macho option of codesign utility. It should place the digital sign externally inside the _CodeSignature/CodeSignature file. This works, because normally, an internally embedded digital sign is placed inside LC_CODE_SIGNATURE load command, inside main kext binary. The problem is that older dynamic linkers in older OS X versions can't handle this load command, only new ones can handle it. By placing it outside of the kext binary, you won't confuse older linkers with an unknown load command.