一 概念
UBSAN:全称Undefined Behavior Sanitizer,用于运行时检测一些未定义行为。未定义,是一个统称,并非是代码中的变量等没有定义,如果是此类错误,编译就会进行拦截。此含义就可以理解为一个非法行为的检测,比如定义的变量超过范围,数组使用不正确的下标,左右移不符合规范,或者超过变量的范围,对齐不匹配等等。
二 内核版本区分
2.1 kernel-4.19
userdebug版本默认使能,user版本未开启,使能方法:
junfang1@buildsrv-n43:~/work/0_code/s0-trank-user/tran_projects$ git diff x6820/x6820_h773_a1/x6820/kernel-4.19/arch/arm64/configs/x6820_h773_defconfig
diff --git a/x6820/x6820_h773_a1/x6820/kernel-4.19/arch/arm64/configs/x6820_h773_defconfig b/x6820/x6820_h773_a1/x6820/kernel-4.19/arch/arm64/configs/x6820_h773_defconfig
index a0781309f..a285bcd64 100644
--- a/x6820/x6820_h773_a1/x6820/kernel-4.19/arch/arm64/configs/x6820_h773_defconfig
+++ b/x6820/x6820_h773_a1/x6820/kernel-4.19/arch/arm64/configs/x6820_h773_defconfig
@@ -4,6 +4,11 @@ CONFIG_IKHEADERS=m
CONFIG_LOG_BUF_SHIFT=19
CONFIG_UCLAMP_TASK=y
CONFIG_BLK_CGROUP=y
+#add for ubsan start
+CONFIG_UBSAN=y
+CONFIG_UBSAN_SANITIZE_ALL=y
+#add for ubsan end
CONFIG_UCLAMP_TASK_GROUP=y
CONFIG_CPUSETS=y
CONFIG_SCHED_TUNE=y
2.2 kernel-5.10
默认所有版本开启,可通过命令查看:
:/ # zcat /proc/config.gz | grep UBSAN
CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
CONFIG_UBSAN=y
CONFIG_UBSAN_TRAP=y
CONFIG_CC_HAS_UBSAN_BOUNDS=y
CONFIG_CC_HAS_UBSAN_ARRAY_BOUNDS=y
CONFIG_UBSAN_BOUNDS=y
CONFIG_UBSAN_ARRAY_BOUNDS=y
CONFIG_UBSAN_LOCAL_BOUNDS=y
# CONFIG_UBSAN_SHIFT is not set
# CONFIG_UBSAN_DIV_ZERO is not set
# CONFIG_UBSAN_UNREACHABLE is not set
# CONFIG_UBSAN_OBJECT_SIZE is not set
# CONFIG_UBSAN_BOOL is not set
# CONFIG_UBSAN_ENUM is not set
CONFIG_UBSAN_SANITIZE_ALL=y
# CONFIG_TEST_UBSAN is not set
三 案例
对应的code如下:
=》除数为0
static void test_ubsan_divrem_overflow(void)
{
volatile int val = 16;
volatile int val2 = 0;
val /= val2;
}
=》数组下标非法
static void test_ubsan_vla_bound_not_positive(void)
{
volatile int size = -1;
char buf[size];
(void)buf;
}
=》右移不合理
static void test_ubsan_shift_out_of_bounds(void)
{
volatile int val = -1;
int val2 = 10;
val2 <<= val;
}
=》数组index越界
static void test_ubsan_out_of_bounds(void)
{
volatile int i = 4, j = 5;
volatile int arr[i];
arr[j] = i;
}
UBASAN检测出来的异常打印如下:
<3>[ 1056.356966]-(7)[7270:insmod]================================================================================
<3>[ 1056.358247]-(7)[7270:insmod]UBSAN: Undefined behaviour in /work/junfang1/work/0_code/s0-trank-user/kernel-4.19/lib/test_ubsan.c:42:6
<3>[ 1056.359784]-(7)[7270:insmod]division by zero
<4>[ 1056.360371]-(7)[7270:insmod]CPU: 7 PID: 7270 Comm: insmod Tainted: G S W 4.19.191+ #13
<4>[ 1056.361551]-(7)[7270:insmod]Hardware name: MT6877V/TZA (DT)
<4>[ 1056.362295]-(7)[7270:insmod]Call trace:
<4>[ 1056.362836]-(7)[7270:insmod] dump_backtrace.cfi_jt+0x0/0x4
<4>[ 1056.363578]-(7)[7270:insmod] dump_stack+0xb8/0xf0
<4>[ 1056.364220]-(7)[7270:insmod] ubsan_epilogue+0x14/0xb8
<4>[ 1056.364900]-(7)[7270:insmod] __ubsan_handle_divrem_overflow+0x1c8/0x1f0
<4>[ 1056.365777]-(7)[7270:insmod] test_ubsan_divrem_overflow+0x70/0x78 [test_ubsan]
<4>[ 1056.366731]-(7)[7270:insmod] init_module+0x20/0xff0 [test_ubsan]
<4>[ 1056.367535]-(7)[7270:insmod] do_one_initcall+0x184/0x360
<4>[ 1056.368251]-(7)[7270:insmod] do_init_module+0x5c/0x27c
<4>[ 1056.368940]-(7)[7270:insmod] load_module+0x3190/0x3860
<4>[ 1056.369631]-(7)[7270:insmod] __arm64_sys_finit_module+0xb0/0xe0
<4>[ 1056.370422]-(7)[7270:insmod] el0_svc_common+0xb0/0x194
<4>[ 1056.371113]-(7)[7270:insmod] el0_svc_handler+0x60/0x78
<4>[ 1056.371806]-(7)[7270:insmod] el0_svc+0x8/0x300
<3>[ 1056.372409]-(7)[7270:insmod]================================================================================
<4>[ 1056.373776]-(7)[7270:insmod]ubsan_epilogue: 3 callbacks suppressed
<3>[ 1056.373784]-(7)[7270:insmod]================================================================================
<3>[ 1056.375878]-(7)[7270:insmod]UBSAN: Undefined behaviour in /work/junfang1/work/0_code/s0-trank-user/kernel-4.19/lib/test_ubsan.c:48:11
<3>[ 1056.377425]-(7)[7270:insmod]variable length array bound value -1 <= 0
<4>[ 1056.378278]-(7)[7270:insmod]CPU: 7 PID: 7270 Comm: insmod Tainted: G S W 4.19.191+ #13
<4>[ 1056.379456]-(7)[7270:insmod]Hardware name: MT6877V/TZA (DT)
<4>[ 1056.380199]-(7)[7270:insmod]Call trace:
<4>[ 1056.380732]-(7)[7270:insmod] dump_backtrace.cfi_jt+0x0/0x4
<4>[ 1056.381466]-(7)[7270:insmod] dump_stack+0xb8/0xf0
<4>[ 1056.382104]-(7)[7270:insmod] ubsan_epilogue+0x14/0xb8
<4>[ 1056.382782]-(7)[7270:insmod] __ubsan_handle_vla_bound_not_positive+0xc4/0xc8
<4>[ 1056.383713]-(7)[7270:insmod] test_ubsan_vla_bound_not_positive+0x38/0x3c [test_ubsan]
<4>[ 1056.384742]-(7)[7270:insmod] init_module+0x20/0xff0 [test_ubsan]
<4>[ 1056.385543]-(7)[7270:insmod] do_one_initcall+0x184/0x360
<4>[ 1056.386254]-(7)[7270:insmod] do_init_module+0x5c/0x27c
<4>[ 1056.386944]-(7)[7270:insmod] load_module+0x3190/0x3860
<4>[ 1056.387633]-(7)[7270:insmod] __arm64_sys_finit_module+0xb0/0xe0
<4>[ 1056.388422]-(7)[7270:insmod] el0_svc_common+0xb0/0x194
<4>[ 1056.389112]-(7)[7270:insmod] el0_svc_handler+0x60/0x78
<4>[ 1056.389801]-(7)[7270:insmod] el0_svc+0x8/0x300
<3>[ 1056.390403]-(7)[7270:insmod]================================================================================
<3>[ 1056.391705]-(7)[7270:insmod]================================================================================
<3>[ 1056.392980]-(7)[7270:insmod]UBSAN: Undefined behaviour in /work/junfang1/work/0_code/s0-trank-user/kernel-4.19/lib/test_ubsan.c:58:7
<3>[ 1056.394516]-(7)[7270:insmod]shift exponent -1 is negative
<4>[ 1056.395237]-(7)[7270:insmod]CPU: 7 PID: 7270 Comm: insmod Tainted: G S W 4.19.191+ #13
<4>[ 1056.396414]-(7)[7270:insmod]Hardware name: MT6877V/TZA (DT)
<4>[ 1056.397157]-(7)[7270:insmod]Call trace:
<4>[ 1056.397685]-(7)[7270:insmod] dump_backtrace.cfi_jt+0x0/0x4
<4>[ 1056.398418]-(7)[7270:insmod] dump_stack+0xb8/0xf0
<4>[ 1056.399054]-(7)[7270:insmod] ubsan_epilogue+0x14/0xb8
<4>[ 1056.399732]-(7)[7270:insmod] __ubsan_handle_shift_out_of_bounds+0x30c/0x328
<4>[ 1056.400651]-(7)[7270:insmod] test_ubsan_shift_out_of_bounds+0x3c/0x40 [test_ubsan]
<4>[ 1056.401645]-(7)[7270:insmod] init_module+0x20/0xff0 [test_ubsan]
<4>[ 1056.402444]-(7)[7270:insmod] do_one_initcall+0x184/0x360
<4>[ 1056.403154]-(7)[7270:insmod] do_init_module+0x5c/0x27c
<4>[ 1056.403843]-(7)[7270:insmod] load_module+0x3190/0x3860
<4>[ 1056.404532]-(7)[7270:insmod] __arm64_sys_finit_module+0xb0/0xe0
<4>[ 1056.405319]-(7)[7270:insmod] el0_svc_common+0xb0/0x194
<4>[ 1056.406008]-(7)[7270:insmod] el0_svc_handler+0x60/0x78
<4>[ 1056.406697]-(7)[7270:insmod] el0_svc+0x8/0x300
<3>[ 1056.407298]-(7)[7270:insmod]================================================================================
<3>[ 1056.408605]-(7)[7270:insmod]================================================================================
<3>[ 1056.409880]-(7)[7270:insmod]UBSAN: Undefined behaviour in /work/junfang1/work/0_code/s0-trank-user/kernel-4.19/lib/test_ubsan.c:66:2
<3>[ 1056.411416]-(7)[7270:insmod]index 5 is out of range for type 'volatile int [i]'
<4>[ 1056.412375]-(7)[7270:insmod]CPU: 7 PID: 7270 Comm: insmod Tainted: G S W 4.19.191+ #13
<4>[ 1056.413552]-(7)[7270:insmod]Hardware name: MT6877V/TZA (DT)
<4>[ 1056.414295]-(7)[7270:insmod]Call trace:
<4>[ 1056.414823]-(7)[7270:insmod] dump_backtrace.cfi_jt+0x0/0x4
<4>[ 1056.415555]-(7)[7270:insmod] dump_stack+0xb8/0xf0
<4>[ 1056.416189]-(7)[7270:insmod] ubsan_epilogue+0x14/0xb8
<4>[ 1056.416868]-(7)[7270:insmod] __ubsan_handle_out_of_bounds+0xcc/0xd0
<4>[ 1056.417699]-(7)[7270:insmod] test_ubsan_out_of_bounds+0xb8/0xbc [test_ubsan]
<4>[ 1056.418627]-(7)[7270:insmod] init_module+0x20/0xff0 [test_ubsan]
<4>[ 1056.419424]-(7)[7270:insmod] do_one_initcall+0x184/0x360
<4>[ 1056.420135]-(7)[7270:insmod] do_init_module+0x5c/0x27c
<4>[ 1056.420824]-(7)[7270:insmod] load_module+0x3190/0x3860
<4>[ 1056.421513]-(7)[7270:insmod] __arm64_sys_finit_module+0xb0/0xe0
<4>[ 1056.422300]-(7)[7270:insmod] el0_svc_common+0xb0/0x194
<4>[ 1056.422989]-(7)[7270:insmod] el0_svc_handler+0x60/0x78
<4>[ 1056.423679]-(7)[7270:insmod] el0_svc+0x8/0x300
四 代码
UBSAN主要利用 就是代码插桩的方式,在关键的地方插入 代码跳转进行检测,以4.19为例:
反汇编一个函数:
0000000000000134 <test_ubsan_divrem_overflow$ac29f208fd07f28460a5dd7ae18fd394>:
134: d100c3ff sub sp, sp, #0x30
138: a9027bfd stp x29, x30, [sp,#32]
13c: 910083fd add x29, sp, #0x20
140: 52800208 mov w8, #0x10 // #16
144: b81fc3a8 stur w8, [x29,#-4]
148: b81f83bf stur wzr, [x29,#-8]
14c: b85f83a8 ldur w8, [x29,#-8]
150: b85fc3a9 ldur w9, [x29,#-4]
154: 1280000a mov w10, #0xffffffff // #-1
158: 52b0000b mov w11, #0x80000000 // #-2147483648
15c: eb0a011f cmp x8, x10
160: 1a9f07ea cset w10, ne
164: eb0b013f cmp x9, x11
168: 1a9f07eb cset w11, ne
16c: 34000108 cbz w8, 18c <test_ubsan_divrem_overflow$ac29f208fd07f28460a5dd7ae18fd394+0x58>
170: 2a0b014a orr w10, w10, w11
174: 360000ca tbz w10, #0, 18c <test_ubsan_divrem_overflow$ac29f208fd07f28460a5dd7ae18fd394+0x58>
178: 1ac80d28 sdiv w8, w9, w8
17c: b81fc3a8 stur w8, [x29,#-4]
180: a9427bfd ldp x29, x30, [sp,#32]
184: 9100c3ff add sp, sp, #0x30
188: d65f03c0 ret =》正常情况,直接就return了
18c: 90000000 adrp x0, 0 <__cfi_check>
190: 91000000 add x0, x0, #0x0
194: aa0903e1 mov x1, x9
198: aa0803e2 mov x2, x8
19c: a900a7e8 stp x8, x9, [sp,#8]
1a0: 94000000 bl 0 <__ubsan_handle_divrem_overflow> =》如果走入异常插桩code
1a4: a940a7e8 ldp x8, x9, [sp,#8]
1a8: 17fffff4 b 178 <test_ubsan_divrem_overflow$ac29f208fd07f28460a5dd7ae18fd394+0x44>
__ubsan_handle_divrem_overflow函数实现在:
lib/ubsan.c里面:
void __ubsan_handle_divrem_overflow(struct overflow_data *data,
void *lhs, void *rhs)
{
unsigned long flags;
char rhs_val_str[VALUE_LENGTH];
if (suppress_report(&data->location))
return;
ubsan_prologue(&data->location, &flags); =》这里面会打印相关log
val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs);
if (type_is_signed(data->type) && get_signed_val(data->type, rhs) == -1)
pr_err("division of %s by -1 cannot be represented in type %s\n",
rhs_val_str, data->type->type_name);
else
pr_err("division by zero\n");
ubsan_epilogue(&flags);
}
EXPORT_SYMBOL(__ubsan_handle_divrem_overflow);
static void ubsan_prologue(struct source_location *location,
unsigned long *flags)
{
current->in_ubsan++;
spin_lock_irqsave(&report_lock, *flags);
pr_err("========================================"
"========================================\n");
print_source_location("UBSAN: Undefined behaviour in", location);
}
static void ubsan_epilogue(unsigned long *flags)
{
int cpu;
struct rq *rq;
dump_stack();
pr_err("========================================"
"========================================\n");
spin_unlock_irqrestore(&report_lock, *flags);
current->in_ubsan--;
cpu = raw_smp_processor_id();
rq = cpu_rq(cpu);
if (!raw_spin_is_locked(&rq->lock)) { =》最后基本上都会走到这边,所以大多场景都是warning log呈现
/* AEE Kernel API Dump for UBSan */
aee_kernel_warning_api(__FILE__, __LINE__, DB_OPT_DEFAULT,
"UBSan error",
"[UBSan report]");
} else {
BUG(); =》可以强制走这边,这样遇到一些不合法行为就可以出发bug
}
}
5.10默认也不会触发bug,需要添加code
146 static void ubsan_epilogue(void)
147 {
148 dump_stack();
149 pr_err("========================================"
150 "========================================\n");
151
152 current->in_ubsan--;
153 + panic_on_warn = 1; =》新增触发panic添加的
154 if (panic_on_warn) { =》如果希望触发panic,需要dts里面传入panic_on_warn
155 /*
156 * This thread may hit another WARN() in the panic path.
157 * Resetting this prevents additional WARN() from panicking the
158 * system on this thread. Other threads are blocked by the
159 * panic_mutex in panic().
160 */
161 panic_on_warn = 0;
162 panic("panic_on_warn set ...\n");
163 }
164 }