This is the section of disassembled code in question. It’s from a Linux kernel module compiled for 4.4.16 on ARMv7.

  ; Registers used:
  ;  - r3 :  unsigned long argp

0000005c         mov        r1, sp
00000060         bic        r2, r1, #0x1fc0
00000064         bic        r2, r2, #0x3f
00000068         ldr        r4, [r2, #0x8]
0000006c         adds       r1, r3, #0x1
00000070         sbcslo     r1, r1, r4
00000074         movlo      r4, #0x0
00000078         cmp        r4, #0x0
0000007c         bne        loc_dc

The stack at this point looks like this:

00  <- SP
04  [padding]
07  u8 arg_kernel
08  pushed[r4]
0c  pushed[r5]
10  pushed[r6]
14  pushed[lr]
18  <Previous SP>

Here’s how I decoded this assembly into pseudo-C:

r4 = *(SP & 0xe000 + 8);
r1 = argp + 1;

if (r1 overflowed) {
    r1 -= r4;
    r4 = 0;

if (r4 == 0) {
  /* jump */

If I got this right, I don’t really understand the purpose of this code. If I made a mistake, I really don’t understand it. Can anyone offer any insight into the purpose of these operations?


Your translation is wrong. The two BIC instructions clear the 13 low bits of the stack pointer (1FC0|3F = 1FFF). In kernel mode, this produces a pointer to the thread_info structure for the current thread.

The ldr then reads the field at offset 8 in it which seems to be addr_limit and r3+1 apparently should not exceed it.

Combined, the code matches this helper from uaccess.h:

#define __range_ok(addr, size) ({ \
    unsigned long flag, roksum; \
    __chk_user_ptr(addr);   \
    __asm__("adds %1, %2, %3; sbcccs %1, %1, %0; movcc %0, #0" \
        : "=&r" (flag), "=&r" (roksum) \
        : "r" (addr), "Ir" (size), "0" (current_thread_info()->addr_limit) \
        : "cc"); \
    flag; })

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.