0

Below code can hook lsm successfully on Redhat8 server, but it will cause crash on Redhat9 server. I have try to find any resource about hlist_add_head_rcu or hook way change on linux 5.1x but cannot find any useful workaround. Could you help check about why that the same way can work on linux 4.x but cannot work on linux 5.1x? If you know the reason, could you help provide some workaround or suggestions. Thanks for your help.

#include <linux/module.h>
#include <linux/kallsyms.h>
#include <linux/version.h>
#include <linux/security.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
#include <linux/lsm_hooks.h>
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
    .symbol_name = "kallsyms_lookup_name"
};
#endif

#define SECURITY_HOOK_ADDR_NAME "security_hook_heads"

static unsigned long find_symbol_addr(const char *sym){
    const char *cpsMethod = "cris test: find_symbol_addr";
    unsigned long addr;
    #ifdef KPROBE_LOOKUP
    typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
    kallsyms_lookup_name_t kallsyms_lookup_name;
    register_kprobe(&kp);
    kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
    unregister_kprobe(&kp);
    #endif
    
    addr = kallsyms_lookup_name(sym);
    if (addr == 0) {
        pr_err("%s: unable to find addr\n", cpsMethod);
        return -EINVAL;
    }
    pr_info("%s: address is %lx\n", cpsMethod, addr);
    return addr;
}

struct security_hook_list cris_hooks[1] = {
};

static int hook_execve_test(struct file *file, int mask)
{
     pr_info("cris test: hook_execve_test\n");
     return 0;

}
static struct security_hook_heads *cris_lsm_hook = NULL;

bool hook_lsm(void){
    const char *cpsMethod = "cris test: hook_lsm";
    int count = 0;
    int i = 0;
    unsigned long addr1;
    
    addr1 = find_symbol_addr(SECURITY_HOOK_ADDR_NAME);
    if (addr1 == 0)
    {
        pr_err("%s: [Fatal] Lookup address for security hook heads failed. Can't enable execve Hook\n", cpsMethod);
        return false;
    }
    
    cris_lsm_hook = (struct security_hook_heads*)addr1;
    cris_hooks[0].head = &(cris_lsm_hook->file_permission);
    cris_hooks[0].hook.file_permission = hook_execve_test;  
    count = ARRAY_SIZE(cris_hooks);
    for (i = 0; i < count; i++){
        
    #if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
    list_add_rcu(&cris_hooks[i].list, cris_hooks[i].head);
    #else
    hlist_add_head_rcu(&cris_hooks[i].list, cris_hooks[i].head);
    #endif
    }
    pr_info("%s: finish hook_lsm.\n", cpsMethod);
 
    return true;
}

void unhook_lsm(void){
    const char *cpsMethod = "cris test: unhook_lsm";
    int count = 0;
    int i = 0;
    count = ARRAY_SIZE(cris_hooks);
    for (i = 0; i < count; i++){
        #if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0)
        list_del_rcu(&cris_hooks[i].list);
        #else
        hlist_del_rcu(&cris_hooks[i].list);
        #endif
    }
    pr_info("%s: Unregister hook module\n", cpsMethod);
}
static int __init prsyms_init(void)
{
    hook_lsm();
    return 0;
}

static void __exit prsyms_exit(void)
{
    unhook_lsm();
}

module_init(prsyms_init);
module_exit(prsyms_exit);

MODULE_LICENSE("GPL");

The version of test servers are: Redhat8: 4.18.0-147.el8.x86_64 Redhat9: 5.14.0-70.13.1.el9_0.x86_64

And below is dump log from Redhat9 crash dump:

[ 2644.871335] cris test: find_symbol_addr: address is ffffffffb3215c60
[ 2644.871354] BUG: unable to handle page fault for address: ffffffffb3215ea0
[ 2644.871361] #PF: supervisor write access in kernel mode
[ 2644.871363] #PF: error_code(0x0003) - permissions violation
[ 2644.871365] PGD b5a15067 P4D b5a15067 PUD b5a16063 PMD 80000000b54000e1
[ 2644.871377] Oops: 0003 [#1] PREEMPT SMP PTI
[ 2644.871387] CPU: 0 PID: 2437 Comm: insmod Kdump: loaded Tainted: G S         OE    --------- ---  5.14.0-70.13.1.el9_0.x86_64 #1
[ 2644.871394] Hardware name: VMware, Inc. VMware7,1/440BX Desktop Reference Platform, BIOS VMW71.00V.13989454.B64.1906190538 06/19/2019
[ 2644.871396] RIP: 0010:hook_lsm.cold+0x47/0x95 [testcris]
[ 2644.871416] Code: 02 00 00 48 8d 93 40 02 00 00 48 c7 05 8f 23 00 00 83 70 72 c0 48 89 15 80 23 00 00 48 89 05 69 23 00 00 48 89 15 6a 23 00 00 <48> c7 83 40 02 00 00 40 94 72 c0 48 85 c0 74 08 48 c7 40 08 40 94
[ 2644.871418] RSP: 0018:ffffb5c00260fde0 EFLAGS: 00010246
[ 2644.871421] RAX: ffffffffb3216cf8 RBX: ffffffffb3215c60 RCX: 0000000000000000
[ 2644.871423] RDX: ffffffffb3215ea0 RSI: ffff917efbc17cc0 RDI: ffff917efbc17cc0
[ 2644.871424] RBP: ffffffffc072c000 R08: 0000000000000000 R09: ffffb5c00260fc28
[ 2644.871425] R10: ffffb5c00260fc20 R11: ffffffffb3be8228 R12: ffff917dc12226f0
[ 2644.871427] R13: ffffb5c00260fe88 R14: 0000000000000003 R15: 0000000000000000
[ 2644.871428] FS:  00007f8005409740(0000) GS:ffff917efbc00000(0000) knlGS:0000000000000000
[ 2644.871444] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2644.871447] CR2: ffffffffb3215ea0 CR3: 000000003f536003 CR4: 00000000001706f0
[ 2644.871467] Call Trace:
[ 2644.871473]  prsyms_init+0xa/0x1000 [testcris]
[ 2644.871477]  do_one_initcall+0x44/0x200
[ 2644.871500]  ? load_module+0xab8/0xb80
[ 2644.871503]  ? kmem_cache_alloc_trace+0x45/0x420
[ 2644.871523]  do_init_module+0x5c/0x270
[ 2644.871536]  __do_sys_finit_module+0xae/0x110
[ 2644.871544]  do_syscall_64+0x3b/0x90
[ 2644.871592]  entry_SYSCALL_64_after_hwframe+0x44/0xae
T.Cris
  • 53
  • 6
  • 1
    I'm afraid this is not the appropriate way to create a LSM. You cannot load LSMs at runtime. The fact that you are using workarounds like `kallsyms_lookup_name` should already tell you that. Finding the hook list and manually editing it is a bad idea, and most likely it won't work because the list is read-only. You should create a proper LSM to be compiled into the kernel, and use `security_add_hooks()` in the `__init` of the module to add the hooks you want. See for example how [`security/commoncap.c`](https://elixir.bootlin.com/linux/v5.14/source/security/commoncap.c#L1467) does it. – Marco Bonelli Jun 21 '22 at 13:23
  • See: [How can I implement my own hook function with LSM?](https://stackoverflow.com/questions/10428212/how-can-i-implement-my-own-hook-function-with-lsm) – Marco Bonelli Jun 21 '22 at 13:24
  • Hi Marco, thanks for your answers. After your suggestions, I tried to use security_add_hooks() in the __init of the module to add the hooks. But I encountered another issue: WARNING: "security_add_hooks" [/root/test/newLSM.ko] undefined! Do you have any suggestions about this? – T.Cris Jun 22 '22 at 03:30
  • @T.Cris: Again, you should compile LSM into the **kernel**. You cannot build a separate kernel **module** which has LSM functionality. – Tsyvarev Jun 22 '22 at 07:16
  • @T.Cris that is expected. Read my comment more carefully. You *cannot* load a LSM after the kernel starts. You need to compile the module *into the kernel itself*. `security_add_hooks()` is marked `__init` and will in-fact be removed from the kernel when it is done initializing all LSMs at boot. – Marco Bonelli Jun 22 '22 at 11:51
  • See also the kernel documentation [1](https://www.kernel.org/doc/html/latest/admin-guide/LSM/index.html) and [2](https://www.kernel.org/doc/html/latest/security/lsm.html). In particular: *"The name “module” is a bit of a misnomer since these extensions are not actually loadable kernel modules. Instead, they are selectable at build-time via CONFIG_DEFAULT_SECURITY and can be overridden at boot-time via the "security=..." kernel command line argument, in the case where multiple LSMs were built into a given kernel"*. – Marco Bonelli Jun 22 '22 at 11:54
  • @MarcoBonelli [TPE](https://github.com/cormander/tpe-lkm) is a loadable kernel module which is able to add LSM hooks. – Melab Dec 31 '22 at 13:06

0 Answers0