Context-saving functions are attrib'd return_twice
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 5 Aug 2013 23:22:44 +0000 (16:22 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 6 Aug 2013 21:58:09 +0000 (14:58 -0700)
The compiler should warn us if it was unable to generate the code that doesn't
rely on registers across the function call.

The places we used it didn't do much on the return, so this probably wasn't a
problem.  Still, it may be in the future, and this beats inspecting the
assembly.

One thing that isn't clear is whether or not returns_twice causes the compiler
to mark all registers as dead, or just the caller-saved registers.  Probably
the former.

The motivation for this is a stack clobber problem, where the compiler may save
the caller-saved registers (before calling save_user_ctx()) by pushing them on
the stack.  If this happens, when save_ returns the first time, it'll restore
the registers properly, but then could clobber that space so that the second
time it returns, the values it pops are garbage.  Hopefully the compiler will
avoid saving registers in this manner, or otherwise do anything that could be
harmed by returning twice.

kern/include/trap.h
user/parlib/include/riscv/vcore.h
user/parlib/include/x86/vcore32.h
user/parlib/include/x86/vcore64.h

index 22a9bed..2d79ddd 100644 (file)
@@ -41,7 +41,7 @@ uintptr_t get_stack_top(void);
 
 /* It's important that this is inline and that ctx is not a stack variable */
 static inline void save_kernel_ctx(struct kernel_ctx *ctx)
-                   __attribute__((always_inline));
+                   __attribute__((always_inline, returns_twice));
 void pop_kernel_ctx(struct kernel_ctx *ctx) __attribute__((noreturn));
 
 /* Sends a non-maskable interrupt, which we have print a trapframe. */
index 9acf82a..f0fb959 100644 (file)
@@ -21,7 +21,7 @@
 typedef void (*helper_fn)(struct hw_trapframe*, struct preempt_data*, uint32_t);
 void __pop_ros_tf_regs(struct hw_trapframe *tf, struct preempt_data* vcpd,
                     uint32_t vcoreid, helper_fn helper) __attribute__((noreturn));
-void __save_ros_tf_regs(struct hw_trapframe *tf);
+void __save_ros_tf_regs(struct hw_trapframe *tf) __attribute__((returns_twice));
 
 /* Helper function that may handle notifications after re-enabling them. */
 static void __pop_ros_tf_notifs(struct hw_trapframe *tf,
index 434a845..400f475 100644 (file)
@@ -272,7 +272,7 @@ static inline void save_user_ctx(struct user_context *ctx)
                      : "=c"(dummy)     /* force clobber for ecx */
                      : "c"(sw_tf)
                      : "eax", "edx", "memory", "cc");
-}
+} __attribute__((always_inline, returns_twice))
 
 /* The old version, kept around for testing */
 static inline void save_user_ctx_hw(struct user_context *ctx)
@@ -298,7 +298,7 @@ static inline void save_user_ctx_hw(struct user_context *ctx)
                     : 
                     : "g"(&tf->tf_esp), "g"(&tf->tf_eip), "g"(tf)
                     : "eax", "memory", "cc");
-}
+} __attribute__((always_inline, returns_twice))
 
 static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
                                  uint32_t stack_top)
index 984f8d7..93dcd32 100644 (file)
@@ -288,7 +288,7 @@ static inline void save_user_ctx(struct user_context *ctx)
                                 : "D"(sw_tf)
                     : "rax", "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11",
                       "memory", "cc");
-}
+} __attribute__((always_inline, returns_twice))
 
 /* The old version, kept around for testing */
 /* Hasn't been used yet for 64 bit.  If you use this, it's worth checking to
@@ -331,7 +331,7 @@ static inline void save_user_ctx_hw(struct user_context *ctx)
                     : 
                     : "g"(&tf->tf_rsp), "g"(&tf->tf_rip), "g"(tf->tf_rax)
                     : "rax", "memory", "cc");
-}
+} __attribute__((always_inline, returns_twice))
 
 static inline void init_user_ctx(struct user_context *ctx, uintptr_t entry_pt,
                                  uintptr_t stack_top)