In the OS X pthreads implementation (http://www.opensource.apple.com/source/Libc/Libc-825.26/pthreads/thread_setup.c?txt) they provide a fake return address on the thread stack (line 140):
ts->rip = (uintptr_t) routine;
/*
** We need to simulate a 16-byte aligned stack frame as if we had
** executed a call instruction. The stack should already be aligned
** before it comes to us and we don't need to push any arguments,
** so we shouldn't need to change it.
*/
ts->rdi = (uintptr_t) thread; /* argument to function */
*--sp = 0; /* fake return address */
ts->rsp = (uintptr_t) sp; /* set stack pointer */
I do not understand how this will not crash with an illegal instruction/segfault when the function that the thread is executing calls 'ret' and pops that return address from the stack. Can anyone explain how this is prevented/handled?
Without looking at the rest of the code, I can only venture a guess. My intuition says, the called thread procedure (the user-supplied start_routine parameter) should never return to the calling function.
Think about it: if the new thread did return, you would have two threads running over the same original code path. I imagine that the thread function that is actually called is a wrapper that calls the user-supplied start_routine. When the start_routine returns, the wrapper then calls pthread_exit.
(main thread)
v
pthread_create
v
thread_setup (sets up stack), and spawns new thread
v |
return to main thread |
|
|
v
wrapper_function
v
user-supplied start_routine
| (returns)
v
wrapper_function calls
v
pthread_exit
Again, this is just a guess, but the whole point is, the new thread should never return to the code that called pthread_create. The purpose of the wrapper then would be to ensure that pthread_exit gets called.
I would have to see what they are passing as routine to thread_setup.
My feelings are confirmed by the fact that you don't have to call pthread_exit.
Related
In my kext's stop() function, I call iflt_detach() to detach a registered iff filter. However, it appears that (for whatever reasons), the filter's detach() function may be called outside of the stop() function. In that case, what should I do in the stop function? I can't return KERN_SUCCESS since that would cause the KEXT to get unloaded with obvious side-effects for the delayed call to the detach() function.
The following snippet is from enetlognke.c and shows the stop() function:
kern_return_t com_dts_apple_kext_enetlognke_stop (kmod_info_t * ki, void * d)
{
kern_return_t retval = KERN_FAILURE; // default result, unless we know that we are
// detached from the interface.
if (gFilterRegistered == FALSE)
return KERN_SUCCESS;
if (gUnregisterProc_started == FALSE)
{
// only want to start the detach process once.
iflt_detach(gEnetFilter);
gUnregisterProc_started = TRUE;
}
if (gUnregisterProc_complete)
{
retval = KERN_SUCCESS;
}
else
{
el_printf("enetlognke_stop: incomplete\n");
}
if (retval == KERN_SUCCESS)
{
// Free KEXT resources
}
return retval;
}
gUnregisterProc_complete is set to TRUE from within this module's dispatch() function. So, if that function call is delayed (and gUnregisterProc_complete is FALSE), the stop function would veto the unload by returning KERN_FAILURE.
So, my questions are:
If KERN_FAILURE is returned, will the kernel call the KEXT's stop() function again? If not, what triggers a retry of the KEXT unload and the call to the stop() function?
Is KERN_FAILURE the correct code to return is the filter has not been detached?
Presumably, the detach function will in this case be called on another thread, once there are no threads remaining running your callbacks?
If so, this becomes a fairly straightforward thread synchronisation problem. Set up a flag variable, e.g. has_detached and protect it by a recursive mutex.
In the stop function: Lock the mutex before calling iflt_detach(). If on return, the flag hasn't been set, sleep on the flag's address while suspending the mutex, until the flag is set. Finally, unlock, and return from the stop function.
At the very end of your detach function: lock the mutex, set the flag, send a wakeup to the potentially sleeping thread and unlock. If the unlock call is in the tail position, there is no race condition between executing your detach function's code and unloading said code.
Effectively, this will block the unloading of the kext until your filter has fully detached.
Note: I haven't tried this in this particular case of network filters (I have yet to write a filter kext), but it's generally a pattern I've used a lot in other kexts.
Note 2: I say use a recursive lock to guard against deadlock in case your detach function does get called on the same thread while inside iflt_detach().
I made a kext to use my system call instead of an existing system call on reference to
Re-routing System Calls.
During a test, I wonder which process calls this systemcall.
I need to allow applications to continue normally except the specified process.
Is there anything that obtain the information of calling process?
If you take a look at the source for the regular implementation of the ptrace system call you can see that it works with the struct proc representing the calling process that's passed in as the first argument:
int
ptrace(struct proc *p, struct ptrace_args *uap, int32_t *retval)
{
// …
if (uap->req == PT_DENY_ATTACH) {
proc_lock(p);
if (ISSET(p->p_lflag, P_LTRACED)) {
proc_unlock(p);
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_FRCEXIT) | DBG_FUNC_NONE,
p->p_pid, W_EXITCODE(ENOTSUP, 0), 4, 0, 0);
exit1(p, W_EXITCODE(ENOTSUP, 0), retval);
/* drop funnel before we return */
thread_exception_return();
/* NOTREACHED */
}
SET(p->p_lflag, P_LNOATTACH);
proc_unlock(p);
return(0);
}
You can use the functions in <sys/proc.h> to get information on the given process, such as proc_pid to find the pid.
the device driver I'm working on is implementing a virtual device. The logic
is as follows:
static struct net_device_ops virt_net_ops = {
.ndo_init = virt_net_init,
.ndo_open = virt_net_open,
.ndo_stop = virt_net_stop,
.ndo_do_ioctl = virt_net_ioctl,
.ndo_get_stats = virt_net_get_stats,
.ndo_start_xmit = virt_net_start_xmit,
};
...
struct net_device *dev;
struct my_dev *virt;
dev = alloc_netdev(..);
/* check for NULL */
virt = netdev_priv(dev);
dev->netdev_ops = &virt_net_ops;
SET_ETHTOOL_OPS(dev, &virt_ethtool_ops);
dev_net_set(dev, net);
virt->magic = MY_VIRT_DEV_MAGIC;
ret = register_netdev(dev);
if (ret) {
printk("register_netdev failed\n");
free_netdev(dev);
return ret;
}
...
What happens is that somewhere somehow the pointer net_device_ops in
'net_dev' gets corrupted, i.e.
1) create the device the first time (allocated net_dev, init the fields
including net_device_ops,which is
initialized with a static structure containing function pointers), register
the device with the kernel invoking register_netdev() - OK
2) attempt to create the device with the same name again, repeat the above
steps, call register_netdev() which will return negative and we
free_netdev(dev) and return error to the caller.
And between these two events the pointer to net_device_ops has changed,
although nowhere in the code it is done explicitly except the initialization
phase.
The kernel version is 2.6.31.8, platform MIPS. Communication channel between the user space and the kernel is implemented via netlink sockets.
Could anybody suggest what possibly can go wrong?
Appreciate any advices, thanks.
Mark
"The bug is somewhere else. "
The second device should not interact with the existing one. If you register_netdev with an existing name, nevertheless the ndo_init virtual function is called first before the condition is detected and -EEXIST is returned. Maybe your init function does something nasty involving some global variables. (For example, does the code assume there is one device, and stash a global pointer to it during initialization?)
I have a problem with get_user() macro. What I did is as follows:
I run the following program
int main()
{
int a = 20;
printf("address of a: %p", &a);
sleep(200);
return 0;
}
When the program runs, it outputs the address of a, say, 0xbff91914.
Then I pass this address to a module running in Kernel Mode that retrieves the contents at this address (at the time when I did this, I also made sure the process didn't terminate, because I put it to sleep for 200 seconds... ):
The address is firstly sent as a string, and I cast them into pointer type.
int * ptr = (int*)simple_strtol(buffer, NULL,16);
printk("address: %p",ptr); // I use this line to make sure the cast is correct. When running, it outputs bff91914, as expected.
int val = 0;
int res;
res= get_user(val, (int*) ptr);
However, res is always not 0, meaning that get_user returns error. I am wondering what is the problem....
Thank you!!
-- Fangkai
That is probably because you're trying to get value from a different user space. That address you got is from your simple program's address space, while you're probably using another program for passing the value to the module, aren't you?
The call to get_user must be made in the context of the user process.
Since you write "I also made sure the process didn't terminate, because I put it to sleep for 200 seconds..." I have a feeling you are not abiding by that rule. For the call to get_user to be in the context of the user process, you would have had to make a system call from that process and there would not have been a need to sleep the process.
So, you need to have your user process make a system call (an ioctl would be fine) and from that system call make the call to get_user.
In Win32 in order to paste data into the clipboard I have to call GlobalAlloc(), then GlobalLock() to obtain a pointer, then copy data, then call GlobalUnlock() and SetClipboardData().
If the code is in C++ an exception might be thrown between calls to GlobalLock() and GlobalUnlock() and if I don't take care of this GlobalUnlock() will not be called.
It this a problem? What exactly happens if I call GlobalLock() and for whatever reason skip a pairing GlobalUnlock() call?
The Question is not only about if or if not you call GlobalUnlock(). You must call GlobalUnlock() and GlobalFree(). Both must be called in order to release the memory you allocated:
HGLOBAL hdl = NULL;
void *ptr = NULL
try {
hdl = GlobalAlloc();
ptr = GlobalLock(hdl);
// etc...
GlobalUnlock(hdl);
ptr = NULL;
SetClipboardData(..., hdl );
}
catch (...) {
if(ptr)
GlobalUnlock(hdl);
if(hdl)
GlobalFree(hdl);
throw;
}
The leak would be application wide. When you exit a windows application, all allocated private memory is released automatically