1

When the LKM is loaded the code is placed in a virtual page space (vmalloc). I'm trying to relocate a function so that it can be called regardless of processor/memory context (ie from an FIQ). I believe this means compiling the function with PIC and then copying to a kmalloc'd space.

I have created a function in reloc_test.c:

unsigned int test_call(unsigned int arg)
{
    if(arg > 256) {
        return 2;
    } else if(arg < 256) {
        return 1;
    } else {
        return 0;
    }
}

This is compiled with gcc -S -fPIC -mfloat-abi=softfp -o reloc_test_asm.S reloc_test.c to create as assembly file (only way I could think of to make the function PIC.

  1   .arch armv6
  2   .eabi_attribute 27, 3
  3   .eabi_attribute 28, 1
  4   .fpu vfp
  5   .eabi_attribute 20, 1
  6   .eabi_attribute 21, 1
  7   .eabi_attribute 23, 3
  8   .eabi_attribute 24, 1
  9   .eabi_attribute 25, 1
 10   .eabi_attribute 26, 2
 11   .eabi_attribute 30, 6
 12   .eabi_attribute 34, 1
 13   .eabi_attribute 18, 4
 14   .file "reloc_test.c"
 15   .text
 16   .align  2
 17   .global test_call
 18   .type test_call, %function
 19 test_call:
 20   @ args = 0, pretend = 0, frame = 8
 21   @ frame_needed = 1, uses_anonymous_args = 0
 22   @ link register save eliminated.
 23   str fp, [sp, #-4]!
 24   add fp, sp, #0
 25   sub sp, sp, #12
 26   str r0, [fp, #-8]
 27   ldr r3, [fp, #-8]
 28   cmp r3, #256
 29   bls .L2
 30   mov r3, #2
 31   b .L3
 32 .L2:
 33   ldr r3, [fp, #-8]
 34   cmp r3, #255
 35   bhi .L4
 36   mov r3, #1
 37   b .L3
 38 .L4:
 39   mov r3, #0
 40 .L3:
 41   mov r0, r3
 42   sub sp, fp, #0
 43   @ sp needed
 44   ldr fp, [sp], #4
 45   bx  lr
 46   .size test_call, .-test_call
 47   .ident  "GCC: (Raspbian 4.8.4-1) 4.8.4"
 48   .section  .note.GNU-stack,"",%progbits

I get the size from objdump:

 $ objdump -t reloc_test_asm.o

reloc_test_asm.o:     file format elf32-littlearm

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 reloc_test.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .note.GNU-stack        00000000 .note.GNU-stack
00000000 l    d  .comment       00000000 .comment
00000000 l    d  .ARM.attributes        00000000 .ARM.attributes
00000000 g     F .text  0000004c test_call

I then have an init for the lkm as follows:

unsigned int test_call(unsigned int arg);
static int __init pulse_logger_init(void)
{
    unsigned int (*test)(unsigned int arg);
    static void* buffer;
    printk(KERN_INFO "%s\n", __func__);

    printk(KERN_INFO "test_call retuns: %d %d %d\n", test_call(0), test_call(256), test_call(512));

    buffer = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
    buffer = (void*) ALIGN((uint32_t)buffer, 4);
    printk(KERN_INFO "\t  buffer(%#10X)\n", (unsigned int)buffer);
    printk(KERN_INFO "\t  test_call(%#10X)\n", (unsigned int)test_call);
    memcpy(buffer, test_call, 0x4c);
    test = (unsigned int (*)(unsigned int)) buffer;
    printk(KERN_INFO "test_call retuns: %d %d %d\n", test_call(0), test(256), test(512));
    __free_pages(buffer, THREAD_SIZE_ORDER);

    return 0;
}

This appears to die horribly in a kernel panic when in insmod:

Sep 24 20:15:04 zTorque kernel: [13329.307348] pulse_logger_init
Sep 24 20:15:04 zTorque kernel: [13329.307389] test_call retuns: 1 0 2
Sep 24 20:15:04 zTorque kernel: [13329.307437]    buffer(0XDB08C000)
Sep 24 20:15:04 zTorque kernel: [13329.307451]    test_call(0XBF004570)
Sep 24 20:15:04 zTorque kernel: [13329.307482] Unable to handle kernel paging request at virtual address db08c000
Sep 24 20:15:04 zTorque kernel: [13329.320232] pgd = db0d4000
Sep 24 20:15:04 zTorque kernel: [13329.328257] [db08c000] *pgd=1b00041e(bad)
Sep 24 20:15:04 zTorque kernel: [13329.337622] Internal error: Oops: 8000000d [#1] PREEMPT ARM
Sep 24 20:15:04 zTorque kernel: [13329.348528] Modules linked in: pulse_logger_comp(O+) cfg80211 rfkill snd_bcm2835 snd_pcm snd_seq snd_seq_device snd_timer                                                                                  snd uio_pdrv_genirq uio
Sep 24 20:15:04 zTorque kernel: [13329.367327] CPU: 0 PID: 2452 Comm: insmod Tainted: G           O    4.1.5+ #809
Sep 24 20:15:04 zTorque kernel: [13329.380234] Hardware name: BCM2708
Sep 24 20:15:04 zTorque kernel: [13329.389186] task: dd352940 ti: dc83a000 task.ti: dc83a000
Sep 24 20:15:04 zTorque kernel: [13329.400168] PC is at 0xdb08c000
Sep 24 20:15:04 zTorque kernel: [13329.408893] LR is at pulse_logger_init+0xe0/0x130 [pulse_logger_comp]
Sep 24 20:15:04 zTorque kernel: [13329.420960] pc : [<db08c000>]    lr : [<bf0070e0>]    psr: 80000013
Sep 24 20:15:04 zTorque kernel: [13329.420960] sp : dc83bdb0  ip : 00000007  fp : dc83bdd4
Sep 24 20:15:04 zTorque kernel: [13329.443668] r10: c008a32c  r9 : 00000000  r8 : 00000001
Sep 24 20:15:04 zTorque kernel: [13329.454552] r7 : bf004658  r6 : bf004b00  r5 : bf004570  r4 : db08c000
Sep 24 20:15:04 zTorque kernel: [13329.466776] r3 : 00000001  r2 : e3530c01  r1 : e51b3008  r0 : 00000100
Sep 24 20:15:04 zTorque kernel: [13329.478944] Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Sep 24 20:15:04 zTorque kernel: [13329.491711] Control: 00c5387d  Table: 1b0d4008  DAC: 00000015
Sep 24 20:15:04 zTorque kernel: [13329.503052] Process insmod (pid: 2452, stack limit = 0xdc83a188)
Sep 24 20:15:04 zTorque kernel: [13329.514633] Stack: (0xdc83bdb0 to 0xdc83c000)
Sep 24 20:15:04 zTorque kernel: [13329.524527] bda0:                                     00000000 c081f770 c081f770 dc9544c0
Sep 24 20:15:04 zTorque kernel: [13329.538348] bdc0: dc954380 bf007000 dc83be54 dc83bdd8 c00095e0 bf00700c 00000000 00000000
Sep 24 20:15:04 zTorque kernel: [13329.552226] bde0: dcadea80 ddb5e9e0 00000000 00000000 ddb5e9e0 ddb5e9e0 dd780b34 dd780b3c
Sep 24 20:15:04 zTorque kernel: [13329.566155] be00: dc83be3c dc83be10 c00ee6c4 c00c5454 dc83be34 dc83be20 00000002 c008b824
Sep 24 20:15:04 zTorque kernel: [13329.580152] be20: df95c000 c0120a24 00000003 bf004980 00000001 db07fea0 dc954380 00000001
Sep 24 20:15:04 zTorque kernel: [13329.594180] be40: dc9543a4 c008a32c dc83be7c dc83be58 c008b860 c000955c bf0049c8 dc954380
Sep 24 20:15:04 zTorque kernel: [13329.608239] be60: dc83be7c dc83bf40 00000001 bf0049c8 dc83bf3c dc83be80 c008d374 c008b800
Sep 24 20:15:04 zTorque kernel: [13329.622269] be80: bf00498c 00007fff c008a2d0 c0313d20 00000013 00000001 df95c000 bf00498c
Sep 24 20:15:04 zTorque kernel: [13329.636272] bea0: 00000000 df95e544 bf004fe8 bf00498c b6f22948 bf004b28 bf004af0 bf004980
Sep 24 20:15:04 zTorque kernel: [13329.650319] bec0: b6f3a000 dc83a000 00002db8 00002594 00000000 bf004604 00000001 00000000
Sep 24 20:15:04 zTorque kernel: [13329.664357] bee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
Sep 24 20:15:04 zTorque kernel: [13329.678366] bf00: 00000000 00000000 00000000 00000000 df95c000 00002594 00000000 b6f3c594
Sep 24 20:15:04 zTorque kernel: [13329.692345] bf20: df95e594 dc83a000 b6f22948 00000000 dc83bfa4 dc83bf40 c008db08 c008bb04
Sep 24 20:15:04 zTorque kernel: [13329.706345] bf40: df95c000 00002594 df95df7c df95cf87 df95d904 00000b28 00000ff8 00000000
Sep 24 20:15:04 zTorque kernel: [13329.720309] bf60: 00000000 00000000 00000025 00000026 0000001b 0000001f 00000016 00000000
Sep 24 20:15:04 zTorque kernel: [13329.734221] bf80: 00000000 00000000 80b420a8 00000080 c000f908 dc83a000 00000000 dc83bfa8
Sep 24 20:15:04 zTorque kernel: [13329.748141] bfa0: c000f700 c008da38 00000000 00000000 b6f3a000 00002594 b6f22948 b6f3a000
Sep 24 20:15:04 zTorque kernel: [13329.762118] bfc0: 00000000 00000000 80b420a8 00000080 80b41fe0 00002594 b6f22948 00000000
Sep 24 20:15:04 zTorque kernel: [13329.776113] bfe0: b6e757d0 be9426a8 b6f19fb4 b6e757e0 60000010 b6f3a000 1dbf4821 1dbf4c21
Sep 24 20:15:04 zTorque kernel: [13329.790218] [<bf0070e0>] (pulse_logger_init [pulse_logger_comp]) from [<c00095e0>] (do_one_initcall+0x90/0x1ec)
Sep 24 20:15:04 zTorque kernel: [13329.806321] [<c00095e0>] (do_one_initcall) from [<c008b860>] (do_init_module+0x6c/0x1c8)
Sep 24 20:15:04 zTorque kernel: [13329.820432] [<c008b860>] (do_init_module) from [<c008d374>] (load_module+0x187c/0x1f34)
Sep 24 20:15:04 zTorque kernel: [13329.834463] [<c008d374>] (load_module) from [<c008db08>] (SyS_init_module+0xdc/0x138)
Sep 24 20:15:04 zTorque kernel: [13329.848369] [<c008db08>] (SyS_init_module) from [<c000f700>] (ret_fast_syscall+0x0/0x54)
Sep 24 20:15:04 zTorque kernel: [13329.862562] Code: 1dbee811 1dbeec11 1dbf4821 1dbf4c21 (e52db004)
Sep 24 20:15:04 zTorque kernel: [13329.931225] ---[ end trace 213b9514d512fd18 ]---

Is there something protecting the kernel from executing from this space?

devanl
  • 1,232
  • 1
  • 10
  • 20
  • You might have to mark the page as executable. But I doubt what your doing is necessary. Any interrupt handler has to be available during any context. If the function isn't already available in any context by default there has to be a better way of doing this. – Ross Ridge Sep 24 '15 at 21:39
  • Where did you get the `memcpy(..., 30)` from? I count at least 19 instructions, and even thumb[2]-instructions are two bytes each. – EOF Sep 24 '15 at 21:57
  • EOF - you're right, added objdump and updated code, its still looks like the dump says it is dying at the beginning buffer(0XDB08C000) Ross - I'll look in to the executible thing, from my little understanding of linux the module code space is vmalloc'd meaning that the virtual page address cannot be accessed from an FIQ? – devanl Sep 25 '15 at 01:46
  • Ross - Were you referring to the NX bit? I don't believe this is supported in the BCM2708 (ARM11). – devanl Sep 25 '15 at 02:05
  • Why wouldn't it be accessible from an FIQ? I don't know about ARM, but on x86 part of the address space, the top 1G, is reserved for the kernel and is mapped into all processes. vmalloc allocates memory within a subset of this region. I'm referring to what ARM calls the XN bit which was introduced in ARMv6, and is supported by the ARM11 core: http://infocenter.arm.com/help/topic/com.arm.doc.ddi0360f/CACHFICI.html – Ross Ridge Sep 25 '15 at 02:15

1 Answers1

0

I'm not sure if this is a complete answer but the contents aren't fit for a comment.

Ross was able to point me in the right direction and I was able to find a way of allocating memory with execution permissions : How to allocate an executable page in a Linux kernel module?

This creates the following code:

printk(KERN_INFO "test_call returns: %d %d %d\n", test_call(0), test_call(256), test_call(512));

buffer = __vmalloc(0x60, GFP_KERNEL, PAGE_KERNEL_EXEC);
if(buffer != NULL){
    buffer = (void*) ALIGN((uint32_t)buffer, 4) + 4;
    printk(KERN_INFO "\t  buffer(%#10X)\n", (unsigned int)buffer);
    printk(KERN_INFO "\t  test_call(%#10X)\n", (unsigned int)test_call);
    memcpy(buffer, test_call, 0x4c);
    printk(KERN_INFO "\t  test_call copied to buffer\n");
    test = (unsigned int (*)(unsigned int)) buffer;
    printk(KERN_INFO "\t  test pointer assigned (%#10X)\n", test);
    printk(KERN_INFO "test returns: %d %d %d\n", test(0), test(256), test(512));
    vfree(buffer);
}

This creates the following output:

Sep 25 14:17:26  kernel: [  109.102562] pulse_logger_init
Sep 25 14:17:26  kernel: [  109.102626] test_call returns: 1 0 2
Sep 25 14:17:26  kernel: [  109.102701]    buffer(0XDF9D4004)
Sep 25 14:17:26  kernel: [  109.102739]    test_call(0XBF004384)
Sep 25 14:17:26  kernel: [  109.102753]    test_call copied to buffer
Sep 25 14:17:26  kernel: [  109.102766]    test pointer assigned (0XDF9D4004)
Sep 25 14:17:26  kernel: [  109.102779] test returns: 1 0 2
Sep 25 14:17:26  kernel: [  109.102793] ------------[ cut here ]------------
Sep 25 14:17:26  kernel: [  109.102836] WARNING: CPU: 0 PID: 2803 at mm/vmalloc.c:1459 __vunmap+0xe4/0xfc()
Sep 25 14:17:26  kernel: [  109.102871] Trying to vfree() bad address (df9d4004)
Sep 25 14:17:26  kernel: [  109.102884] Modules linked in: pulse_logger_comp(O+) cfg80211 rfkill snd_bcm2835 snd_pcm snd_seq snd_seq_device snd_timer snd uio_pdrv_genirq uio
Sep 25 14:17:26  kernel: [  109.102979] CPU: 0 PID: 2803 Comm: insmod Tainted: G           O    4.1.5+ #809
Sep 25 14:17:26  kernel: [  109.102994] Hardware name: BCM2708
Sep 25 14:17:26  kernel: [  109.103063] [<c0016d88>] (unwind_backtrace) from [<c0013b50>] (show_stack+0x20/0x24)
Sep 25 14:17:26  kernel: [  109.103123] [<c0013b50>] (show_stack) from [<c058690c>] (dump_stack+0x20/0x28)
Sep 25 14:17:26  kernel: [  109.103170] [<c058690c>] (dump_stack) from [<c0024178>] (warn_slowpath_common+0x8c/0xc4)
Sep 25 14:17:26  kernel: [  109.103204] [<c0024178>] (warn_slowpath_common) from [<c00241f0>] (warn_slowpath_fmt+0x40/0x48)
Sep 25 14:17:26  kernel: [  109.103261] [<c00241f0>] (warn_slowpath_fmt) from [<c0120a64>] (__vunmap+0xe4/0xfc)
Sep 25 14:17:26  kernel: [  109.103294] [<c0120a64>] (__vunmap) from [<c0120b1c>] (vfree+0x50/0x90)
Sep 25 14:17:26  kernel: [  109.103339] [<c0120b1c>] (vfree) from [<bf0060fc>] (pulse_logger_init+0xfc/0x12c [pulse_logger_comp])
Sep 25 14:17:26  kernel: [  109.103415] [<bf0060fc>] (pulse_logger_init [pulse_logger_comp]) from [<c00095e0>] (do_one_initcall+0x90/0x1ec)
Sep 25 14:17:26  kernel: [  109.103465] [<c00095e0>] (do_one_initcall) from [<c008b860>] (do_init_module+0x6c/0x1c8)
Sep 25 14:17:26  kernel: [  109.103524] [<c008b860>] (do_init_module) from [<c008d374>] (load_module+0x187c/0x1f34)
Sep 25 14:17:26  kernel: [  109.103559] [<c008d374>] (load_module) from [<c008db08>] (SyS_init_module+0xdc/0x138)
Sep 25 14:17:26  kernel: [  109.103614] [<c008db08>] (SyS_init_module) from [<c000f700>] (ret_fast_syscall+0x0/0x54)
Sep 25 14:17:26  kernel: [  109.103636] ---[ end trace c37a24bf1309572d ]---

The good news is that this looks like its allocated for the Kernel-mode Virtual address space (and not page-mapped). I'm basing this on a belief that the User/Kernel split occurs at 0xC0000000. That would suggest that kernel vmalloc's for the LKM reside in the User-mode page-mapped virtual address range?

Not sure why the vfree call is claiming a bad address.

Community
  • 1
  • 1
devanl
  • 1,232
  • 1
  • 10
  • 20