#include #include #include #include #include #include #include #include #include #include #include #include #include #include "include/lkm.h" #define DEVICE_NAME "lkm" #define CLASS_NAME "lkmclass" int lkm_init_device_driver(void); static int lkm_init(void); static void lkm_cleanup(void); int lkm_open(struct inode *inode, struct file *filp); int lkm_release(struct inode *inode, struct file *filep); long lkm_ioctl(struct file *file, unsigned int num, long unsigned int param); void lkm_alloc(size_t id, size_t loc, size_t size); pte_t *page_walk_pte(size_t addr); void dpm_test(void); void bpf_dpm_split(size_t size); static struct file_operations lkm_fops = { .owner = THIS_MODULE, .open = lkm_open, .release = lkm_release, .unlocked_ioctl = lkm_ioctl, }; static int lkm_major; static struct class *lkm_class; static struct device *lkm_device; static pgd_t *_init_top_pgt; static void *(*module_alloc)(unsigned long); static int (*set_memory_rox)(unsigned long, int); struct msg_queue; struct msg_queue *(*ipc_obtain_object_check)(struct ipc_ids *ns, int id); unsigned long slub_addr_base; unsigned long __text; unsigned long vmemmap_base; unsigned long vmalloc_base; unsigned long __end; static struct kprobe kp = { .symbol_name = "init_top_pgt" }; static struct kprobe kp1 = { .symbol_name = "module_alloc" }; static struct kprobe kp2 = { .symbol_name = "set_memory_rox" }; static struct kprobe kp3 = { .symbol_name = "ipc_obtain_object_check" }; static struct kprobe kp4 = { .symbol_name = "slub_addr_base" }; static struct kprobe kp5 = { .symbol_name = "_text" }; static struct kprobe kp6 = { .symbol_name = "vmemmap_base" }; static struct kprobe kp7 = { .symbol_name = "vmalloc_base" }; static struct kprobe kp8 = { .symbol_name = "__brk_dmi_alloc" }; #define OBJS_SZ (1ULL << 20) void *objs[OBJS_SZ]; /* * Initialization device driver */ #define V5_15 #define V6_5 #define V6_6 #define V6_8 int lkm_init_device_driver(void) { int ret; printk(KERN_INFO "lkm:init_device_driver: start\n"); ret = register_chrdev(0, DEVICE_NAME, &lkm_fops); if (ret < 0) goto ERROR; lkm_major = ret; #if defined(V6_5) || defined(V6_8) || defined(V6_6) lkm_class = class_create(CLASS_NAME); #else lkm_class = class_create(0, CLASS_NAME); #endif if (IS_ERR(lkm_class)) { ret = PTR_ERR(lkm_class); goto ERROR1; } lkm_device = device_create(lkm_class, 0, MKDEV(lkm_major, 0), 0, DEVICE_NAME); if (IS_ERR(lkm_device)) { ret = PTR_ERR(lkm_device); goto ERROR2; } printk(KERN_INFO "lkm:init_device_driver: done '/dev/%s c %d' 0 created\n", DEVICE_NAME, lkm_major); return 0; ERROR2: printk(KERN_ERR "lkm:init_device_driver: class destroy\n"); class_unregister(lkm_class); class_destroy(lkm_class); ERROR1: printk(KERN_ERR "lkm:init_device_driver: unregister chrdev\n"); unregister_chrdev(lkm_major, CLASS_NAME); ERROR: printk(KERN_ERR "lkm:init_device_driver: fail %d\n", ret); lkm_device = 0; lkm_class = 0; lkm_major = -1; return ret; } /* * Initialization */ static int lkm_init(void) { int ret; printk(KERN_INFO "lkm:init: start\n"); ret = lkm_init_device_driver(); if (ret) goto ERROR; register_kprobe(&kp); _init_top_pgt = (void *)kp.addr; register_kprobe(&kp1); module_alloc = (void *(*)(unsigned long))kp1.addr; register_kprobe(&kp2); set_memory_rox = (int (*)(unsigned long, int))kp2.addr; register_kprobe(&kp3); ipc_obtain_object_check = (struct msg_queue * (*)(struct ipc_ids *, int)) kp3.addr; register_kprobe(&kp4); slub_addr_base = *(unsigned long *)kp4.addr; register_kprobe(&kp5); __text = (unsigned long)kp5.addr; register_kprobe(&kp6); vmemmap_base = *(unsigned long *)kp6.addr; register_kprobe(&kp7); vmalloc_base = *(unsigned long *)kp7.addr; register_kprobe(&kp8); __end = (unsigned long)kp8.addr; printk(KERN_INFO "lkm:init: init_top_pgt %016zx\n", (size_t)_init_top_pgt); printk(KERN_INFO "lkm:init: lkm_class %016zx\n", (size_t)lkm_class); printk(KERN_INFO "lkm:init: lkm_ioctl %016zx\n", (size_t)lkm_ioctl); printk(KERN_INFO "lkm:init: done\n"); return 0; ERROR: printk(KERN_ERR "lkm:init: error\n"); return ret; } /* * Cleanup */ static void lkm_cleanup(void) { printk(KERN_INFO "lkm:cleanup\n"); unregister_kprobe(&kp); unregister_kprobe(&kp1); unregister_kprobe(&kp2); unregister_kprobe(&kp3); unregister_kprobe(&kp4); unregister_kprobe(&kp5); unregister_kprobe(&kp6); unregister_kprobe(&kp7); unregister_kprobe(&kp8); device_destroy(lkm_class, MKDEV(lkm_major, 0)); class_unregister(lkm_class); class_destroy(lkm_class); unregister_chrdev(lkm_major, DEVICE_NAME); } /* * Close "/dev/lkm" */ int lkm_release(struct inode *inode, struct file *filep) { printk(KERN_INFO "lkm:release\n"); module_put(THIS_MODULE); return 0; } EXPORT_SYMBOL(lkm_release); /* * Open "/dev/lkm" */ int lkm_open(struct inode *inode, struct file *filp) { printk(KERN_INFO "lkm:open\n"); try_module_get(THIS_MODULE); return 0; } EXPORT_SYMBOL(lkm_open); #pragma GCC push_options #pragma GCC optimize("O0") void lkm_alloc(size_t id, size_t loc, size_t size) { switch (loc) { case 0: objs[id] = kmalloc(size, GFP_KERNEL); break; case 1: objs[id] = kmalloc(size, GFP_KERNEL); break; case 2: objs[id] = kmalloc(size, GFP_KERNEL); break; case 3: objs[id] = kmalloc(size, GFP_KERNEL); break; case 4: objs[id] = kmalloc(size, GFP_KERNEL); break; case 5: objs[id] = kmalloc(size, GFP_KERNEL); break; case 6: objs[id] = kmalloc(size, GFP_KERNEL); break; case 7: objs[id] = kmalloc(size, GFP_KERNEL); break; case 8: objs[id] = kmalloc(size, GFP_KERNEL); break; case 9: objs[id] = kmalloc(size, GFP_KERNEL); break; case 10: objs[id] = kmalloc(size, GFP_KERNEL); break; case 11: objs[id] = kmalloc(size, GFP_KERNEL); break; case 12: objs[id] = kmalloc(size, GFP_KERNEL); break; case 13: objs[id] = kmalloc(size, GFP_KERNEL); break; case 14: objs[id] = kmalloc(size, GFP_KERNEL); break; case 15: objs[id] = kmalloc(size, GFP_KERNEL); break; case 16: objs[id] = kmalloc(size, GFP_KERNEL); break; case 17: objs[id] = kmalloc(size, GFP_KERNEL); break; case 18: objs[id] = kmalloc(size, GFP_KERNEL); break; case 19: objs[id] = kmalloc(size, GFP_KERNEL); break; case 20: objs[id] = kmalloc(size, GFP_KERNEL); break; case 21: objs[id] = kmalloc(size, GFP_KERNEL); break; case 22: objs[id] = kmalloc(size, GFP_KERNEL); break; case 23: objs[id] = kmalloc(size, GFP_KERNEL); break; case 24: objs[id] = kmalloc(size, GFP_KERNEL); break; case 25: objs[id] = kmalloc(size, GFP_KERNEL); break; case 26: objs[id] = kmalloc(size, GFP_KERNEL); break; case 27: objs[id] = kmalloc(size, GFP_KERNEL); break; case 28: objs[id] = kmalloc(size, GFP_KERNEL); break; case 29: objs[id] = kmalloc(size, GFP_KERNEL); break; case 30: objs[id] = kmalloc(size, GFP_KERNEL); break; case 31: objs[id] = kmalloc(size, GFP_KERNEL); break; default: printk(KERN_ERR "lkm:alloc: invalid loc\n"); break; } } #pragma GCC pop_options pte_t * page_walk_pte(size_t addr) { unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT; pgd_t *pgd; p4d_t *p4d; pud_t *pud; pmd_t *pmd; pte_t *pte; if (above != 0 && above != -1UL) return 0; pgd = pgd_offset_pgd(_init_top_pgt, addr); if (pgd_none(*pgd)) return 0; p4d = p4d_offset(pgd, addr); if (!p4d_present(*p4d)) return 0; pud = pud_offset(p4d, addr); if (!pud_present(*pud)) return 0; if (pud_large(*pud)) return 0; pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) return 0; if (pmd_large(*pmd)) return 0; pte = pte_offset_kernel(pmd, addr); if (pte_none(*pte)) return 0; return pte; } pmd_t * page_walk_pmd(size_t addr) { unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT; pgd_t *pgd; p4d_t *p4d; pud_t *pud; pmd_t *pmd; if (above != 0 && above != -1UL) return 0; pgd = pgd_offset_pgd(_init_top_pgt, addr); if (pgd_none(*pgd)) return 0; p4d = p4d_offset(pgd, addr); if (!p4d_present(*p4d)) return 0; pud = pud_offset(p4d, addr); if (!pud_present(*pud)) return 0; if (pud_large(*pud)) return 0; pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) return 0; return pmd; } bool isLargePage(size_t addr) { unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT; pgd_t *pgd; p4d_t *p4d; pud_t *pud; pmd_t *pmd; pte_t *pte; if (above != 0 && above != -1UL) return 0; pgd = pgd_offset_pgd(_init_top_pgt, addr); if (pgd_none(*pgd)) return 0; p4d = p4d_offset(pgd, addr); if (!p4d_present(*p4d)) return 0; pud = pud_offset(p4d, addr); if (!pud_present(*pud)) return 0; if (pud_large(*pud)) return 0; pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) return 0; if (pmd_large(*pmd)) return 1; pte = pte_offset_kernel(pmd, addr); if (pte_none(*pte)) return 0; return 0; } void dpm_test(void) { int max_large = 10; size_t start = 0; size_t end = 32ULL * 1024ULL * 1024ULL * 1024ULL; printk(KERN_INFO "lkm:dpm_test: physical mapping paddr [%016zx - %016zx] vaddr [%016zx - %016zx]\n", start, end, (size_t)__va(start), (size_t)__va(end)); size_t count = 0; size_t paddr; for (paddr = start; paddr < end; paddr += PMD_SIZE) { size_t vaddr = (size_t)phys_to_virt(paddr); pte_t *pte = page_walk_pte(vaddr); if (pte) { size_t diff = 0; size_t writable = 0; size_t pagecachedisabled = 0; size_t global = 0; size_t _paddr; for (_paddr = paddr; _paddr < paddr + PMD_SIZE; _paddr += PAGE_SIZE) { size_t vaddr = (size_t)phys_to_virt(_paddr); pte_t *pte = page_walk_pte(vaddr); if (!pte) { // printk(KERN_DEBUG "lkm:dpm_test: // vaddr %016zx pte 0\n", vaddr); continue; } diff += !!((pte->pte & 0xfff) ^ 0x163); writable += !!(pte->pte & _PAGE_RW); pagecachedisabled += !!(pte->pte & _PAGE_PCD); global += !!(pte->pte & _PAGE_GLOBAL); // printk(KERN_DEBUG "lkm:dpm_test: vaddr %016zx // pte %016zx\n", vaddr, pte->pte); count++; } printk(KERN_DEBUG "lkm:dpm_test: vaddr %016zx with [%4zd diff] [%4zd wr] [%4zd pcd] [%4zd g]\n", vaddr, diff, writable, pagecachedisabled, global); } else if (max_large > 0 && isLargePage(vaddr)) { max_large--; printk(KERN_DEBUG "lkm:dpm_test: 2MB: vaddr %016zx\n", vaddr); } } printk(KERN_INFO "lkm:dpm_test: count/max %5zu/%zu\n", count, (end - start) / PAGE_SIZE); pmd_t *_text_pmd = page_walk_pmd(__text); pmd_t *_end_pmd = page_walk_pmd(__end); printk(KERN_INFO "lkm:dpm_text: _text %016zx\n", __text); printk(KERN_INFO "lkm:dpm_text: _end %016zx\n", __end); size_t _text_pa = (size_t)__va(_text_pmd->pmd & 0xfffffffff000); size_t _end_pa = (size_t)__va(_end_pmd->pmd & 0xfffffffff000); printk(KERN_INFO "lkm:dpm_text: _text_pa %016zx %s\n", _text_pa, isLargePage(_text_pa) ? "2MB page" : "4k page"); printk(KERN_INFO "lkm:dpm_text: _end_pa %016zx %s\n", _end_pa, isLargePage(_end_pa) ? "2MB page" : "4k page"); } void bpf_dpm_split(size_t size) { void *obj; pte_t *pte; size_t vaddr; obj = module_alloc(size); set_memory_rox((unsigned long)obj, size >> 12); pte = page_walk_pte((unsigned long)obj); vaddr = (size_t)phys_to_virt(pte->pte & 0xffffffffff000); printk(KERN_INFO "obj %016zx vaddr %016zx\n", (unsigned long)obj, vaddr); } struct msg_queue { struct kern_ipc_perm q_perm; time64_t q_stime; time64_t q_rtime; time64_t q_ctime; unsigned long q_cbytes; unsigned long q_qnum; unsigned long q_qbytes; struct pid *q_lspid; struct pid *q_lrpid; struct list_head q_messages; struct list_head q_receivers; struct list_head q_senders; }; #define IPC_MSG_IDS 1 #define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) static unsigned long msg_copy_to_user(size_t msqid, size_t type) { struct msg_queue *msq; struct msg_msg *msg; struct msg_msg *found = 0; struct ipc_namespace *ns; struct ipc_ids *ids; rcu_read_lock(); // printk(KERN_DEBUG "lkm:msg_copy_to_user: msqid %ld\n", msqid); ns = current->nsproxy->ipc_ns; // printk(KERN_DEBUG "lkm:msg_copy_to_user: ns %016zd\n", (unsigned // long)ns); ids = &msg_ids(ns); // printk(KERN_DEBUG "lkm:msg_copy_to_user: ids %016zd\n", (unsigned // long)ids); msq = ipc_obtain_object_check(ids, msqid); // printk(KERN_DEBUG "lkm:msg_copy_to_user: msq %016zd\n", (unsigned // long)msq); if (IS_ERR(msq)) goto RETURN; list_for_each_entry(msg, &msq->q_messages, m_list) { if (msg->m_type == type) { found = msg; // printk(KERN_DEBUG "lkm:msg_copy_to_user: found // %016zx\n", (unsigned long)found); break; } } RETURN: rcu_read_unlock(); return (unsigned long)found; } #ifndef SLAB_BASE_ADDR size_t SLAB_BASE_ADDR = 0; #endif #ifndef SLAB_END_ADDR size_t SLAB_END_ADDR = 0; #endif static int bad_address(void *p) { unsigned long dummy; return get_kernel_nofault(dummy, (unsigned long *)p); } noinline void pagetable_walk(msg_t *msg) { size_t address = msg->ptw.uaddr; pgd_t *base = __va(read_cr3_pa()); pgd_t *pgd = base + pgd_index(address); p4d_t *p4d; pud_t *pud; pmd_t *pmd; pte_t *pte; if (bad_address(pgd)) goto out; // pr_info("PGD %lx ", pgd_val(*pgd)); msg->ptw.pgde = pgd_val(*pgd); if (!pgd_present(*pgd)) goto out; p4d = p4d_offset(pgd, address); if (bad_address(p4d)) goto out; // pr_cont("P4D %lx ", p4d_val(*p4d)); msg->ptw.p4de = p4d_val(*p4d); if (!p4d_present(*p4d) || p4d_large(*p4d)) goto out; pud = pud_offset(p4d, address); if (bad_address(pud)) goto out; // pr_cont("PUD %lx ", pud_val(*pud)); msg->ptw.pude = pud_val(*pud); if (!pud_present(*pud) || pud_large(*pud)) goto out; pmd = pmd_offset(pud, address); if (bad_address(pmd)) goto out; // pr_cont("PMD %lx ", pmd_val(*pmd)); msg->ptw.pmde = pmd_val(*pmd); if (!pmd_present(*pmd) || pmd_large(*pmd)) goto out; pte = pte_offset_kernel(pmd, address); if (bad_address(pte)) goto out; // pr_cont("PTE %lx", pte_val(*pte)); msg->ptw.pte = pte_val(*pte); out: // pr_cont("\n"); return; } /* * ioctl code */ long lkm_ioctl(struct file *_, unsigned int num, long unsigned int param) { int ret; msg_t msg; size_t *uaddr = 0; size_t tmp = -1; // void *dummy; size_t id; struct files_struct *files; struct file *file; struct fdtable *fdt; struct pipe_inode_info *pipe; unsigned int tail; unsigned int head; unsigned int mask; // printk(KERN_INFO "lkm:ioctl: start num 0x%08x param 0x%016lx\n", num, // param); ret = copy_from_user((msg_t *)&msg, (msg_t *)param, sizeof(msg_t)); if (ret < 0) { printk(KERN_ERR "lkm:ioctl: copy_from_user failed\n"); ret = -1; goto RETURN; } switch (num) { case LKM_ALLOC: if (msg.al.id >= OBJS_SZ) { printk(KERN_ERR "lkm:ioctl: index %zd too large\n", msg.al.id); ret = -1; goto RETURN; } if (msg.al.size > 4096) { printk(KERN_ERR "lkm:ioctl: size %zd too big\n", msg.al.size); ret = -1; goto RETURN; } objs[msg.al.id] = kmalloc(msg.al.size, GFP_KERNEL); break; case LKM_FREE: if (msg.fr.id >= OBJS_SZ) { printk(KERN_ERR "lkm:ioctl: %zd too large\n", msg.fr.id); ret = -1; goto RETURN; } kfree(objs[msg.fr.id]); break; case LKM_WRITE: // printk(KERN_INFO "lkm:ioctl: arbitrary write\n"); *(size_t *)msg.wr.kaddr = msg.wr.value; break; case LKM_READ: // printk(KERN_INFO "lkm:ioctl: arbitrary read\n"); tmp = *(size_t *)msg.rd.kaddr; uaddr = (size_t *)msg.rd.uaddr; goto COPY_TMP_TO_USER; case LKM_DPM_TEST: dpm_test(); break; case LKM_ACCESS_PRIMITIVE: *(volatile size_t *)msg.ap.addr; break; case LKM_BPF_DPM_SPLIT: // printk(KERN_INFO "lkm:ioctl: bpf dpm split\n"); bpf_dpm_split(msg.dpms.size); break; case LKM_MSG_MSG_LEAK: tmp = msg_copy_to_user(msg.mrd.msqid, msg.mrd.mtype); uaddr = (size_t *)msg.mrd.uaddr; // printk(KERN_DEBUG "lkm:ioctl: msg_msg leak %16zx\n", tmp); goto COPY_TMP_TO_USER; case LKM_DPM_LEAK: tmp = (size_t)__va(0); uaddr = (size_t *)msg.drd.uaddr; // printk(KERN_DEBUG "lkm:ioctl: dpm leak %16zx\n", tmp); goto COPY_TMP_TO_USER; case LKM_VIRTUAL_BASE_LEAK: tmp = (size_t)slub_addr_base; uaddr = (size_t *)msg.drd.uaddr; printk(KERN_INFO "lkm:ioctl: slub_addr_base %16zx within [%016zx %016zx]\n", tmp, SLAB_BASE_ADDR, SLAB_END_ADDR); goto COPY_TMP_TO_USER; case LKM_SEQ_FILE_LEAK: id = msg.frd.fd; files = current->files; spin_lock(&files->file_lock); fdt = files_fdtable(files); file = fdt->fd[id]; spin_unlock(&files->file_lock); tmp = (size_t)file->private_data; // printk(KERN_INFO "lkm:ioctl: seq_file %016zx\n", tmp); uaddr = (size_t *)msg.frd.uaddr; goto COPY_TMP_TO_USER; case LKM_CRED_LEAK: tmp = (size_t)current->real_cred; uaddr = (size_t *)msg.drd.uaddr; // printk(KERN_INFO "lkm:ioctl: current->cred %016zx\n", tmp); goto COPY_TMP_TO_USER; case LKM_FILE_LEAK: id = msg.frd.fd; files = current->files; spin_lock(&files->file_lock); fdt = files_fdtable(files); tmp = (size_t)fdt->fd[id]; spin_unlock(&files->file_lock); // printk(KERN_INFO "lkm:ioctl: file %016zx\n", tmp); uaddr = (size_t *)msg.frd.uaddr; goto COPY_TMP_TO_USER; case LKM_STACK_LEAK: tmp = (size_t)current->stack; uaddr = (size_t *)msg.drd.uaddr; // printk(KERN_DEBUG "lkm:ioctl: current->stack %16zx with // current %16zx within [%016zx %016zx]\n", tmp, (size_t)&dummy, // VMALLOC_START, VMALLOC_END); goto COPY_TMP_TO_USER; case LKM_CODE_LEAK: tmp = (size_t)__text; uaddr = (size_t *)msg.drd.uaddr; goto COPY_TMP_TO_USER; case LKM_VMEMMAP_LEAK: tmp = (size_t)vmemmap_base; uaddr = (size_t *)msg.drd.uaddr; goto COPY_TMP_TO_USER; case LKM_VMALLOC_BASE_LEAK: tmp = (size_t)vmalloc_base; uaddr = (size_t *)msg.drd.uaddr; goto COPY_TMP_TO_USER; case LKM_ARB_FREE: printk(KERN_DEBUG "lkm:ioctl: free %016zx\n", msg.af.kaddr); kfree((void *)msg.af.kaddr); break; case LKM_PIPE_BUFFER_LEAK: id = msg.pbrd.fd; files = current->files; spin_lock(&files->file_lock); fdt = files_fdtable(files); file = fdt->fd[id]; spin_unlock(&files->file_lock); pipe = file->private_data; tail = pipe->tail; head = pipe->head; mask = pipe->ring_size - 1; if (msg.pbrd.rdend) tmp = (size_t)&pipe->bufs[tail & mask]; else tmp = (size_t)&pipe->bufs[(head - 1) & mask]; // printk(KERN_DEBUG "lkm:ioctl: pipe_buffer %016zx\n", tmp); uaddr = (size_t *)msg.pbrd.uaddr; goto COPY_TMP_TO_USER; case LKM_PAGETABLE_WALK: printk(KERN_DEBUG "lkm:ioctl: page table walk\n"); msg.ptw.pgde = 0; msg.ptw.p4de = 0; msg.ptw.pude = 0; msg.ptw.pmde = 0; msg.ptw.pte = 0; pagetable_walk(&msg); ret = copy_to_user((msg_t *)param, (msg_t *)&msg, sizeof(msg_t)); if (ret < 0) { printk(KERN_ALERT "lkm:ioctl: copy_to_user failed\n"); ret = -1; goto RETURN; } break; case LKM_IS_4KB: ret = !isLargePage(msg.ap.addr); // printk(KERN_DEBUG "lkm:ioctl: %016zx is %s page\n", // msg.ap.addr, ret == 1 ? "4kB" : "2MB"); return ret; default: printk(KERN_ERR "lkm:ioctl: no valid num\n"); ret = -1; goto RETURN; } ret = 0; goto DONE; COPY_TMP_TO_USER: if (uaddr == 0) { // && "forgot to set uaddr"); printk(KERN_ERR "lkm:ioctl: uaddr == 0\n"); // ret = -1; goto RETURN; } if (tmp == -1) { // && "forgot to set tmp"); printk(KERN_ERR "lkm:ioctl: tmp == -1\n"); // ret = -1; goto RETURN; } // printk(KERN_INFO "lkm:ioctl: copy 0x%016zx to mem[0x%016zx]\n", tmp, // (size_t)uaddr); ret = copy_to_user(uaddr, &tmp, sizeof(size_t)); if (ret < 0) { printk(KERN_ERR "lkm:ioctl: copy_to_user failed\n"); ret = -1; goto RETURN; } ret = 0; DONE: // printk(KERN_INFO "lkm:ioctl: done\n"); RETURN: return ret; } EXPORT_SYMBOL(lkm_ioctl); module_init(lkm_init); module_exit(lkm_cleanup); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lukas Maar"); MODULE_DESCRIPTION("LKM"); MODULE_VERSION("0.1");