Răsfoiți Sursa

Merge pull request #1309 from Yawning/fix/freebsd-syscall

src: "Fix" the system call intrinsic for FreeBSD
gingerBill 2 ani în urmă
părinte
comite
151396507e
1 a modificat fișierele cu 45 adăugiri și 4 ștergeri
  1. 45 4
      src/llvm_backend_proc.cpp

+ 45 - 4
src/llvm_backend_proc.cpp

@@ -2660,10 +2660,26 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 				{
 					GB_ASSERT(arg_count <= 7);
 
+					// FreeBSD additionally clobbers r8, r9, r10, but they
+					// can also be used to pass in arguments, so this needs
+					// to be handled in two parts.
+					bool clobber_arg_regs[7] = {
+						false, false, false, false, false, false, false
+					};
+					if (build_context.metrics.os == TargetOs_freebsd) {
+						clobber_arg_regs[4] = true; // r10
+						clobber_arg_regs[5] = true; // r8
+						clobber_arg_regs[6] = true; // r9
+					}
+
 					char asm_string[] = "syscall";
 					gbString constraints = gb_string_make(heap_allocator(), "={rax}");
 					for (unsigned i = 0; i < arg_count; i++) {
-						constraints = gb_string_appendc(constraints, ",{");
+						if (!clobber_arg_regs[i]) {
+							constraints = gb_string_appendc(constraints, ",{");
+						} else {
+							constraints = gb_string_appendc(constraints, ",+{");
+						}
 						static char const *regs[] = {
 							"rax",
 							"rdi",
@@ -2687,10 +2703,35 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 					// Some but not all system calls will additionally
 					// clobber memory.
 					//
-					// TODO: FreeBSD is different and will also clobber
-					// R8, R9, and R10.  Additionally CF is used to
-					// indicate an error instead of -errno.
+					// As a fix for CVE-2019-5595, FreeBSD started
+					// clobbering R8, R9, and R10, instead of restoring
+					// them.  Additionally unlike Linux, instead of
+					// returning negative errno, positive errno is
+					// returned and CF is set.
+					//
+					// TODO:
+					//  * Figure out what Darwin does.
+					//  * Add some extra handling to propagate CF back
+					//    up to the caller on FreeBSD systems so that
+					//    the caller knows that the return value is
+					//    positive errno.
 					constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}");
+					if (build_context.metrics.os == TargetOs_freebsd) {
+						// Second half of dealing with FreeBSD's system
+						// call semantics.  Explicitly clobber the registers
+						// that were not used to pass in arguments, and
+						// then clobber RFLAGS.
+						if (arg_count < 5) {
+							constraints = gb_string_appendc(constraints, ",~{r10}");
+						}
+						if (arg_count < 6) {
+							constraints = gb_string_appendc(constraints, ",~{r8}");
+						}
+						if (arg_count < 7) {
+							constraints = gb_string_appendc(constraints, ",~{r9}");
+						}
+						constraints = gb_string_appendc(constraints, ",~{cc}");
+					}
 
 					inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
 				}