From 11795fa3b366cf4d1842e99fbbc7007f7e8f4768 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Thu, 2 May 2024 17:29:00 +0200 Subject: [PATCH] IBM zSystems DFLTCC: Extend sanitizer checks Currently the DFLTCC sanitizer instrumentation is limited to MSAN-unpoisoning the parameter block. Add ASAN and MSAN checks; also MSAN-unpoison the window. Introduce the generic instrument_read(), instrument_write() and instrument_read_write() macros, that are modeled after the repsective functions in the Linux kernel. --- arch/s390/dfltcc_detail.h | 71 +++++++++++++++++++++++++++++++-------- zbuild.h | 49 +++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 14 deletions(-) diff --git a/arch/s390/dfltcc_detail.h b/arch/s390/dfltcc_detail.h index 49779d60c5..21417ca8d4 100644 --- a/arch/s390/dfltcc_detail.h +++ b/arch/s390/dfltcc_detail.h @@ -162,6 +162,30 @@ typedef enum { #define HB_BITS 15 #define HB_SIZE (1 << HB_BITS) +/* Return lengths of high (starting at param->ho) and low (starting at 0) fragments of the circular history buffer. */ +static inline void get_history_lengths(struct dfltcc_param_v0 *param, size_t *hl_high, size_t *hl_low) { + *hl_high = MIN(param->hl, HB_SIZE - param->ho); + *hl_low = param->hl - *hl_high; +} + +/* Notify instrumentation about an upcoming read/write access to the circular history buffer. */ +static inline void instrument_read_write_hist(struct dfltcc_param_v0 *param, void *hist) { + size_t hl_high, hl_low; + + get_history_lengths(param, &hl_high, &hl_low); + instrument_read_write(hist + param->ho, hl_high); + instrument_read_write(hist, hl_low); +} + +/* Notify MSan about a completed write to the circular history buffer. */ +static inline void msan_unpoison_hist(struct dfltcc_param_v0 *param, void *hist) { + size_t hl_high, hl_low; + + get_history_lengths(param, &hl_high, &hl_low); + __msan_unpoison(hist + param->ho, hl_high); + __msan_unpoison(hist, hl_low); +} + static inline dfltcc_cc dfltcc(int fn, void *param, unsigned char **op1, size_t *len1, z_const unsigned char **op2, size_t *len2, void *hist) { @@ -170,14 +194,33 @@ static inline dfltcc_cc dfltcc(int fn, void *param, size_t t3 = len1 ? *len1 : 0; z_const unsigned char *t4 = op2 ? *op2 : NULL; size_t t5 = len2 ? *len2 : 0; - Z_REGISTER int r0 __asm__("r0") = fn; - Z_REGISTER void *r1 __asm__("r1") = param; - Z_REGISTER unsigned char *r2 __asm__("r2") = t2; - Z_REGISTER size_t r3 __asm__("r3") = t3; - Z_REGISTER z_const unsigned char *r4 __asm__("r4") = t4; - Z_REGISTER size_t r5 __asm__("r5") = t5; + Z_REGISTER int r0 __asm__("r0"); + Z_REGISTER void *r1 __asm__("r1"); + Z_REGISTER unsigned char *r2 __asm__("r2"); + Z_REGISTER size_t r3 __asm__("r3"); + Z_REGISTER z_const unsigned char *r4 __asm__("r4"); + Z_REGISTER size_t r5 __asm__("r5"); int cc; + /* Insert pre-instrumentation for DFLTCC. */ + switch (fn & DFLTCC_FN_MASK) { + case DFLTCC_QAF: + instrument_write(param, DFLTCC_SIZEOF_QAF); + break; + case DFLTCC_GDHT: + instrument_read_write(param, DFLTCC_SIZEOF_GDHT_V0); + instrument_read(t4, t5); + break; + case DFLTCC_CMPR: + case DFLTCC_XPND: + instrument_read_write(param, DFLTCC_SIZEOF_CMPR_XPND_V0); + instrument_read(t4, t5); + instrument_write(t2, t3); + instrument_read_write_hist(param, hist); + break; + } + + r0 = fn; r1 = param; r2 = t2; r3 = t3; r4 = t4; r5 = t5; __asm__ volatile( #ifdef HAVE_SYS_SDT_H STAP_PROBE_ASM(zlib, dfltcc_entry, STAP_PROBE_ASM_TEMPLATE(5)) @@ -201,6 +244,7 @@ static inline dfltcc_cc dfltcc(int fn, void *param, : "cc", "memory"); t2 = r2; t3 = r3; t4 = r4; t5 = r5; + /* Insert post-instrumentation for DFLTCC. */ switch (fn & DFLTCC_FN_MASK) { case DFLTCC_QAF: __msan_unpoison(param, DFLTCC_SIZEOF_QAF); @@ -211,10 +255,12 @@ static inline dfltcc_cc dfltcc(int fn, void *param, case DFLTCC_CMPR: __msan_unpoison(param, DFLTCC_SIZEOF_CMPR_XPND_V0); __msan_unpoison(orig_t2, t2 - orig_t2 + (((struct dfltcc_param_v0 *)param)->sbb == 0 ? 0 : 1)); + msan_unpoison_hist(param, hist); break; case DFLTCC_XPND: __msan_unpoison(param, DFLTCC_SIZEOF_CMPR_XPND_V0); __msan_unpoison(orig_t2, t2 - orig_t2); + msan_unpoison_hist(param, hist); break; } @@ -297,12 +343,9 @@ static inline void append_history(struct dfltcc_param_v0 *param, unsigned char * static inline void get_history(struct dfltcc_param_v0 *param, const unsigned char *history, unsigned char *buf) { - if (param->ho + param->hl <= HB_SIZE) - /* Circular history buffer does not wrap - copy one chunk */ - memcpy(buf, history + param->ho, param->hl); - else { - /* Circular history buffer wraps - copy two chunks */ - memcpy(buf, history + param->ho, HB_SIZE - param->ho); - memcpy(buf + HB_SIZE - param->ho, history, param->ho + param->hl - HB_SIZE); - } + size_t hl_high, hl_low; + + get_history_lengths(param, &hl_high, &hl_low); + memcpy(buf, history + param->ho, hl_high); + memcpy(buf + hl_high, history, hl_low); } diff --git a/zbuild.h b/zbuild.h index d550b4c582..206eed2312 100644 --- a/zbuild.h +++ b/zbuild.h @@ -246,6 +246,31 @@ # endif #endif +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define Z_ADDRESS_SANITIZER 1 +# endif +#elif defined(__SANITIZE_ADDRESS__) +# define Z_ADDRESS_SANITIZER 1 +#endif + +/* + * __asan_loadN() and __asan_storeN() calls are inserted by compilers in order to check memory accesses. + * They can be called manually too, with the following caveats: + * gcc says: "warning: implicit declaration of function ‘...’" + * g++ says: "error: new declaration ‘...’ ambiguates built-in declaration ‘...’" + * Accommodate both. + */ +#ifdef Z_ADDRESS_SANITIZER +#ifndef __cplusplus +void __asan_loadN(void *, long); +void __asan_storeN(void *, long); +#endif +#else +# define __asan_loadN(a, size) do { Z_UNUSED(a); Z_UNUSED(size); } while (0) +# define __asan_storeN(a, size) do { Z_UNUSED(a); Z_UNUSED(size); } while (0) +#endif + #if defined(__has_feature) # if __has_feature(memory_sanitizer) # define Z_MEMORY_SANITIZER 1 @@ -254,7 +279,31 @@ #endif #ifndef Z_MEMORY_SANITIZER +# define __msan_check_mem_is_initialized(a, size) do { Z_UNUSED(a); Z_UNUSED(size); } while (0) # define __msan_unpoison(a, size) do { Z_UNUSED(a); Z_UNUSED(size); } while (0) #endif +/* Notify sanitizer runtime about an upcoming read access. */ +#define instrument_read(a, size) do { \ + void *__a = (void *)(a); \ + long __size = size; \ + __asan_loadN(__a, __size); \ + __msan_check_mem_is_initialized(__a, __size); \ +} while (0) + +/* Notify sanitizer runtime about an upcoming write access. */ +#define instrument_write(a, size) do { \ + void *__a = (void *)(a); \ + long __size = size; \ + __asan_storeN(__a, __size); \ +} while (0) + +/* Notify sanitizer runtime about an upcoming read/write access. */ +#define instrument_read_write(a, size) do { \ + void *__a = (void *)(a); \ + long __size = size; \ + __asan_storeN(__a, __size); \ + __msan_check_mem_is_initialized(__a, __size); \ +} while (0) + #endif