diff options
| -rw-r--r-- | arch/x86/syscalls/syscall_32.tbl | 1 | ||||
| -rw-r--r-- | arch/x86/syscalls/syscall_64.tbl | 1 | ||||
| -rw-r--r-- | include/linux/syscalls.h | 1 | ||||
| -rw-r--r-- | kernel/module.c | 367 | ||||
| -rw-r--r-- | kernel/sys_ni.c | 1 |
5 files changed, 223 insertions, 148 deletions
diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index a47103fbc692..83b3838417ed 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl | |||
| @@ -356,3 +356,4 @@ | |||
| 356 | 347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv | 356 | 347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv |
| 357 | 348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev | 357 | 348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev |
| 358 | 349 i386 kcmp sys_kcmp | 358 | 349 i386 kcmp sys_kcmp |
| 359 | 350 i386 finit_module sys_finit_module | ||
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl index a582bfed95bb..7c58c84b7bc8 100644 --- a/arch/x86/syscalls/syscall_64.tbl +++ b/arch/x86/syscalls/syscall_64.tbl | |||
| @@ -319,6 +319,7 @@ | |||
| 319 | 310 64 process_vm_readv sys_process_vm_readv | 319 | 310 64 process_vm_readv sys_process_vm_readv |
| 320 | 311 64 process_vm_writev sys_process_vm_writev | 320 | 311 64 process_vm_writev sys_process_vm_writev |
| 321 | 312 common kcmp sys_kcmp | 321 | 312 common kcmp sys_kcmp |
| 322 | 313 common finit_module sys_finit_module | ||
| 322 | 323 | ||
| 323 | # | 324 | # |
| 324 | # x32-specific system call numbers start at 512 to avoid cache impact | 325 | # x32-specific system call numbers start at 512 to avoid cache impact |
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 727f0cd73921..32bc035bcd68 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h | |||
| @@ -868,4 +868,5 @@ asmlinkage long sys_process_vm_writev(pid_t pid, | |||
| 868 | 868 | ||
| 869 | asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, | 869 | asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, |
| 870 | unsigned long idx1, unsigned long idx2); | 870 | unsigned long idx1, unsigned long idx2); |
| 871 | asmlinkage long sys_finit_module(int fd, const char __user *uargs); | ||
| 871 | #endif | 872 | #endif |
diff --git a/kernel/module.c b/kernel/module.c index 6e48c3a43599..6d2c4e4ca1f5 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/ftrace_event.h> | 21 | #include <linux/ftrace_event.h> |
| 22 | #include <linux/init.h> | 22 | #include <linux/init.h> |
| 23 | #include <linux/kallsyms.h> | 23 | #include <linux/kallsyms.h> |
| 24 | #include <linux/file.h> | ||
| 24 | #include <linux/fs.h> | 25 | #include <linux/fs.h> |
| 25 | #include <linux/sysfs.h> | 26 | #include <linux/sysfs.h> |
| 26 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
| @@ -2425,18 +2426,17 @@ static inline void kmemleak_load_module(const struct module *mod, | |||
| 2425 | #endif | 2426 | #endif |
| 2426 | 2427 | ||
| 2427 | #ifdef CONFIG_MODULE_SIG | 2428 | #ifdef CONFIG_MODULE_SIG |
| 2428 | static int module_sig_check(struct load_info *info, | 2429 | static int module_sig_check(struct load_info *info) |
| 2429 | const void *mod, unsigned long *_len) | ||
| 2430 | { | 2430 | { |
| 2431 | int err = -ENOKEY; | 2431 | int err = -ENOKEY; |
| 2432 | unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; | 2432 | const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; |
| 2433 | unsigned long len = *_len; | 2433 | const void *mod = info->hdr; |
| 2434 | 2434 | ||
| 2435 | if (len > markerlen && | 2435 | if (info->len > markerlen && |
| 2436 | memcmp(mod + len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { | 2436 | memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { |
| 2437 | /* We truncate the module to discard the signature */ | 2437 | /* We truncate the module to discard the signature */ |
| 2438 | *_len -= markerlen; | 2438 | info->len -= markerlen; |
| 2439 | err = mod_verify_sig(mod, _len); | 2439 | err = mod_verify_sig(mod, &info->len); |
| 2440 | } | 2440 | } |
| 2441 | 2441 | ||
| 2442 | if (!err) { | 2442 | if (!err) { |
| @@ -2454,59 +2454,97 @@ static int module_sig_check(struct load_info *info, | |||
| 2454 | return err; | 2454 | return err; |
| 2455 | } | 2455 | } |
| 2456 | #else /* !CONFIG_MODULE_SIG */ | 2456 | #else /* !CONFIG_MODULE_SIG */ |
| 2457 | static int module_sig_check(struct load_info *info, | 2457 | static int module_sig_check(struct load_info *info) |
| 2458 | void *mod, unsigned long *len) | ||
| 2459 | { | 2458 | { |
| 2460 | return 0; | 2459 | return 0; |
| 2461 | } | 2460 | } |
| 2462 | #endif /* !CONFIG_MODULE_SIG */ | 2461 | #endif /* !CONFIG_MODULE_SIG */ |
| 2463 | 2462 | ||
| 2464 | /* Sets info->hdr, info->len and info->sig_ok. */ | 2463 | /* Sanity checks against invalid binaries, wrong arch, weird elf version. */ |
| 2465 | static int copy_and_check(struct load_info *info, | 2464 | static int elf_header_check(struct load_info *info) |
| 2466 | const void __user *umod, unsigned long len, | ||
| 2467 | const char __user *uargs) | ||
| 2468 | { | 2465 | { |
| 2469 | int err; | 2466 | if (info->len < sizeof(*(info->hdr))) |
| 2470 | Elf_Ehdr *hdr; | 2467 | return -ENOEXEC; |
| 2468 | |||
| 2469 | if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0 | ||
| 2470 | || info->hdr->e_type != ET_REL | ||
| 2471 | || !elf_check_arch(info->hdr) | ||
| 2472 | || info->hdr->e_shentsize != sizeof(Elf_Shdr)) | ||
| 2473 | return -ENOEXEC; | ||
| 2474 | |||
| 2475 | if (info->hdr->e_shoff >= info->len | ||
| 2476 | || (info->hdr->e_shnum * sizeof(Elf_Shdr) > | ||
| 2477 | info->len - info->hdr->e_shoff)) | ||
| 2478 | return -ENOEXEC; | ||
| 2471 | 2479 | ||
| 2472 | if (len < sizeof(*hdr)) | 2480 | return 0; |
| 2481 | } | ||
| 2482 | |||
| 2483 | /* Sets info->hdr and info->len. */ | ||
| 2484 | static int copy_module_from_user(const void __user *umod, unsigned long len, | ||
| 2485 | struct load_info *info) | ||
| 2486 | { | ||
| 2487 | info->len = len; | ||
| 2488 | if (info->len < sizeof(*(info->hdr))) | ||
| 2473 | return -ENOEXEC; | 2489 | return -ENOEXEC; |
| 2474 | 2490 | ||
| 2475 | /* Suck in entire file: we'll want most of it. */ | 2491 | /* Suck in entire file: we'll want most of it. */ |
| 2476 | if ((hdr = vmalloc(len)) == NULL) | 2492 | info->hdr = vmalloc(info->len); |
| 2493 | if (!info->hdr) | ||
| 2477 | return -ENOMEM; | 2494 | return -ENOMEM; |
| 2478 | 2495 | ||
| 2479 | if (copy_from_user(hdr, umod, len) != 0) { | 2496 | if (copy_from_user(info->hdr, umod, info->len) != 0) { |
| 2480 | err = -EFAULT; | 2497 | vfree(info->hdr); |
| 2481 | goto free_hdr; | 2498 | return -EFAULT; |
| 2482 | } | 2499 | } |
| 2483 | 2500 | ||
| 2484 | err = module_sig_check(info, hdr, &len); | 2501 | return 0; |
| 2502 | } | ||
| 2503 | |||
| 2504 | /* Sets info->hdr and info->len. */ | ||
| 2505 | static int copy_module_from_fd(int fd, struct load_info *info) | ||
| 2506 | { | ||
| 2507 | struct file *file; | ||
| 2508 | int err; | ||
| 2509 | struct kstat stat; | ||
| 2510 | loff_t pos; | ||
| 2511 | ssize_t bytes = 0; | ||
| 2512 | |||
| 2513 | file = fget(fd); | ||
| 2514 | if (!file) | ||
| 2515 | return -ENOEXEC; | ||
| 2516 | |||
| 2517 | err = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat); | ||
| 2485 | if (err) | 2518 | if (err) |
| 2486 | goto free_hdr; | 2519 | goto out; |
| 2487 | 2520 | ||
| 2488 | /* Sanity checks against insmoding binaries or wrong arch, | 2521 | if (stat.size > INT_MAX) { |
| 2489 | weird elf version */ | 2522 | err = -EFBIG; |
| 2490 | if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0 | 2523 | goto out; |
| 2491 | || hdr->e_type != ET_REL | ||
| 2492 | || !elf_check_arch(hdr) | ||
| 2493 | || hdr->e_shentsize != sizeof(Elf_Shdr)) { | ||
| 2494 | err = -ENOEXEC; | ||
| 2495 | goto free_hdr; | ||
| 2496 | } | 2524 | } |
| 2497 | 2525 | info->hdr = vmalloc(stat.size); | |
| 2498 | if (hdr->e_shoff >= len || | 2526 | if (!info->hdr) { |
| 2499 | hdr->e_shnum * sizeof(Elf_Shdr) > len - hdr->e_shoff) { | 2527 | err = -ENOMEM; |
| 2500 | err = -ENOEXEC; | 2528 | goto out; |
| 2501 | goto free_hdr; | ||
| 2502 | } | 2529 | } |
| 2503 | 2530 | ||
| 2504 | info->hdr = hdr; | 2531 | pos = 0; |
| 2505 | info->len = len; | 2532 | while (pos < stat.size) { |
| 2506 | return 0; | 2533 | bytes = kernel_read(file, pos, (char *)(info->hdr) + pos, |
| 2534 | stat.size - pos); | ||
| 2535 | if (bytes < 0) { | ||
| 2536 | vfree(info->hdr); | ||
| 2537 | err = bytes; | ||
| 2538 | goto out; | ||
| 2539 | } | ||
| 2540 | if (bytes == 0) | ||
| 2541 | break; | ||
| 2542 | pos += bytes; | ||
| 2543 | } | ||
| 2544 | info->len = pos; | ||
| 2507 | 2545 | ||
| 2508 | free_hdr: | ||
