64bit processes can issue INT $0x80 instruction, and get the syscall dispatched through ia32_syscall(). This works because syscall argument fetch and result return are selected from the process sysent. But, ia32_syscall() does not verify some conditions and does not perform some actions which are considered unnecessary because the caller is supposed to only access lower 4G. The INT syscall path breaks this assumption. We never supported such hack, so disable it. Send the offending thread SIGBUS as if #GP was issued by hardware due to IDT vector 0x80 having not enough DPL value. amd64: ia32_fetch_syscall_args() does not need to check params != NULL Whatever params pointer is, it does not matter. copyin() handles any values. In fact, params cannot be ever NULL.
Details
Diff Detail
- Repository
- rG FreeBSD src repository
- Lint
Lint Not Applicable - Unit
Tests Not Applicable
Event Timeline
This is the test program I used
.text .globl _start _start: movl $4, %eax movq $0, %rdi movq $string, %rsi movq $string_end-string, %rdx int $0x80 movl $1, %eax movq $0, %rdi int $0x80 jmp . string: .ascii "hello\n" string_end:
But, ia32_syscall() does not verify some conditions and does not perform some actions which are considered unnecessary because the caller is supposed to only access lower 4G.
What conditions and actions does this refer to?
For instance
if (__predict_false(td->td_frame->tf_rip >= (la57 ? VM_MAXUSER_ADDRESS_LA57 : VM_MAXUSER_ADDRESS_LA48))) set_pcb_flags(td->td_pcb, PCB_FULL_IRET); amd64_syscall_ret_flush_l1d_check_inline(td->td_errno);
There is more to it, for instance fsbase/gsbase are not properly saved in the int80 entry point.
This was added to address some vulnerability specific to syscall/sysret? I am not sure.
amd64_syscall_ret_flush_l1d_check_inline(td->td_errno);
This is present in ia32_syscall().
There is more to it, for instance fsbase/gsbase are not properly saved in the int80 entry point.
So, isn't the SV_ILP32 check insufficient? A program can brand itself as a 32-bit executable and still manipulate the user gsbase via sysarch(I386_SET_GSBASE).
| sys/amd64/ia32/ia32_syscall.c | ||
|---|---|---|
| 220 | I think these assignments can come after the check? | |
There is a comment above this fragment in the trap.c which explains it. It was FreeBSD-SA-12:04.sysret
amd64_syscall_ret_flush_l1d_check_inline(td->td_errno);This is present in ia32_syscall().
There is more to it, for instance fsbase/gsbase are not properly saved in the int80 entry point.
So, isn't the SV_ILP32 check insufficient? A program can brand itself as a 32-bit executable and still manipulate the user gsbase via sysarch(I386_SET_GSBASE).
It is fine to use the syscall. Problems are caused by WRFSBASE/WRGSBASE instructions, which are only available in 64bit mode.
Will you convert it to a test program that we can add to the regression test suite? It's fine to add plain C programs there, i.e., no ATF.
This snippet cannot become a test program, since it works before the patch, but is killed by SIGBUS on the first int 80 after.
The proper test program would need to catch SIGBUS, try int 80, and confirm that it got SIGBUS instead of some syscall action.
I can write it, but it is definitely not this asm program.
| sys/amd64/ia32/ia32_syscall.c | ||
|---|---|---|
| 220 | Only orig_tf_rflags can be moved, I think. | |
Or fork a child, invoke the snippet there, and check for SIGBUS in the parent.
It would be nice to have it IMO.