this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Solve semaphore leaks and various locking isssues in LKM, LKM now prints TID into dmesg for clarity

+210 -28
+16 -2
src/lkm/darling_task.c
··· 25 25 #include <linux/printk.h> 26 26 #include <linux/rwlock.h> 27 27 #include "servers/thread_act.h" 28 + #include "primitives/semaphore.h" 28 29 29 30 static rwlock_t my_lock; 30 31 static struct rb_root all_tasks = RB_ROOT; 31 32 static unsigned int task_count = 0; 33 + 34 + extern ipc_namespace_t kernel_namespace; 32 35 33 36 struct task_entry 34 37 { ··· 214 217 write_unlock(&task->threads_lock); 215 218 } 216 219 217 - void darling_task_free_threads(mach_task_t* task) 220 + void darling_task_destruct(mach_task_t* task) 218 221 { 219 222 struct thread_entry *entry, *n; 223 + struct list_head *pos, *nn; 220 224 221 - debug_msg("darling_task_free_threads(%p)\n", task); 225 + debug_msg("darling_task_destruct(%p)\n", task); 222 226 write_lock(&task->threads_lock); 223 227 224 228 rbtree_postorder_for_each_entry_safe(entry, n, &task->threads, node) 225 229 { 226 230 ipc_port_put(entry->thread_port); 227 231 kfree(entry); 232 + } 233 + 234 + // Destroy all semaphores 235 + list_for_each_safe(pos, nn, &task->semaphores) 236 + { 237 + struct mach_semaphore* sem = (struct mach_semaphore*) 238 + container_of(pos, struct mach_semaphore, head); 239 + 240 + debug_msg("!!! destroy unreleased semaphore %d\n", sem->kernel_right); 241 + ipc_space_right_put(&kernel_namespace, sem->kernel_right); 228 242 } 229 243 230 244 task->threads.rb_node = NULL;
+5 -2
src/lkm/darling_task.h
··· 1 1 /* 2 2 * Darling Mach Linux Kernel Module 3 - * Copyright (C) 2015 Lubos Dolezel 3 + * Copyright (C) 2015-2017 Lubos Dolezel 4 4 * 5 5 * This program is free software; you can redistribute it and/or 6 6 * modify it under the terms of the GNU General Public License ··· 26 26 #include <linux/rbtree.h> 27 27 #include <linux/spinlock.h> 28 28 #include <linux/hashtable.h> 29 + #include <linux/list.h> 29 30 30 31 // Initialized in ipc_server.c 31 32 struct mach_task ··· 44 45 45 46 spinlock_t cv_wq_lock; 46 47 DECLARE_HASHTABLE(cv_wq, 8); 48 + 49 + struct list_head semaphores; 47 50 }; 48 51 49 52 typedef struct mach_task mach_task_t; ··· 68 71 darling_mach_port_t* darling_task_lookup_thread(mach_task_t* task, 69 72 pid_t thread); 70 73 71 - void darling_task_free_threads(mach_task_t* task); 74 + void darling_task_destruct(mach_task_t* task); 72 75 73 76 static inline bool 74 77 task_is_64bit(void)
+3 -1
src/lkm/debug.h
··· 22 22 #undef __unused 23 23 #include <linux/printk.h> 24 24 #include <linux/slab.h> 25 + #include <linux/sched.h> 26 + #include <linux/kernel.h> 25 27 26 28 #ifdef DARLING_DEBUG 27 - # define debug_msg(fmt, ...) printk(KERN_DEBUG "Darling Mach: " fmt, ##__VA_ARGS__) 29 + # define debug_msg(fmt, ...) printk(KERN_DEBUG "Darling Mach: <%d> " fmt, current->pid, ##__VA_ARGS__) 28 30 #else 29 31 # define debug_msg(fmt, ...) 30 32 #endif
+77 -4
src/lkm/ipc_msg.c
··· 26 26 #include <linux/uaccess.h> 27 27 #include <linux/jiffies.h> 28 28 #include <linux/atomic.h> 29 + #include <linux/flex_array.h> 29 30 #include "darling_task.h" 30 31 31 32 extern ipc_namespace_t kernel_namespace; ··· 34 35 static 35 36 mach_msg_return_t ipc_msg_complex_copyin(ipc_namespace_t* space, 36 37 struct ipc_kmsg* kmsg); 38 + static 39 + mach_msg_return_t ipc_msg_copyout_complex(ipc_namespace_t* space, 40 + struct ipc_kmsg* kmsg, 41 + struct flex_array* ool_ports); 42 + static 43 + bool walk_complex_msg(mach_msg_base_t* base, 44 + bool (*cb)(mach_msg_type_descriptor_t*,int,void*), 45 + void* private); 37 46 38 47 int ipc_msg_count(void) 39 48 { ··· 313 322 } 314 323 315 324 static 325 + bool _copy_to_move(mach_msg_type_descriptor_t* desc, int index, void* priv) 326 + { 327 + if (desc->type == MACH_MSG_PORT_DESCRIPTOR) 328 + { 329 + mach_msg_port_descriptor_t* pdesc = (mach_msg_port_descriptor_t*) desc; 330 + if (pdesc->disposition == MACH_MSG_TYPE_COPY_SEND) 331 + pdesc->disposition = MACH_MSG_TYPE_MOVE_SEND; 332 + } 333 + return true; 334 + } 335 + 336 + static 316 337 mach_msg_return_t ipc_msg_invoke_server(struct ipc_kmsg* kmsg, 317 338 int options, mach_msg_timeout_t timeout) 318 339 { ··· 321 342 mach_msg_header_t* reply_msg; 322 343 mach_msg_return_t ret; 323 344 mach_port_t tmp; 345 + struct flex_array* ool = NULL; 346 + 347 + if (kmsg->msg->msgh_bits & MACH_MSGH_BITS_CIRCULAR) 348 + { 349 + debug_msg("Prevented a circular message delivery\n"); 350 + ret = KERN_FAILURE; 351 + goto err; 352 + } 324 353 325 354 subsystem = kmsg->target->port->server_port.subsystem; 326 355 ··· 365 394 */ 366 395 // Insert reply port from kmsg into kernel_namespace 367 396 // and put the right name into reply_msg 397 + ipc_space_lock(&kernel_namespace); 368 398 if (kmsg->reply != NULL) 369 399 { 370 400 ipc_space_right_insert(&kernel_namespace, kmsg->reply, ··· 376 406 reply_msg->msgh_remote_port = 0; 377 407 } 378 408 409 + if (MACH_MSGH_BITS_IS_COMPLEX(kmsg->msg->msgh_bits)) 410 + { 411 + // Handle OOL rights, memory regions etc. 412 + ool = flex_array_alloc(sizeof(mach_port_t), 1024, GFP_KERNEL); 413 + ipc_msg_copyout_complex(&kernel_namespace, kmsg, ool); 414 + } 415 + ipc_space_unlock(&kernel_namespace); 416 + 379 417 reply_msg->msgh_id = kmsg->msg->msgh_id + 100; 380 418 // reply_msg->msgh_voucher_port = kmsg->msg->msgh_voucher_port; 381 419 ··· 395 433 kmsg->msg->msgh_remote_port = kmsg->msg->msgh_local_port; 396 434 kmsg->msg->msgh_local_port = tmp; 397 435 436 + if (ool != NULL) 437 + { 438 + // Destroy all ports sent to us 439 + int i = 0; 440 + mach_port_t* pport; 441 + 442 + while ((pport = (mach_port_t*) flex_array_get(ool, i++)) != NULL) 443 + { 444 + ipc_space_right_put(&kernel_namespace, *pport); 445 + } 446 + 447 + flex_array_free(ool); 448 + } 449 + 398 450 debug_msg("ipc_msg_deliver(): reply size: %d, ret = 0x%x\n", 399 451 reply_msg->msgh_size, 400 452 ((mig_reply_error_t*)reply_msg)->RetCode); 401 453 402 454 // The right used to call us is now consumed. 403 455 ipc_right_put_unlock(kmsg->target); 456 + 457 + /* 458 + if (MACH_MSGH_BITS_IS_COMPLEX(reply_msg->msgh_bits)) 459 + { 460 + // MIG uses "copy_send" for output arguments, 461 + // but we want to get rid of the copy in kernel namespace. 462 + // Change the disposition to "move_send". 463 + walk_complex_msg((mach_msg_base_t*) reply_msg, _copy_to_move, NULL); 464 + } 465 + */ 404 466 405 - // TODO: prevent circular loops 467 + // prevent circular loops 468 + reply_msg->msgh_bits |= MACH_MSGH_BITS_CIRCULAR; 406 469 ret = ipc_msg_send(&kernel_namespace, reply_msg, timeout, options); 407 470 408 471 if (ret != MACH_MSG_SUCCESS) ··· 799 862 ipc_namespace_t* space; 800 863 struct ipc_kmsg* kmsg; 801 864 mach_msg_return_t ret; 865 + struct flex_array* ool_ports; 866 + unsigned int ool_ports_count; 802 867 }; 803 868 804 869 static ··· 824 889 ret = ipc_space_right_insert(args->space, 825 890 args->kmsg->complex_items[index].port, 826 891 &port_desc->name); 892 + 893 + if (ret == KERN_SUCCESS && args->ool_ports) 894 + { 895 + flex_array_put(args->ool_ports, args->ool_ports_count++, 896 + &port_desc->name, GFP_KERNEL); 897 + } 827 898 } 828 899 else 829 900 { ··· 840 911 841 912 static 842 913 mach_msg_return_t ipc_msg_copyout_complex(ipc_namespace_t* space, 843 - struct ipc_kmsg* kmsg) 914 + struct ipc_kmsg* kmsg, struct flex_array* ool_ports) 844 915 { 845 916 struct ipc_msg_copyout_complex_args args = { 846 917 .space = space, 847 918 .kmsg = kmsg, 848 - .ret = KERN_SUCCESS 919 + .ret = KERN_SUCCESS, 920 + .ool_ports_count = 0, 921 + .ool_ports = ool_ports, 849 922 }; 850 923 if (kmsg->complex_items == NULL) 851 924 return KERN_SUCCESS; ··· 1037 1110 { 1038 1111 // Handle OOL rights, memory regions etc. 1039 1112 ipc_msg_copyout_complex(&task->namespace, 1040 - &delivery->kmsg); 1113 + &delivery->kmsg, NULL); 1041 1114 } 1042 1115 1043 1116 // Copy over the message to recipient's buffer.
+6 -1
src/lkm/ipc_right.c
··· 85 85 { 86 86 if (PORT_IS_VALID(port)) 87 87 { 88 - debug_msg("\tChanging num of rights\n"); 89 88 if (right->type == MACH_PORT_RIGHT_SEND) 89 + { 90 90 port->num_srights--; 91 + debug_msg("\tChanging num of rights (s) -> %d\n", port->num_srights); 92 + } 91 93 else if (right->type == MACH_PORT_RIGHT_SEND_ONCE) 94 + { 92 95 port->num_sorights--; 96 + debug_msg("\tChanging num of rights (so) -> %d\n", port->num_sorights); 97 + } 93 98 94 99 list_del(&right->reflist); 95 100
+3 -1
src/lkm/ipc_server.c
··· 32 32 33 33 task = (mach_task_t*) kport->private_data; 34 34 35 - darling_task_free_threads(task); 35 + darling_task_destruct(task); 36 36 37 37 /* Deallocate the port right space. Deletes all references. */ 38 38 ipc_space_put(&task->namespace); ··· 63 63 port->server_port.subsystem = &task_subsystem; 64 64 port->server_port.private_data = task; 65 65 port->server_port.cb_free = task_free; 66 + 67 + INIT_LIST_HEAD(&task->semaphores); 66 68 67 69 return task; 68 70 }
+28
src/lkm/ipc_space.c
··· 228 228 right = ipc_space_lookup(space, name); 229 229 if (right == NULL) 230 230 { 231 + debug_msg("ipc_space_right_put(): righ %d not found\n", name); 231 232 mutex_unlock(&space->mutex); 232 233 return KERN_INVALID_NAME; 233 234 } ··· 239 240 240 241 if (PORT_IS_VALID(port)) 241 242 mutex_unlock(&port->mutex); 243 + 244 + mutex_unlock(&space->mutex); 245 + return KERN_SUCCESS; 246 + } 247 + 248 + mach_msg_return_t ipc_space_right_put_unlocked(ipc_namespace_t* space, mach_port_name_t name) 249 + { 250 + struct mach_port_right* right; 251 + darling_mach_port_t* port; 252 + 253 + if (!name) 254 + return KERN_SUCCESS; 255 + 256 + mutex_lock(&space->mutex); 257 + 258 + right = ipc_space_lookup_unlocked(space, name); 259 + if (right == NULL) 260 + { 261 + debug_msg("ipc_space_right_put_unlocked(): righ %d not found\n", name); 262 + mutex_unlock(&space->mutex); 263 + return KERN_INVALID_NAME; 264 + } 265 + 266 + port = right->port; 267 + 268 + ipc_space_name_put(space, name); 269 + ipc_right_put(right); 242 270 243 271 mutex_unlock(&space->mutex); 244 272 return KERN_SUCCESS;
+7
src/lkm/ipc_space.h
··· 83 83 * 84 84 * Automatically deallocates port on last reference 85 85 * or marks port as dead if name refers to a receive right. 86 + * 87 + * Expects the space to be unlocked. 86 88 */ 87 89 mach_msg_return_t ipc_space_right_put(ipc_namespace_t* space, mach_port_name_t name); 90 + 91 + /* 92 + * Same as above, but caller has already locked the port 93 + */ 94 + mach_msg_return_t ipc_space_right_put_unlocked(ipc_namespace_t* space, mach_port_name_t name); 88 95 89 96 /* 90 97 * Deallocate a port name. Does not delete the associated right.
+2 -1
src/lkm/mig/taskServer.c
··· 10 10 #define __MIG_check__Request__task_subsystem__ 1 11 11 12 12 #include "taskServer.h" 13 + #include <linux/printk.h> 13 14 14 15 #ifndef mig_internal 15 16 #define mig_internal static __inline__ ··· 2106 2107 #endif /* defined(__MIG_check__Request__semaphore_destroy_t__defined) */ 2107 2108 2108 2109 OutP->RetCode = semaphore_destroy(In0P->Head.msgh_request_port, In0P->semaphore.name); 2109 - 2110 + printk(KERN_DEBUG "!!! sem des retcode 0x%x\n", OutP->RetCode); 2110 2111 OutP->NDR = NDR_record; 2111 2112 2112 2113
+7 -10
src/lkm/primitives/semaphore.c
··· 22 22 #include "../ipc_server.h" 23 23 #include "../ipc_port.h" 24 24 #include "../debug.h" 25 - #include <linux/semaphore.h> 25 + #include "../darling_task.h" 26 26 #include <linux/slab.h> 27 27 #include <linux/atomic.h> 28 28 #include <linux/version.h> 29 29 #include <linux/sched.h> 30 - #include <linux/list.h> 31 30 32 31 static atomic_t sem_count = ATOMIC_INIT(0); 33 32 34 33 static void 35 34 mach_semaphore_destroy(server_port_t* port); 36 35 37 - struct mach_semaphore 38 - { 39 - bool active; 40 - struct semaphore sem; 41 - rwlock_t rwlock; 42 - }; 43 - 44 36 void 45 - mach_semaphore_setup(darling_mach_port_t* port, int value) 37 + mach_semaphore_setup(mach_task_t* task, darling_mach_port_t* port, int value, mach_port_t kernel_right) 46 38 { 47 39 struct mach_semaphore* ms; 48 40 ··· 56 48 port->server_port.private_data = ms; 57 49 58 50 ms->active = true; 51 + ms->kernel_right = kernel_right; 52 + 59 53 sema_init(&ms->sem, value); 60 54 rwlock_init(&ms->rwlock); 61 55 62 56 atomic_inc(&sem_count); 57 + 58 + list_add(&ms->head, &task->semaphores); 63 59 } 64 60 65 61 static void ··· 69 65 70 66 ms = (struct mach_semaphore*) port->private_data; 71 67 ms->active = false; 68 + list_del(&ms->head); 72 69 73 70 while (!write_trylock(&ms->rwlock)) 74 71 up(&ms->sem);
+14 -1
src/lkm/primitives/semaphore.h
··· 20 20 #ifndef PRIMITIVES_SEMAPHORE_H 21 21 #define PRIMITIVES_SEMAPHORE_H 22 22 #include "../ipc_types.h" 23 + #include <linux/semaphore.h> 24 + #include <linux/list.h> 25 + #include <linux/rwlock.h> 26 + 27 + struct mach_semaphore 28 + { 29 + bool active; 30 + struct semaphore sem; 31 + rwlock_t rwlock; 32 + mach_port_t kernel_right; 33 + struct list_head head; 34 + }; 35 + 23 36 24 37 void 25 - mach_semaphore_setup(darling_mach_port_t* port, int value); 38 + mach_semaphore_setup(mach_task_t* task, darling_mach_port_t* port, int value, mach_port_t kernel_right); 26 39 27 40 // All of the following unlock the port on their own 28 41
+38 -3
src/lkm/servers/task.c
··· 25 25 #include "../darling_task.h" 26 26 #include "../ipc_port.h" 27 27 #include "../ipc_space.h" 28 + #include "../ipc_server.h" 28 29 #include "../primitives/semaphore.h" 29 30 #include "task.h" 30 31 #include "stub.h" ··· 271 272 if (ret != KERN_SUCCESS) 272 273 return ret; 273 274 274 - mach_semaphore_setup(port, value); 275 + mach_semaphore_setup(task_self, port, value, *semaphore); 275 276 276 277 return KERN_SUCCESS; 277 278 } ··· 282 283 semaphore_t semaphore 283 284 ) 284 285 { 285 - return _kernelrpc_mach_port_destroy(darling_task_get_current(), 286 - task, semaphore); 286 + darling_mach_port_t* sema_port; 287 + mach_task_t* task_self; 288 + struct mach_semaphore* sema_obj; 289 + darling_mach_port_right_t* right; 290 + mach_port_t kernel_right; 291 + 292 + task_self = darling_task_get_current(); 293 + ipc_space_lock(&kernel_namespace); 294 + 295 + right = ipc_space_lookup(&kernel_namespace, semaphore); 296 + 297 + ipc_space_unlock(&kernel_namespace); 298 + 299 + if (!PORT_IS_VALID(right->port)) 300 + { 301 + return KERN_SUCCESS; 302 + } 303 + 304 + sema_port = right->port; 305 + if (!sema_port || !sema_port->is_server_port 306 + || sema_port->server_port.port_type != PORT_TYPE_SEMAPHORE) 307 + { 308 + if (sema_port) 309 + ipc_port_unlock(sema_port); 310 + return KERN_FAILURE; 311 + } 312 + 313 + sema_obj = (struct mach_semaphore*) sema_port->server_port.private_data; 314 + kernel_right = sema_obj->kernel_right; 315 + 316 + ipc_port_put(sema_port); 317 + 318 + // Kill the (now dead) right 319 + ipc_space_right_put(&kernel_namespace, sema_obj->kernel_right); 320 + 321 + return KERN_SUCCESS; 287 322 } 288 323 289 324 kern_return_t task_policy_set
+4 -2
src/lkm/traps.c
··· 295 295 mach_port_name_t name; 296 296 kern_return_t ret; 297 297 298 - ipc_port_lock(task->task_self); 298 + // No reason to lock task_self, this task is not going away 299 + // while we are running. (Avoid an ABBA lock problem). 300 + // ipc_port_lock(task->task_self); 299 301 300 302 ret = ipc_space_make_send(&task->namespace, task->task_self, false, &name); 301 303 302 - ipc_port_unlock(task->task_self); 304 + // ipc_port_unlock(task->task_self); 303 305 304 306 if (ret == KERN_SUCCESS) 305 307 return name;