[Arm] Debugging patch for stack corruption

No matter how carefully you examine the code, it's common for people to believe that the code they wrote has no issues. This belief can make it hard to spot code that causes stack corruption. In such cases, adding appropriate debugging code to the function can help identify the problem by logging the behavior.

Stack Debugging Code for 32-bit Armv7-A Architecture

First, let's look at debugging code for the 32-bit Armv7-A architecture.

01 #define GET_SP_REGISTER_DEBUG() \
02	debug_sp_register()
03	
04 unsigned long sp_value;
05 unsigned long lr_value;
06
07 static inline void debug_sp_register(void)
08 {
09	asm volatile ("mov %0, sp\n" \
10		     "mov %1, r14"\ 
11		      :"=r" (sp_value), "=r" (lr_value));		
12					
13	printf("sp: 0x%x, x30: 0x%x \n", sp_value, lr_value);				
14
15	printf("[0x%lx]: 0x%lx \n", (unsigned long)sp_value, *((unsigned long*)(sp_value + 0x0)) );
16	printf("[0x%lx]: 0x%lx \n", (unsigned long)(sp_value + 0x4), *((unsigned long*)(sp_value + 0x4)) );
17	printf("[0x%lx]: 0x%lx \n", (unsigned long)(sp_value + 0x8), *((unsigned int*)(sp_value + 0x8)) )
18 }        

The debugging code above prints the stack address and the value of the link register. The key routine is implemented in the debug_sp_register() function. Let's look at lines 09-11:

09	asm volatile ("mov %0, sp\n" \
10		     "mov %1, r14"\ 
11		      :"=r" (sp_value), "=r" (lr_value));        

.Lines 09-11 store the stack pointer register (sp) and the link register (r14) in the global variables sp_value and lr_value. Line 13 then prints the values of the stack pointer and link register.

Next, let's look at lines 15-17:

15	printf("[0x%lx]: 0x%lx \n", (unsigned long)sp_value, *((unsigned long*)(sp_value + 0x0)) );
16	printf("[0x%lx]: 0x%lx \n", (unsigned long)(sp_value + 0x4), *((unsigned long*)(sp_value + 0x4)) );
17	printf("[0x%lx]: 0x%lx \n", (unsigned long)(sp_value + 0x8), *((unsigned int*)(sp_value + 0x8)) )        

These lines print the data stored in the stack. Running this code will display the data stored in the stack.

Now, let's look at the GET_SP_REGISTER_DEBUG() macro on lines 01-02:

01 #define GET_SP_REGISTER_DEBUG() \
02	debug_sp_register()        

This macro allows you to add the GET_SP_REGISTER_DEBUG() function to your code. The compiler will replace it with a call to debug_sp_register().

You can simply add the GET_SP_REGISTER_DEBUG() macro to any function where you want to check the stack address or link register.

Stack Debugging Code for 64-bit Armv8-A Architecture

Now, let's look at debugging code for the 64-bit Armv8-A architecture.

01 #define GET_SP_REGISTER_DEBUG() \
02	get_sp_register_with_function(__func__, __LINE__)
03	
04 unsigned long sp_value;
05 unsigned long lr_value;
06
07 static inline void debug_sp_register(void)
08 {
09	asm volatile ("mov %0, sp\n" \
10		     "mov %1, x30"\ 
11		      :"=r" (sp_value), "=r" (lr_value));		
12					
13	printf("sp: 0x%x, x30: 0x%x \n", sp_value, lr_value);				
14
15	printf("[0x%lx]: 0x%lx \n", (unsigned long)sp_value, *((unsigned long*)(sp_value + 0x0)) );
16	printf("[0x%lx]: 0x%lx \n", (unsigned long)(sp_value + 0x8), *((unsigned long*)(sp_value + 0x8)) );
17	printf("[0x%lx]: 0x%lx \n", (unsigned long)(sp_value + 0x10), *((unsigned int*)(sp_value + 0x10)) )
18 }        

The function name and code are similar, but the assembly code in lines 09-11 is different:


09	asm volatile ("mov %0, sp\n" \
10		     "mov %1, x30"\ 
11		      :"=r" (sp_value), "=r" (lr_value));        

In the Armv8 architecture, the link register is x30, so line 10 uses x30 instead of r14.

How to Use the Stack Debugging Code

To use the above code, simply add the GET_SP_REGISTER_DEBUG() function to any function where you want to print the current stack address and link register values.

Here's an example of how to add it to a function:


01 int add_func(int x, int y)
02 {
03    int result = x + y;
04    GET_SP_REGISTER_DEBUG();
05	  
06    printf("x:%d, y:%d \n", x, y);
07   
08    return result;
09 }        

.By adding the GET_SP_REGISTER_DEBUG() function, you can see the stack register and the data stored in the stack in the terminal. For example:

01 sp: 0x7fe564d940 lr: 0x556b5a0b48
02 [0x7fe564d940]: 0x7fe564d960
03 [0x7fe564d948]: 0x556b5a0b48
04 [0x7fe564d950]: 0x6b5a0720        

In the output, line 01 shows the stack address 0x7fe564d940 and the link register 0x556b5a0b48. Lines 02-04 show the data stored at the stack addresses.

This debugging code can be used in various ways in real-world projects to efficiently detect stack corruption. I hope you find it useful in your own projects!

Satish Sugasi

Software Engineer @Sigma Connectivity . Ex-Qualcomm | Bluetooth Development, Embedded Systems

1 个月

Insightful

回复
Pintu Kumar (Agarwal)

Linux Kernel | Memory Management

1 个月

Austin Kim we already have dump_stack() and current_thread_info() and similar functions, are that not enough?

Vijay Kumar Peshkar

Principal Engineering Manager at Qualcomm

1 个月

Very informative

要查看或添加评论,请登录

Austin Kim的更多文章

社区洞察

其他会员也浏览了