Why doesn't device_create return error when a file already exists? - linux-kernel

I am writing a PCI driver with a character device for an interface (Linux 4.9.13). Here's the scenario that bothers me:
Run touch /dev/foo0 which creates a normal file in the /dev directory.
Load the driver module. Here's a pseudo code representing what happens there (pretty standard character device registration):
// When the module is initialized:
alloc_chrdev_region(&dev, 0, 256, "foo");
class = class_create(THIS_MODULE, "foo");
// Later, when a suitable PCI device is connected the probe function
// calls the following functions:
cdev_init(dev->md_cdev, &fops);
dev->md_devnum = MKDEV(major, 0 + index);
res = cdev_add(dev->md_cdev, dev->md_devnum, 1);
dev->md_sysfsdev = device_create(class, 0, dev->md_devnum, 0, "foo%d", index);
Details:
index is just another free index
What seems weird to me is nothing raises an error that there is already a /dev/foo0 file which is not a character device. I do check all the errors (I think so) but I omitted related code for the sake of conciseness. Everything works as expected if I do not run touch /dev/foo0. Otherwise, I can neither read nor write to the device.
Why is it so? Shouldn't device_create return an error or at least create /dev/foo1 instead?

Related

CopyPipe of DriverKit IOUSBHostInterface fails with kIOReturnError (0xe00002bc)

For my own edification, I'm trying to read some audio data from a USB audio interface using a DriverKit System Extension.
My IOProviderClass is IOUSBHostInterface. I can successfully Open() the interface, but CopyPipe() returns kIOReturnError (0xe00002bc). Why can't I copy the pipe?
To be able to open the interface at all, I had to outmatch AppleUSBAudio so my IOKitPersonalities explicitly match the bConfigurationValue, bInterfaceNumber, idVendor, idProduct, and bcdDevice keys. This list may not be minimal.
In ioreg I can normally see the interfaces (sometimes only my matching one is there, although I think this is a degenerate situation). I see a AppleUserUSBHostHIDDevice child on some of my other interfaces. Could this be the problem? Normally the device has no problem being both USBAudio and HID. I am trying unsuccessfully to out match HID too.
I was passing the wrong endpoint address to CopyPipe().
To find an endpoint address you need to enumerate through the IOUSBDescriptorHeaders in the IOUSBConfigurationDescriptor and examine the descriptors with bDescriptorType equal to kIOUSBDescriptorTypeEndpoint.
IOUSBGetNextDescriptor() from USBDriverKit/AppleUSBDescriptorParsing.h is made for this and will save you from having think about pointer manipulation.
If the endpoint is in a different alternate setting, then you need to switch the interface to that one with SelectAlternateSetting().
void
enumerate_configs(const IOUSBConfigurationDescriptor *configDesc) {
const IOUSBDescriptorHeader *curHeader = NULL;
while ((curHeader = IOUSBGetNextDescriptor(configDesc, curHeader))) {
switch (curHeader->bDescriptorType) {
case kIOUSBDescriptorTypeEndpoint: {
auto endpoint = (const IOUSBEndpointDescriptor *)curHeader;
os_log(OS_LOG_DEFAULT, "Endpoint bLength: %{public}i, bDescriptorType: %i, bEndpointAddress: %i, bmAttributes: 0x%x, wMaxPacketSize: %i, bInterval: %i",
endpoint->bLength,
endpoint->bDescriptorType,
endpoint->bEndpointAddress, // pass this to CopyPipe()
endpoint->bmAttributes,
endpoint->wMaxPacketSize,
endpoint->bInterval);
}
break;
default:
os_log(OS_LOG_DEFAULT, "some other type: %{public}i", curHeader->bDescriptorType);
break;
}
}
}

Enabling Closed-Display Mode w/o Meeting Apple's Requirements

EDIT:
I have heavily edited this question after making some significant new discoveries and the question not having any answers yet.
Historically/AFAIK, keeping your Mac awake while in closed-display mode and not meeting Apple's requirements, has only been possible with a kernel extension (kext), or a command run as root. Recently however, I have discovered that there must be another way. I could really use some help figuring out how to get this working for use in a (100% free, no IAP) sandboxed Mac App Store (MAS) compatible app.
I have confirmed that some other MAS apps are able to do this, and it looks like they might be writing YES to a key named clamshellSleepDisabled. Or perhaps there's some other trickery involved that causes the key value to be set to YES? I found the function in IOPMrootDomain.cpp:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
I'd like to give this a try and see if that's all it takes, but I don't really have any idea about how to go about calling this function. It's certainly not a part of the IOPMrootDomain documentation, and I can't seem to find any helpful example code for functions that are in the IOPMrootDomain documentation, such as setAggressiveness or setPMAssertionLevel. Here's some evidence of what's going on behind the scenes according to Console:
I've had a tiny bit of experience working with IOMProotDomain via adapting some of ControlPlane's source for another project, but I'm at a loss for how to get started on this. Any help would be greatly appreciated. Thank you!
EDIT:
With #pmdj's contribution/answer, this has been solved!
Full example project:
https://github.com/x74353/CDMManager
This ended up being surprisingly simple/straightforward:
1. Import header:
#import <IOKit/pwr_mgt/IOPMLib.h>
2. Add this function in your implementation file:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
3. Use the following to call the above function from somewhere else in your implementation:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
I have no personal experience with the PM root domain, but I do have extensive experience with IOKit, so here goes:
You want IOPMrootDomain::setDisableClamShellSleep() to be called.
A code search for sites calling setDisableClamShellSleep() quickly reveals a location in RootDomainUserClient::externalMethod(), in the file iokit/Kernel/RootDomainUserClient.cpp. This is certainly promising, as externalMethod() is what gets called in response to user space programs calling the IOConnectCall*() family of functions.
Let's dig in:
IOReturn RootDomainUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
IOReturn ret = kIOReturnBadArgument;
switch (selector)
{
…
…
…
case kPMSetClamshellSleepState:
fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);
ret = kIOReturnSuccess;
break;
…
So, to invoke setDisableClamShellSleep() you'll need to:
Open a user client connection to IOPMrootDomain. This looks straightforward, because:
Upon inspection, IOPMrootDomain has an IOUserClientClass property of RootDomainUserClient, so IOServiceOpen() from user space will by default create an RootDomainUserClient instance.
IOPMrootDomain does not override the newUserClient member function, so there are no access controls there.
RootDomainUserClient::initWithTask() does not appear to place any restrictions (e.g. root user, code signing) on the connecting user space process.
So it should simply be a case of running this code in your program:
io_connect_t connection = IO_OBJECT_NULL;
IOReturn ret = IOServiceOpen(
root_domain_service,
current_task(),
0, // user client type, ignored
&connection);
Call the appropriate external method.
From the code excerpt earlier on, we know that the selector must be kPMSetClamshellSleepState.
arguments->scalarInput[0] being zero will call setDisableClamShellSleep(false), while a nonzero value will call setDisableClamShellSleep(true).
This amounts to:
IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint64_t inputs[] = { disable ? 1 : 0 };
return IOConnectCallScalarMethod(
root_domain_connection, kPMSetClamshellSleepState,
&inputs, 1, // 1 = length of array 'inputs'
NULL, &num_outputs);
}
When you're done with your io_connect_t handle, don't forget to IOServiceClose() it.
This should let you toggle clamshell sleep on or off. Note that there does not appear to be any provision for automatically resetting the value to its original state, so if your program crashes or exits without cleaning up after itself, whatever state was last set will remain. This might not be great from a user experience perspective, so perhaps try to defend against it somehow, for example in a crash handler.

Workaround for a certain IOServiceOpen() call requiring root privileges

Background
It is possible to perform a software-controlled disconnection of the power adapter of a Mac laptop by creating an DisableInflow power management assertion.
Code from this answer to an SO question can be used to create said assertion. The following is a working example that creates this assertion until the process is killed:
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <unistd.h>
int main()
{
IOPMAssertionID neverSleep = 0;
IOPMAssertionCreateWithName(kIOPMAssertionTypeDisableInflow,
kIOPMAssertionLevelOn,
CFSTR("disable inflow"),
&neverSleep);
while (1)
{
sleep(1);
}
}
This runs successfully and the power adapter is disconnected by software while the process is running.
What's interesting, though, is that I was able to run this code as a regular user, without root privileges, which wasn't supposed to happen. For instance, note the comment in this file from Apple's open source repositories:
// Disables AC Power Inflow (requires root to initiate)
#define kIOPMAssertionTypeDisableInflow CFSTR("DisableInflow")
#define kIOPMInflowDisableAssertion kIOPMAssertionTypeDisableInflow
I found some code which apparently performs the actual communication with the charger; it can be found here. The following functions, from this file, appears to be of particular interest:
IOReturn
AppleSmartBatteryManagerUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
if (selector >= kNumBattMethods) {
// Invalid selector
return kIOReturnBadArgument;
}
switch (selector)
{
case kSBInflowDisable:
// 1 scalar in, 1 scalar out
return this->secureInflowDisable((int)arguments->scalarInput[0],
(int *)&arguments->scalarOutput[0]);
break;
// ...
}
// ...
}
IOReturn AppleSmartBatteryManagerUserClient::secureInflowDisable(
int level,
int *return_code)
{
int admin_priv = 0;
IOReturn ret = kIOReturnNotPrivileged;
if( !(level == 0 || level == 1))
{
*return_code = kIOReturnBadArgument;
return kIOReturnSuccess;
}
ret = clientHasPrivilege(fOwningTask, kIOClientPrivilegeAdministrator);
admin_priv = (kIOReturnSuccess == ret);
if(admin_priv && fOwner) {
*return_code = fOwner->disableInflow( level );
return kIOReturnSuccess;
} else {
*return_code = kIOReturnNotPrivileged;
return kIOReturnSuccess;
}
}
Note how, in secureInflowDisable(), root privileges are checked for prior to running the code. Note also this initialization code in the same file, again requiring root privileges, as explicitly pointed out in the comments:
bool AppleSmartBatteryManagerUserClient::initWithTask(task_t owningTask,
void *security_id, UInt32 type, OSDictionary * properties)
{
uint32_t _pid;
/* 1. Only root processes may open a SmartBatteryManagerUserClient.
* 2. Attempts to create exclusive UserClients will fail if an
* exclusive user client is attached.
* 3. Non-exclusive clients will not be able to perform transactions
* while an exclusive client is attached.
* 3a. Only battery firmware updaters should bother being exclusive.
*/
if ( kIOReturnSuccess !=
clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator))
{
return false;
}
// ...
}
Starting from the code from the same SO question above (the question itself, not the answer), for the sendSmartBatteryCommand() function, I wrote some code that calls the function passing kSBInflowDisable as the selector (the variable which in the code).
Unlike the code using assertions, this one only works as root. If running as a regular user, IOServiceOpen() returns, weirdly enough, kIOReturnBadArgument (not kIOReturnNotPrivileged, as I would have expected). Perhaps this has to do with the initWithTask() method above.
The question
I need to perform a call with a different selector to this same Smart Battery Manager kext. Even so, I can't even get to the IOConnectCallMethod() since IOServiceOpen() fails, presumably because the initWithTask() method prevents any non-root users from opening the service.
The question, therefore, is this: how is IOPMAssertionCreateWithName() capable of creating a DisableInflow assertion without root privileges?
The only possibility I can think of is if there's a root-owned process to which requests are forwarded, and which performs the actual work of calling IOServiceOpen() and later IOConnectCallMethod() as root.
However, I'm hoping there's a different way of calling the Smart Battery Manager kext which doesn't require root (one that doesn't involve the IOServiceOpen() call.) Using IOPMAssertionCreateWithName() itself is not possible in my application, since I need to call a different selector within that kext, not the one that disables inflow.
It's also possible this is in fact a security vulnerability, which Apple will now fix in a future release as soon as it is alerted to this question. That would be too bad, but understandable.
Although running as root is a possibility in macOS, it's obviously desirable to avoid privilege elevation unless absolutely necessary. Also, in the future I'd like to run the same code under iOS, where it's impossible to run anything as root, in my understanding (note this is an app I'm developing for my own personal use; I understand linking to IOKit wipes out any chance of getting the app published in the App Store).

Running a kernel with CUDAfy .NET using OpenCL asynchronously

I am trying to make asynchronous kernel calls to my GPGPU using CUDAfy .NET.
When I pass values to the kernel and copy them back to the host, I do not always get the value I expect.
I have a structure Foo with a byte Bar:
[Cudafy]
public struct Foo {
public byte Bar;
}
And I have a kernel I want to call:
[Cudafy]
public static void simulation(GThread thread, Foo[] f)
{
f[0].Bar = 3;
thread.SyncThreads();
}
I have a single thread with streamID = 1 (I tried using multiple threads, and noticed the issue. Reducing to a single thread didn't seem to fix the issue though).
//allocate
streamID = 1;
count = 1;
gpu.CreateStream(streamID);
Foo[] sF = new Foo[count];
IntPtr hF = gpu.HostAllocate<Foo>(count);
Foo[] dF = gpu.Allocate<Foo>(sF);
while (true)
{
//set value
sF[0].Bar = 1;
byte begin = sF[0].Bar;
//host -> pinned
GPGPU.CopyOnHost<Foo>(sF, 0, hF, 0, count);
sF[0].Bar = 2;
lock (gpu)
{
//pinned -> device
gpu.CopyToDeviceAsync<Foo>(hF, 0, dF, 0, count, streamID);
//run
gpu.Launch().simulation(dF);
//device -> pinned
gpu.CopyFromDeviceAsync<Foo>(dF, 0, hF, 0, count, streamID);
}
//WAIT
gpu.SynchronizeStream(streamID);
//pinned -> host
GPGPU.CopyOnHost<Foo>(hF, 0, sF, 0, count);
byte end = sF[0].Bar;
}
//de-allocate
gpu.Free(dF);
gpu.HostFree(hF);
gpu.DestroyStream(streamID);
First I create a stream on the GPU.
I am creating a regular structure Foo array of size 1 (sF) and setting it's Bar value to 1. Then I create pinned memory on the host (hF) for Foo as well. I also create memory on the device for Foo (dF).
I initialize the structure's Bar value to 1 then I copy it to the pinned memory (As a check, I set the value to 2 for the structure after copying to pinned, you'll see why later). Then I use a lock to ensure I have full access to the GPU and I queue a copy to dF, a run for the kernel, and a copy from dF. At this point I don't know when this will all actually run on the GPU... so I can call SynchronizeStream to wait on the host until the device is done.
When it's done, I can copy the pinned memory (hF) to the shared memory (sF). When I get the value, it's usually a 3 (which was set on the device) or a 1 (which means either the value wasn't set in the kernel, or the new value wasn't copied to the pinned memory). I do know that the pinned memory is copied to the structure because the structure never has the value of 2.
Over many runs, a small percentage is runs results in something other than begin=1 and end=3. It would always be begin=1, end=1 and it happens about 5-10% of the time.
I have no idea why this happens. I know it generally highlights a race condition, but by calling the sync calls, I would expect the async calls to work in a predictable fashion.
Why would I be encountering this kind of issue with this code?
Thank you so much!
-Phil
I just figured out the issue that was occurring. While the launch was being done asynchronously... I didn't include the stream for the launch.
Changing my launch to be:
gpu.Launch(gridsize,blocksize,streamID).simulation(dF);
resolved the problem. It seems that the launches were occurring on stream 0 and the stream 1 and 2 were being synced. So sometimes the data gets set, sometimes it doesn't. A race condition.

What is Target Device of IOCTL_USB_GET_ROOT_HUB_NAME (USB driver specific IOCTL IRQ)

I am a bit confused by the USB IOCTL IOCTL_USB_GET_ROOT_HUB_NAME. What is the target device of it? Although the MSDN WDK doc clearly indicates the target device, I am still confused by the USBVIEW sample provided by the WDK. The reason I'm confused is as follows:
I am new to kernel mode and USB driver writing in Windows and is now studying the USBVIEW sample from the windows driver kit http://msdn.microsoft.com/en-us/library/ff558728(v=vs.85).aspx. The MSDN describes the first step the USBVIEW sample performs as:
Enumerate host controllers and root
hubs. Host controllers have symbolic
link names of the form "HCDx", where x
starts at 0.
Use CreateFile() to open each host
controller symbolic link.
Create a node in the tree view to
represent each host controller.
After a host controller has been
opened, send the host controller an
IOCTL_USB_GET_ROOT_HUB_NAME request to
get the symbolic link name of the root
hub that is part of the host
controller
But, I double checked the usage of IOCTL_USB_GET_ROOT_HUB_NAME in MSDN http://msdn.microsoft.com/en-us/library/ff537326(v=VS.85).aspx
which says:
IOCTL_USB_GET_ROOT_HUB_NAME is a
user-mode I/O control request. This
request targets the USB hub FDO.
Note that the target of the IOCTL_USB_GET_ROOT_HUB_NAME IRP is a USB Hub FDO. However, as described by the USBVIEW sample, we just retreived the host controller symbolic link which means the device object is a host controller device object. How could we send it a IOCTL_USB_GET_ROOT_HUB_NAME IRP? Should we retreive a USB hub FDO somehow first?
I would guess it's an unfortunate copy-paste error. IOCTL_USB_GET_ROOT_HUB_NAME is indeed sent to the host controller and therefore handled by the USB Host Controller FDO.
By the way, just to put you in context:
The term "FDO" only loosely concerns user mode -- it's not like you can access any other "xDO" anyway. If you were to send this IOCTL in kernel mode, then sure, you can send an IOCTL to any specific device object in the device stack ("can" doesn't mean "should", mind you). However, a DeviceIoControl from a user mode application always sends IOCTLs to the top of the device stack (therefore it passes all the filters, the FDO and down to the PDO).
This question was asked on March 28, so I really hope you've solved it by now :)
As the documentation states you will need a handle to the USB host controller but it is not very clear on how you are supposed to get such a handle. In USBView something similar to this function is used to get the device path name by passing GUID_DEVINTERFACE_USB_HOST_CONTROLLER (include initguid.h and usbiodef.h):
vector<wstring> EnumDevices(
_In_ const GUID Guid
)
{
vector<wstring> r;
int index = 0;
HDEVINFO hDevInfo = SetupDiGetClassDevs(&Guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
SP_DEVINFO_DATA DevInfoData;
memset(&DevInfoData, 0, sizeof(SP_DEVINFO_DATA));
DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
while (SetupDiEnumDeviceInfo(hDevInfo, index, &DevInfoData)) {
index++;
int jndex = 0;
SP_DEVICE_INTERFACE_DATA DevIntData;
memset(&DevIntData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
DevIntData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
while (SetupDiEnumDeviceInterfaces(
hDevInfo,
&DevInfoData, &Guid, jndex, &DevIntData
)) {
jndex++;
// Get the size required for the structure.
DWORD RequiredSize;
SetupDiGetDeviceInterfaceDetail(
hDevInfo, &DevIntData, NULL, NULL, &RequiredSize, NULL
);
PSP_DEVICE_INTERFACE_DETAIL_DATA pDevIntDetData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + RequiredSize
);
memset(pDevIntDetData, 0, sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + RequiredSize);
pDevIntDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&DevIntData,
pDevIntDetData, RequiredSize,
NULL,
&DevInfoData
);
r.push_back(wstring(pDevIntDetData->DevicePath));
free(pDevIntDetData);
}
}
return r;
}
Keep in mind using the above function you can also request devices of type GUID_DEVINTERFACE_USB_HUB and GUID_DEVINTERFACE_USB_DEVICE which may eliminate any need to interact with the host controller or hubs directly.

Resources