Prex 0.9.0 suffers from a serious bug in its exception handling code

In our Systems course here at Northeastern, we’re working with Prex as our experimental platform this semester. A recent assignment called for the implementation of some virtual-memory-like features, using Prex’s exceptions to deliver information about faulting memory accesses to the faulting process, which is thereby given an opportunity to arrange for some memory to back the accessed address.

The problem is in the x86 code implementing system call handling for the exception_return() system call. The logic for detecting that an exception_return() has occurred neglects to take into account the fact that returning from an exception replaces the active context. The fix is to check %eax before calling syscall_handler.

Without the patch below, %eax will be smashed in the context of the code that was interrupted by the exception.

I emailed this patch to the Prex list, but something about my message triggered Sourceforge’s spam detection software, or something equally tedious and annoying, and it hasn’t made its way through yet; so I’m posting this here in the hopes that those in need of it might find it.

Here’s the required change:

--- a/bsp/hal/x86/arch/locore.S
+++ b/bsp/hal/x86/arch/locore.S
@@ -313,11 +313,23 @@ ENTRY(syscall_entry)
	pushl	$(SYSCALL_INT)		/* Trap number */
	SAVE_ALL
	SETUP_SEG
-	call	syscall_handler
+	/* We check the saved value of eax here, before calling syscall_handler,
+	   because if it's zero (i.e. exception_return), we will completely
+	   obliterate the saved registers in context_restore() within
+	   exception_return() within syscall_handler(), which will make the value
+	   of 0x10(%esp) contain whatever random eax value happened to be around
+	   at the time the exception was delivered. For example, if the exn
+	   was a page fault, eax could be anything at all. If we check 0x10(%esp)
+	   after syscall_handler has returned, it will in the general case contain
+	   junk. */
	cmpl	$0, 0x10(%esp)		/* Skip setting eax if exception_return */
	je	1f
+	call	syscall_handler
	movl	%eax, 0x10(%esp)	/* Set return value to eax */
+	jmp	2f
 1:
+	call	syscall_handler
+2:
	call	exception_deliver	/* Check exception */
 syscall_ret:
	RESTORE_ALL