Fixes sys_change_to memory clobber
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 11 Oct 2012 00:32:18 +0000 (17:32 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 16 Oct 2012 21:42:20 +0000 (14:42 -0700)
This was previously fixed in the kernel in 67e1cce, and then screwed up
recently in d9513a4.

Fixing it in the kernel is a bad way to go, so this fixes it in
userspace.  The core issue is userspace asks the kernel to do something
to basically 'free' memory (reset the VC ctx to the top of stack), and
that memory is being used by the kernel to report the syscall.  It's
somewhat equivalent to mmapping a page, putting a struct syscall on it,
then using that to request munmapping that page, and then being
surprised when something bad happens to you (we actually don't handle
this well yet).

user/parlib/include/vcore.h
user/parlib/syscall.c
user/parlib/vcore.c

index 467d8b9..25f63de 100644 (file)
@@ -30,9 +30,11 @@ extern "C" {
 
 /* Defined by glibc; Must be implemented by a user level threading library */
 extern void vcore_entry();
-/* Declared in glibc's start.c */
+/* Defined in glibc's start.c */
 extern __thread bool __vcore_context;
 extern __thread int __vcoreid;
+/* Defined in vcore.c */
+extern __thread struct syscall __vcore_one_sysc;       /* see sys_change_vcore */
 
 /* Vcore API functions */
 static inline size_t max_vcores(void);
index 08bcedb..10f46bb 100644 (file)
@@ -1,6 +1,7 @@
 // System call stubs.
 
 #include <parlib.h>
+#include <vcore.h>
 
 int sys_proc_destroy(int pid, int exitcode)
 {
@@ -148,7 +149,32 @@ int sys_block(unsigned int usec)
  *             -EINVAL some userspace bug */
 int sys_change_vcore(uint32_t vcoreid, bool enable_my_notif)
 {
-       return ros_syscall(SYS_change_vcore, vcoreid, enable_my_notif, 0, 0, 0, 0);
+       /* Since we might be asking to start up on a fresh stack (if
+        * enable_my_notif), we need to use some non-stack memory for the struct
+        * sysc.  Our vcore could get restarted before the syscall finishes (after
+        * unlocking the proc, before finish_sysc()), and the act of finishing would
+        * write onto our stack.  Thus we use the per-vcore struct. */
+       int flags;
+       /* Need to wait while a previous syscall is not done or locked.  Since this
+        * should only be called from VC ctx, we'll just spin.  Should be extremely
+        * rare.  Note flags is initialized to SC_DONE. */
+       do {
+               cpu_relax();
+               flags = atomic_read(&__vcore_one_sysc.flags);
+       } while (!(flags & SC_DONE) || flags & SC_K_LOCK);
+       __vcore_one_sysc.num = SYS_change_vcore;
+       __vcore_one_sysc.arg0 = vcoreid;
+       __vcore_one_sysc.arg1 = enable_my_notif;
+       /* keep in sync with glibc sysdeps/ros/syscall.c */
+       __ros_arch_syscall((long)&__vcore_one_sysc, 1);
+       /* If we returned, either we wanted to (!enable_my_notif) or we failed.
+        * Need to wait til the sysc is finished to find out why.  Again, its okay
+        * to just spin. */
+       do {
+               cpu_relax();
+               flags = atomic_read(&__vcore_one_sysc.flags);
+       } while (!(flags & SC_DONE) || flags & SC_K_LOCK);
+       return __vcore_one_sysc.retval;
 }
 
 int sys_change_to_m(void)
index 2349d92..fa81200 100644 (file)
@@ -20,6 +20,7 @@ atomic_t nr_new_vcores_wanted;
 atomic_t vc_req_being_handled;
 
 extern void** vcore_thread_control_blocks;
+__thread struct syscall __vcore_one_sysc = {.flags = (atomic_t)SC_DONE, 0};
 
 /* TODO: probably don't want to dealloc.  Considering caching */
 static void free_transition_tls(int id)