I'm attempting to write an application that talks to a custom USB device. We have written a driver (kext) that is automatically loaded when the USB device is attached to the computer. It is installed into the appropriate location (/System/Library/Extensions & /Library/Extensions) during the installation process.
In my application (.app), I need to be able to confirm that this kext is installed and is loadable on the user's computer.
My initial thought was to use the "System" command and get the result of "kextstat | grep mykext". However, kextstat only shows the kernel extensions that are loaded. (It's possible the application can be launched before the user has attached the USB device).
The only other thing that I can think of is to use "kextfind | grep mykext" to determine if the kext exists, however, I don't know if it is loadable or not. (ie. it could exist, but someone messed with the plist file, and now the signature fails, so it won't ever be loaded)
I know that it's possible, because Apple's System Profiler is able to list all of the extensions, if they are loaded, and if they are loadable.
From the kextutil man page:
kextutil prints diagnostic information about kexts by default, but some options cause certain tests to be skipped. To ensure that all tests are performed, use the -print-diagnostics (-t) option. The -print-diagnostics option is typically used with -no-load (-n) after a load failure to pinpoint a problem. It can be used with any other set of options, however.
Note: when the -no-load (-n) option is used then sudo (root user) isn't required.
Related
I'm perplexed by this one and not sure what's relevant so will include all context:
MacBook Pro with an M1 Pro running macOS 12.6.
Apple clang version 14.0.0, freshly installed by deleting DeveloperTools folder and running xcode-select --install.
Using zsh in Terminal.
Network share mounted using no-configuration Finder method (seems to use standard SMB, but authenticates with my Apple ID)
Network share is my home directory on a iMac with a Core i5 running macOS 11.6.8.
Update: also tried root directory and using the tmp directory, to eliminate one category of doubt. Same result.
The minimum repeatable example of the issue I've managed to find is:
Use gcc from Apple's Developer Tools to compile a “Hello World” C application (originally discovered using ghc to compile Haskell - effect is the same).
Run the compiled executable. No surprises.
cd to the mounted network drive.
Do the same thing there - compiled executable hangs! First surprise, but relatively minor.
Return to the local machine. Original compiled executable still runs fine.
Use the DeveloperTools to compile anything, including the original source - compiled executable on local machine now hangs!
I've created an asciinema recording of the MRE. You can see the key part of the transcript in this still:
I’ve tried killing processes, checking lsof, unmounting the drive, logging in and out, checking the PATH, etc. Nothing gets me back to a working state short of a reboot.
Some more troubleshooting data:
gcc -v is identical for both executables, except for -fdebug-compilation-dir (set to cwd) and the name of the object file (randomly generated).
Just performing the compilation doesn't trigger the issue - running the networked executable does.
Trawling through the voluminous Console log reveals nothing relevant.
system.log shows no entries around the time of the issue.
lsof and ps -axww show reams and reams of output that is hard to spot patterns in, but I'm pretty sure there is no significant before/after differences.
I left the hung process running on the local machine overnight, and there's no change the next day.
Have I triggered some sandboxing or security fault and am being protected from disastrous consequences? Or this some clang/llvm related quirk I'm not familiar with? Or, given that ghc using its native code generator seems to have the same result, is this a bug in the way stdout is provided to executables? I'm at a loss!
Oh boy, avoiding Apple ID authentication of the network share fixed this for me.
I forced Finder to not use it’s magic no-configuration Apple ID login method, by opening the Location in Finder, clicking the "Disconnect" button and then clicking the "Connect As..." button that appears in its place. If I choose "Registered User" and use my username and password, I can then execute exactly the same commands (since the mount name ends up being the same) and execution works without an issue. I can continue to compile and execute to my heart's content.
That the Apple ID method is being used in the first place is not obvious (in true minimal design fashion), but subtly indicated at the top of the Finder window as "Connected as ". The only obvious difference this makes, is the username shown in mount:
Apple ID:
//com.apple.idms.appleid.prd.<UUID>#<HOSTNAME>._smb._tcp.local/<SHARE> on /Volumes/<SHARE> (smbfs, nodev, nosuid, mounted by <USERNAME>)
"Registered User":
//<USERNAME>#<HOSTNAME>._smb._tcp.local/<SHARE> on /Volumes/<SHARE> (smbfs, nodev, nosuid, mounted by <USERNAME>)
Obviously something far more significant is different, given the fundamental impact, but it's not at all clear to me what that is. So at this stage, this answer is just a workaround to a nasty bug.
I've hooked the system call to typedef int (*orig_open_f_type)(const char *__file, int __oflag, ...); and thus, whenever a file gets opened, my code gets the event before it is passed on to the system. I created a dynamic library that overrides the open call and inject this library using DYLD_INSERT_LIBRARIES - working on a Mac machine and using XCode. It is a standard step that enables me to hook calls.
Now, I have bash script in which I have some files that I want to open. I have tried xdg-open , cat, exec - but they are not triggering the system call to open the file.
How should I invoke this open call in my bash script?
Please note that I have tested my open call hook, by opening files in C code.
I believe you're running foul of Apple's SIP (System Integrity Protection) which is designed to stop people doing things like that with system-provided executables. SIP was added to Mac OS X El Capitan (10.11) and continues in macOS Sierra (10.12).
To demonstrate whether this is the problem, consider copying /bin/cat to /usr/local/bin/cat and then try hooking (running) the local copy. You might get away with it there. This 'workaround' is purely for demonstration purposes. Basically, if I'm right, SIP is Apple's way of saying "don't go messing with our software".
You can follow links from Can Mac OS X El Capitan run software compiled for Yosemite that expects libraries in /usr/gnu/lib? to find out more about SIP. Following links via What is the "rootless" feature in El Capitan, really? on Ask Different to a blog article on System Integrity Protection, it says explicitly:
Runtime protection
SIP’s protections are not limited to protecting the system from filesystem changes. There are also system calls which are now restricted in their functionality.
task_for_pid() / processor_set_tasks() fail with EPERM
Mach special ports are reset on exec(2)
dyld environment variables are ignored
DTrace probes unavailable
However, SIP does not block inspection by the developer of their own applications while they’re being developed. Xcode’s tools will continue to allow apps to be inspected and debugged during the development process.
For more details on this, I recommend taking a look at Apple’s developer documentation for SIP.
Emphasis added
Basically, this means that you won't be able to hook calls to the open() system call for Apple-supplied software installed in the system directories. You will need to rethink what you are trying to do.
Running any normal command -- like cat -- that processes a file will cause the file to be opened. You can also open a file (and immediately close it) using the shell syntax:
: < /path/to/file
If your system call hook isn't getting called, something must be wrong with your hook -- there's no way these commands are working without opening the file. Alas, you haven't explained how you implemented your hook, so we have no way of debugging that.
The file command opens the file to look at its contents.
$ file /path/to/file
I have suggested this because it eventually leads to having the system call open which can be confirmed using strace.
$ strace file /path/to/file 2>&1 | grep open
I thought one of the good things about using file is that it opens the file in read only mode. In comparison to other ideas, unlike cat, it will not have to run through the entire file, just part of it, so the time complexity using file may be constant. Unlike vim, which someone has suggested, file will return when finished and not block like a text editor would.
We use a publicly available kernel extension (http://tuntaposx.sourceforge.net) in our application to add tun/tap network functionality to macOS.
It appears that other applications also use the very same kernel extension, but unfortunately they modified the kext's bundle identifier, which prevents us from checking by the bundle identifier whether the same kernel extension is already loaded when our application tries to load its own copy of the kext.
Is there an alternative way of testing whether a kernel extension with the same functionality is already loaded? Maybe by calling some functions of the kext?
I'm writing an OSX kernel extension for an audio device driver (it's software, but emulates a hardware device).
During development, it'd be convenient to completely uninstall existing old versions and then build and install the new version from scratch. However, this occasionally seems to not be possible without a system restart.
The program itself is not running and the source files have been deleted from the /System/Library/Extensions/ dir.
But kextstat reveals a single instance:
$ kextstat | grep 'com.foo.driver.bar'
219 0 0xfff123 0x5000 0x5000 com.foo.driver.bar (0.0.1) <102 5 4 3>
(...meaning:)
Index Refs Address Size Wired Name (Version) <Linked Against>
So there are 0 Refs to my driver instance, but kextunload will sometimes fail, complaining of existing instances:
$ sudo kextunload -b com.foo.driver.bar
(kernel) Can't unload kext com.foo.driver.bar; classes have instances:
(kernel) Kext com.foo.driver.bar class FooBarDriver has 1 instance.
(kernel) Kext com.foo.driver.bar class com_foo_driver_bar has 1 instance.
Failed to unload com.foo.driver.bar - (libkern/kext) kext is in use or retained (cannot unload).
When this happens, there's no way to "force" unload the kext (that I know of).
Am I right in guessing that this single instance still exists because of a reference held in memory by the running OS kernel? That doesn't seem right, because then kextunload would always fail. So why does kextunload only sometimes require a system restart to "fully" unload all driver instances?
Running kextunload for an IOKit kext will (if no other kexts depend on it) cause the kernel to attempt to terminate() any instances of classes in that kext which are in the I/O Kit registry. It will then wait a bit and check if any of that kext's classes still have instances. If not, it will unload the kext. If instances remain, kextunload fails (the terminated instances stay terminated, though; by this I mean that I/O kit matching is not re-run on their providers).
So somehow, you're still ending up with live instances.
One possibility is that your objects are refusing to terminate(). This can happen if they have clients that won't give up control, e.g. you can't unload the driver for a disk with a mounted file system on top. Userspace clients that don't respond to termination messages are another example.
Otherwise, the instances terminate, but are not freed. Since they seem to be of two of your main driver classes, if you don't have any user clients that won't give up their claim, I'm going to go out on a limb and suggest that you might have a circular reference. If that's not it, you'll just have to hunt for retain()s which are not matched by a release(). I give some tips on how to track these down in this answer.
If the instances terminate and are deregistered, they will no longer appear in the output of the ioreg commandline tool, so that's an easy way of checking which of the two cases applies here.
I am writing a customised mass storage kernel extension for Mac, subclassed from the IOSCSIPeripheralDeviceType05 logical unit driver. I have got as far as compiling a .kext file, which passes kextutil -n -t ..., but I cannot load it for testing.
I have a non-zero IOKitDebug field in the .plist, and the GetDeviceConfiguration looks like this:
IOReturn
com_MyCompany_driver_MyDriver::GetDeviceConfiguration( void )
{
IOLog( "MyDriver overriding GetConfiguration\n" );
return super::GetDeviceConfiguration();
}
However when I copy it into /System/Library/Extensions/ and execute it with kextutil /System/Library/Extension/MyDriver.kext, nothing happens, and nothing appears in /var/log/system.log.
How do I load this .kext to be able to debug it?
First, don't put extensions in /SLE while developing them. That's asking for trouble, as it can cause the extension to be loaded automatically. This could cause the system to become unbootable. Just copy to, e.g. /tmp/ and use kextutil to load it from there.
Second, put some debug output in com_MyCompany_driver_MyDriver::init() as that's the first thing that'll get called.
Third, your problem is likely the device matching. If the device is already matched by an existing driver at the time of loading your kext, yours won't be considered. If you can't hot-plug the device and you can't unload the existing driver before loading yours, you may need to install the kext in SLE to be loaded on boot after all. If you're still having matching trouble, post some details of the device you're trying to match (ioreg/IORegistryExplorer output) and the matching dictionary from your info.plist.