<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/56289>56289</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
-fsanitize=integer-divide-by-zero can cause functions to fallthrough to subsequent functions
</td>
</tr>
<tr>
<th>Labels</th>
<td>
question,
compiler-rt:ubsan
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
nickdesaulniers
</td>
</tr>
</table>
<pre>
Consider the following reduced test case:
```c
struct fb_fix_screeninfo {
unsigned line_length;
};
struct fb_fix_screeninfo fb_sys_read(int);
long ufx_ops_write_result;
int ufx_ops_write_offset;
void ufx_ops_write(struct fb_fix_screeninfo *info) {
if (ufx_ops_write_result) {
int start = ufx_ops_write_offset / info->line_length;
start = start > 0 ? start : 0;
int lines = ufx_ops_write_result / info->line_length;
fb_sys_read(start);
}
}
void ufx_usb_probe() {}
```
```sh
$ clang -O2 -fsanitize=integer-divide-by-zero -S smscufx.i -o - -fno-asynchronous-unwind-tables -fno-verbose-asm
```
```asm
ufx_ops_write:
pushq %rax
cmpq $0, ufx_ops_write_result(%rip)
je .LBB0_3
movl ufx_ops_write_offset(%rip), %eax
movl (%rdi), %ecx
testl %ecx, %ecx
je .LBB0_2
xorl %edi, %edi
xorl %edx, %edx
divl %ecx
testl %eax, %eax
cmovgl %eax, %edi
popq %rax
jmp fb_sys_read@PLT
.LBB0_3:
popq %rax
retq
.LBB0_2:
leaq .L__unnamed_1(%rip), %rdi
movq %rax, %rsi
xorl %edx, %edx
callq __ubsan_handle_divrem_overflow@PLT
movq ufx_ops_write_result(%rip), %rsi
leaq .L__unnamed_2(%rip), %rdi
xorl %edx, %edx
callq __ubsan_handle_divrem_overflow@PLT
.Lfunc_end0:
```
>From what I can tell, it seems that before simplifycfg we have a phi from the overflow handlers with poison values. simplifycfg is turning them into a call to `__ubsan_handle_divrem_overflow` followed by unreachable.
```llvm
; before simplifycfg
define dso_local void @ufx_ops_write(ptr nocapture noundef readonly %info) local_unnamed_addr #0 {
entry:
%0 = load i64, ptr @ufx_ops_write_result, align 8, !tbaa !5
%tobool.not = icmp eq i64 %0, 0
br i1 %tobool.not, label %if.end, label %if.then
if.then: ; preds = %entry
%1 = load i32, ptr @ufx_ops_write_offset, align 4, !tbaa !9
%2 = load i32, ptr %info, align 4, !tbaa !11
%.not = icmp eq i32 %2, 0, !nosanitize !13
br i1 %.not, label %cont, label %cont.thread, !prof !14, !nosanitize !13
cont.thread: ; preds = %if.then
%div10 = udiv i32 %1, %2
%3 = tail call i32 @llvm.smax.i32(i32 %div10, i32 0)
br label %cont3
cont: ; preds = %if.then
%4 = zext i32 %1 to i64, !nosanitize !13
tail call void @__ubsan_handle_divrem_overflow(ptr nonnull @1, i64 %4, i64 0) #4, !nosanitize !13
%.pr = load i32, ptr %info, align 4, !tbaa !11
%.not9 = icmp eq i32 %.pr, 0
br i1 %.not9, label %handler.divrem_overflow2, label %cont3, !prof !14, !nosanitize !13
handler.divrem_overflow2: ; preds = %cont
%5 = load i64, ptr @ufx_ops_write_result, align 8, !tbaa !5
tail call void @__ubsan_handle_divrem_overflow(ptr nonnull @3, i64 %5, i64 0) #4, !nosanitize !13
br label %cont3, !nosanitize !13
cont3: ; preds = %cont.thread, %handler.divrem_overflow2, %cont
%6 = phi i32 [ %3, %cont.thread ], [ poison, %handler.divrem_overflow2 ], [ poison, %cont ]
%call = tail call i32 @fb_sys_read(i32 noundef %6) #4
br label %if.end
if.end: ; preds = %cont3, %entry
ret void
}
```llvm
; after simplifycfg
define dso_local void @ufx_ops_write(ptr nocapture noundef readonly %info) local_unnamed_addr #0 {
entry:
%0 = load i64, ptr @ufx_ops_write_result, align 8, !tbaa !5
%tobool.not = icmp eq i64 %0, 0
br i1 %tobool.not, label %if.end, label %if.then
if.then: ; preds = %entry
%1 = load i32, ptr @ufx_ops_write_offset, align 4, !tbaa !9
%2 = load i32, ptr %info, align 4, !tbaa !11
%.not = icmp eq i32 %2, 0, !nosanitize !13
br i1 %.not, label %cont, label %cont.thread, !prof !14, !nosanitize !13
cont.thread: ; preds = %if.then
%div10 = udiv i32 %1, %2
%3 = tail call i32 @llvm.smax.i32(i32 %div10, i32 0)
%call = tail call i32 @fb_sys_read(i32 noundef %3) #5
br label %if.end
cont: ; preds = %if.then
%4 = zext i32 %1 to i64, !nosanitize !13
tail call void @__ubsan_handle_divrem_overflow(ptr nonnull @1, i64 %4, i64 0) #5, !nosanitize !13
%.pr = load i32, ptr %info, align 4, !tbaa !11
%.not9 = icmp eq i32 %.pr, 0
call void @llvm.assume(i1 %.not9)
%5 = load i64, ptr @ufx_ops_write_result, align 8, !tbaa !5
tail call void @__ubsan_handle_divrem_overflow(ptr nonnull @3, i64 %5, i64 0) #5, !nosanitize !13
unreachable
if.end: ; preds = %cont.thread, %entry
ret void
}
```
This comes from [this thread](https://lore.kernel.org/lkml/CAKwvOdnfRnqBF8exO-Y1ooX=67TrO_8fSzgZwvUtidN_P31hzw@mail.gmail.com/) on LKML. I know we could use `-mllvm -trap-unreachable` here. Is this a case of "well, the divide by zero case is UB, so we don't need to safely return?" Because it would be nice if ubsan errors were recoverable.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJztWVtv2zYU_jXOCyFDF18f_JA0CzA0W4utBba9CJREWWwpUiEpO8mv3zmUZEu2cts6YMNaFLZEnvv5eM4JnajsYfNOScMzpoktGMmVEGrP5ZZoltUpy4hlxpKUGjaJLif-9cS_nCz85n_avBur69SSPIlzfh-bVDMmucwVmSyvGgpCatCxlSBOcMliweTWFpOo3Z4sr4_Pz4uEFfNgYs1oNglXXNpJuD7hFQqsr_P7WFUm3mtuGZCbWtgDHbCdEKg8N8yeCNopng3pQOXTzoaX-A329P3mOWysRq0ZEgIpWGUs1ZZMoutR-0DUDUEl3iT6YSyQKOYoonv6gfjweXN4vyT-gAMVozQzorix9VWKh6lxyvrJIQTTfMj3MMK1SeJKqwQj3AXmQN3B7eTVFO1COCOpoJB070NIvNxQyS1_BLxeg2dsy7SX8R0g3EsevEemFfF-JaY0KSiecuLBO3BJ5VHzINNCK6lq49USTkHmWZoICIzb3zGdKMOArnzesgPBEDyH8-Ovq9oUd_A9Ceea3h-W07JqVmf-JHw3DmIM0FzzCmPb8X1h8DG9vbry4-iwWKqdgK9RoPeFgCJ4YT0zWs6WKuM9qvRIhYWhIXPL5wQ9q8LD4r3SHRMKbpjgaWz_IDQ7CoVU9pSO2ULvR1xKwaftOUVPcaWqkYx8KSv47EN75n-8_dTsdxHvJ3ZUimb2rs8S9lkEo3cuUHFcS0lLlsXBSIJ0z1Zwpqel3TevD2JKhUABoDGB4xIXVGaCxRBbzcpYAdBz6AJ9V49KXwTluTkjHoYvePjtPZje5rVMYyYz_7yTNa83WpVkX1BLfoSOJ6H1CYG6ORRmxkoDHRL2EpYrzYjhZSV4_pDmW7JnpKA7RiipCk5yFIPNtDODNNZpQ_bcFqRS3ChJdlTUzEwHgjjoqLXE_gsCSizNCqSitwSewNIXHF74bQuHVps8QNsFzKYFFrHpictC7LoyFl2NONXsZSyHck8yo2KhwAziSjbE9bQvVlYTCRQV2M_gqZbASvDEKCkeMH1dd3RyDkCgWaZhN_KPzZBJqx8OOSLI67vWJBTNCF_MMCeo79SMAxrfESpg3CCrBjmBTSjF73lPpFWJUmIqVdMsOdRewu5QvFOInH5HnmjCgyET7guaMOFcy6eAq9MlyKDszxPdUuTa5ev-YW4qGMWa3oyHwAXn6EbQi0wUPhmZru53kZmdRGbdExmOi-wy-JSIIOjJOI9rFDrZTVwbTqm6Xu34o7NwnwU6VfJ8AaLaDB1OKIwRuRM3e1ZL89lnf21azlIySLXzHk5l0EC2hsfO96CtY2GPMnJUlnLRHHJHOnOHc2pKCgMKZmDVSnByXUWCd_8wAbiIDUJy5uSbQPc6J2du65Hd24ODWKPa8_lMeo_edsXkhaLWVRcpa2ACehfJ9qTOumffDY5h9JJ6xFWlvxHG12MgB_Hj5cNxDPDbdobpicvhGcijvwLvJ6U_g4azzDv4HL2ef-NS_PfREPXQMH8rGs5PznP0DcXbztJoQAc161kUnGdg4QThpOHwNr9yhaRH2wqHrWu3ChTNzPGCuicZUKjbPBrhMjZau07-RIfFbhpA2w9pGYl_20aHLRNX3hTw0Xh34Rm0TxjLHehO_ip9YkSiuWX6-4T0fUL6PiG9NSX_ggnpr5esqC1Z89eVrP_ZtDX_101bA18dTKgxdYkleDCF9aHxHxtqXgp678_-f6SbDqeXN_XU5vVTwQ1JVclMc1sCE4fFpVYujiGrwtrKYJMLb-C_UJpNvzItmZgqvcWVr6WAr3eX7_e7D5nMf5F3Vzcrdv_B-z1Q6jcwd7H8pD_Eq_zXx-0f-91ny7Of449RUDzixVAJKZpu3ScY4pSsiZLk9v1Pt1NCfiRfpdrjzU6qapGR2jC8fvFKRBTxrKaV1w_zwicFAxOBE90AX6j71YS4Whvu26skvBZqbqPxesbdRjsqoP98hQRGoc4MR6-lJZLhLzCKGJozmBEguLWG9gi2hoRcsZSiWdySvbMxgbGCpwx_dHBgI0xrhbdOYBjwpog2dxV0kW2ibB2t6YXlVrDNay7N8UKs0Yf3aJYradCyHEAOWVP1tnCGgmJ2VwMkjmQXtRabYTa33BZ10gbejVnNlwe96QtL4YjdcDi0zMDDfBGu1hfFJqXLIMhn4XKeLRbhPE9oEPjLbJmHi6W_WAcXriibDWAJwgM2GNQOjw1MQ1BWcRh7PY312QUIN-fXF3wT-mHoL8J1sJwFfjAF8WG4DFiWzleLZR4AXJhDiisnAL8LvXHmJvXWYJXhxprjJtQb_LGLOVNAPq1tofQGcvM1Y4bWQnKmzYXzcOPc-xOZlvhi">