[RFC][PATCH 5/X][libsanitizer] Introduce longjmp/setjmp interceptors to libhwasan

Matthew Malcomson Matthew.Malcomson@arm.com
Fri Sep 6 14:46:00 GMT 2019


When using hwasan to tag parameters on the stack, we need to ensure that
the shadow stack is cleared upon exit of a function.

If this is not done, then accesses to an untagged memory region (e.g.
parameters passed on the stack) can end up being checked against a
shadow region that was coloured for a variable some time in the past.

Ensuring that the shadow stack is cleared on normal function exit will
be done by adding instrumentation to the function epilogue through the
compiler.
longjmp and setjmp are some abnormal methods of exiting the function
that can't be handled in the compiler since they can be called in
uninstrumented code to unwind past instrumented function frames.

This patch introduces some interceptors for setjmp and longjmp.
This pair of functions only behaves differently to the glibc version by
uncolouring the stack between the point where longjmp was called and the
destination stack pointer.

The file hwasan_interceptors.cc aims to include as little system headers
as possible, since it defines interceptors for system functions and
hence needs to use its own data structures that are independent of the
current platform.  To avoid including signal.h yet still be able to
handle sigsetjmp we manually define a __sigset_t structure and similarly
we define data structures to use for the intercepting setjmp/longjmp.

libsanitizer/ChangeLog:

2019-09-06  Matthew Malcomson  <matthew.malcomson@arm.com>

	* hwasan/Makefile.am: Add hwasan_setjmp.S.
	* hwasan/Makefile.in: Regenerate.
	* hwasan/hwasan_interceptors.cc (_SIGSET_NWORDS):
	(struct __sigset_t): New structure.
	(struct __jmp_buf_tag): New structure.
	(sigprocmask): Declaration of system function.
	(SIG_BLOCK): New.
	(SIG_SETMASK): New.
	(__sigjmp_save): New.
	(__hwasan_internal_longjmp): longjmp that clears shadow tags.
	(siglongjmp): New interceptors.
	(__libc_longjmp): New interceptors.
	(longjmp): New interceptors.
	* hwasan/hwasan_setjmp.S: New file.



###############     Attachment also inlined for ease of reply    ###############


diff --git a/libsanitizer/hwasan/Makefile.am b/libsanitizer/hwasan/Makefile.am
index 3754f435ce0342f5c8051f6ea44273bb811c9659..36c0fc4d9e832dfc81c72d2d05c2b327589daee2 100644
--- a/libsanitizer/hwasan/Makefile.am
+++ b/libsanitizer/hwasan/Makefile.am
@@ -13,6 +13,7 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
 toolexeclib_LTLIBRARIES = libhwasan.la
 
 hwasan_files = \
+	       hwasan_setjmp.S \
 	       hwasan_allocator.cc \
 	       hwasan.cc \
 	       hwasan_dynamic_shadow.cc \
diff --git a/libsanitizer/hwasan/Makefile.in b/libsanitizer/hwasan/Makefile.in
index f89bc9e440502fcf8072d67aa61a4b63cd490b40..472fbf7d8d2a5311db8fe4c14376f9213cdddde4 100644
--- a/libsanitizer/hwasan/Makefile.in
+++ b/libsanitizer/hwasan/Makefile.in
@@ -145,10 +145,10 @@ am__DEPENDENCIES_1 =
 libhwasan_la_DEPENDENCIES =  \
 	$(top_builddir)/sanitizer_common/libsanitizer_common.la \
 	$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
-am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
-	hwasan_interceptors.lo hwasan_linux.lo hwasan_new_delete.lo \
-	hwasan_poisoning.lo hwasan_report.lo hwasan_thread.lo \
-	hwasan_thread_list.lo
+am__objects_1 = hwasan_setjmp.lo hwasan_allocator.lo hwasan.lo \
+	hwasan_dynamic_shadow.lo hwasan_interceptors.lo \
+	hwasan_linux.lo hwasan_new_delete.lo hwasan_poisoning.lo \
+	hwasan_report.lo hwasan_thread.lo hwasan_thread_list.lo
 am_libhwasan_la_OBJECTS = $(am__objects_1)
 libhwasan_la_OBJECTS = $(am_libhwasan_la_OBJECTS)
 AM_V_lt = $(am__v_lt_@AM_V@)
@@ -174,6 +174,16 @@ DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/../depcomp
 am__depfiles_maybe = depfiles
 am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo "  CPPAS   " $@;
+am__v_CPPAS_1 = 
 CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
 	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
 LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
@@ -192,6 +202,24 @@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
 am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
 am__v_CXXLD_0 = @echo "  CXXLD   " $@;
 am__v_CXXLD_1 = 
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
 SOURCES = $(libhwasan_la_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
@@ -380,6 +408,7 @@ AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
 ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
 toolexeclib_LTLIBRARIES = libhwasan.la
 hwasan_files = \
+	       hwasan_setjmp.S \
 	       hwasan_allocator.cc \
 	       hwasan.cc \
 	       hwasan_dynamic_shadow.cc \
@@ -439,7 +468,7 @@ MAKEOVERRIDES =
 all: all-am
 
 .SUFFIXES:
-.SUFFIXES: .cc .lo .o .obj
+.SUFFIXES: .S .cc .lo .o .obj
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
 	@for dep in $?; do \
 	  case '$(am__configure_deps)' in \
@@ -522,9 +551,31 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_new_delete.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_poisoning.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_report.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_setjmp.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_thread_list.Plo@am__quote@
 
+.S.o:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@	$(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@	$(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
 .cc.o:
 @am__fastdepCXX_TRUE@	$(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
 @am__fastdepCXX_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
diff --git a/libsanitizer/hwasan/hwasan_interceptors.cc b/libsanitizer/hwasan/hwasan_interceptors.cc
index 9a0770f563b7948644cb10cdd0f88edc79f786d5..2edfba5d4ba065ca1dd09400c182962ecff5d9ca 100644
--- a/libsanitizer/hwasan/hwasan_interceptors.cc
+++ b/libsanitizer/hwasan/hwasan_interceptors.cc
@@ -284,6 +284,107 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
                                  &HwasanThreadStartFunc, A);
   return res;
 }
+
+
+#if defined(__aarch64__)
+/*
+   Setjmp and longjmp implementations are platform specific, and hence the
+   interception code is platform specific too.  As yet we've only implemented
+   the interception for AArch64.
+ */
+# define _SIGSET_NWORDS	(1024 / (8 * sizeof (unsigned long int)))
+typedef struct
+  {
+    unsigned long int __val[_SIGSET_NWORDS];
+  } __sigset_t;
+
+#undef _SIGSET_NWORDS
+typedef __sigset_t sigset_t;
+typedef unsigned long long __jmp_buf [22];
+struct __jmp_buf_tag
+  {
+    /* NOTE: The machine-dependent definitions of `__sigsetjmp'
+       assume that a `jmp_buf' begins with a `__jmp_buf' and that
+       `__mask_was_saved' follows it.  Do not move these members
+       or add others before it.  */
+    __jmp_buf __jmpbuf;		/* Calling environment.  */
+    int __mask_was_saved;	/* Saved the signal mask?  */
+    __sigset_t __saved_mask;	/* Saved signal mask.  */
+  };
+typedef struct __jmp_buf_tag jmp_buf[1];
+typedef struct __jmp_buf_tag sigjmp_buf[1];
+
+/* Get and/or change the set of blocked signals.  */
+extern "C" int sigprocmask (int __how, const sigset_t *__restrict __set,
+			sigset_t *__restrict __oset);
+#define SIG_BLOCK 0
+#define SIG_SETMASK 2
+extern "C" int __sigjmp_save (sigjmp_buf env, int savemask)
+{
+  env[0].__mask_was_saved = (savemask
+			     && sigprocmask (SIG_BLOCK, (sigset_t *) 0,
+					     (sigset_t *) &env[0].__saved_mask) == 0);
+  return 0;
+}
+
+static void __attribute__ ((always_inline))
+__hwasan_internal_longjmp (__jmp_buf env, int retval)
+{
+  /* Clear all memory tags on the stack between here and where we're going.  */
+  unsigned long long stack_pointer = env[13];
+  /* The stack pointer should never be tagged, so we don't need to clear the
+     tag for this function call.  */
+  __hwasan_handle_longjmp ((void *)stack_pointer);
+
+  /* Run code for handling a longjmp.
+     Need to use a register that isn't going to be loaded from the environment
+     buffer -- hence why we need to specify the register to use.  */
+  register int retval_tmp asm ("x1") = retval;
+  register void *env_address asm ("x0") = &env[0];
+  asm volatile (
+	"ldp	x19, x20, [%0, #0<<3];"
+	"ldp	x21, x22, [%0, #2<<3];"
+	"ldp	x23, x24, [%0, #4<<3];"
+	"ldp	x25, x26, [%0, #6<<3];"
+	"ldp	x27, x28, [%0, #8<<3];"
+	"ldp	x29, x30, [%0, #10<<3];"
+	"ldp	 d8,  d9, [%0, #14<<3];"
+	"ldp	d10, d11, [%0, #16<<3];"
+	"ldp	d12, d13, [%0, #18<<3];"
+	"ldp	d14, d15, [%0, #20<<3];"
+	"ldr	x5, [%0, #13<<3];"
+	"mov	sp, x5;"
+	/* Return the value requested to return through arguments.
+	   This should be in x1 given what we requested above.  */
+	"cmp	%1, #0;"
+	"mov	x0, #1;"
+	"csel	x0, %1, x0, ne;"
+	"br	x30;" : "+r" (env_address) : "r" (retval_tmp));
+}
+
+INTERCEPTOR(void, siglongjmp, sigjmp_buf env, int val)
+{
+  if (env[0].__mask_was_saved)
+    /* Restore the saved signal mask.  */
+    (void) sigprocmask (SIG_SETMASK,
+			(sigset_t *) &env[0].__saved_mask,
+			(sigset_t *) 0);
+  __hwasan_internal_longjmp (env[0].__jmpbuf, val);
+}
+
+INTERCEPTOR(void, __libc_longjmp, jmp_buf env, int val)
+{
+  __hwasan_internal_longjmp (env[0].__jmpbuf, val);
+}
+
+INTERCEPTOR(void, longjmp, jmp_buf env, int val)
+{
+  __hwasan_internal_longjmp (env[0].__jmpbuf, val);
+}
+#undef SIG_BLOCK
+#undef SIG_SETMASK
+
+#endif // __aarch64__
 #endif // HWASAN_WITH_INTERCEPTORS
 
 static void BeforeFork() {
@@ -302,7 +403,6 @@ INTERCEPTOR(int, fork, void) {
   return pid;
 }
 
-
 struct HwasanInterceptorContext {
   bool in_interceptor_scope;
 };
@@ -325,6 +425,11 @@ void InitializeInterceptors() {
   INTERCEPT_FUNCTION(fork);
 
 #if HWASAN_WITH_INTERCEPTORS
+#if defined(__aarch64__)
+  INTERCEPT_FUNCTION(longjmp);
+  INTERCEPT_FUNCTION(__libc_longjmp);
+  INTERCEPT_FUNCTION(siglongjmp);
+#endif
   INTERCEPT_FUNCTION(pthread_create);
 #endif
 
diff --git a/libsanitizer/hwasan/hwasan_setjmp.S b/libsanitizer/hwasan/hwasan_setjmp.S
new file mode 100644
index 0000000000000000000000000000000000000000..56add3e149abcc37ec23b8cdc8f4ba2cbc722c08
--- /dev/null
+++ b/libsanitizer/hwasan/hwasan_setjmp.S
@@ -0,0 +1,52 @@
+// We want to save the context of the calling function.
+// That requires
+// 1) No modification of the link register by this function.
+// 2) No modification of the stack pointer by this function.
+// 3) (no modification of any other saved register, but that's not really going
+// to occur, and hence isn't as much of a worry).
+//
+// There's essentially no way to ensure that the compiler will not modify the
+// stack pointer when compiling a C function.
+// Hence we have to write this function in assembly.
+
+#if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__)
+
+.macro ENTRY symbol
+  .align 2
+  .global \symbol
+  .type  \symbol\(), %function
+\symbol\():
+  .cfi_startproc
+.endm
+
+.macro END symbol
+  .cfi_endproc
+  .size \symbol, .-\symbol
+.endm
+
+ENTRY _setjmp
+mov	x1, #0
+b	1f
+END _setjmp
+
+ENTRY __sigsetjmp
+1:
+  stp	x19, x20, [x0, #0<<3]
+  stp	x21, x22, [x0, #2<<3]
+  stp	x23, x24, [x0, #4<<3]
+  stp	x25, x26, [x0, #6<<3]
+  stp	x27, x28, [x0, #8<<3]
+  stp	x29, x30, [x0, #10<<3]
+  stp	 d8,  d9, [x0, #14<<3]
+  stp	d10, d11, [x0, #16<<3]
+  stp	d12, d13, [x0, #18<<3]
+  stp	d14, d15, [x0, #20<<3]
+  mov	x2,  sp
+  str	x2,  [x0, #13<<3]
+  // We always have the second argument to __sigjmp_save (savemask) set, since
+  // the _setjmp function above has set it for us as `false`.
+  // This function is defined in hwasan_interceptors.cc
+  b	__sigjmp_save
+END __sigsetjmp
+
+#endif

-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: hwasan-implementation04.patch
URL: <http://gcc.gnu.org/pipermail/gcc-patches/attachments/20190906/6afef4d0/attachment.ksh>


More information about the Gcc-patches mailing list