[PATCH 12/14] KVM: arm64: Detect arithmetic overflow is inside hyp/nVHE.

George-Aurelian Popescu
2020-09-14T17:30:36Z

From: George Popescu <georgepope@google.com>

Whenever an arithmetic overflow: addition, subtraction, multiplication,
division or negating happens inside the hyp/nVHE code,
an __ubsan_handle_*_overflow is called.

All the overflow handlers are sharing the same structure called
overflow_data and they use the write_overflow_data(*) function to store
the data to the buffer.

When decapsulating the data inside the kernel, the right handler is
called by checking the "op" field, which stores the arithmetic
opperator.

Signed-off-by: George Popescu <georgepope@google.com>
---
arch/arm64/include/asm/kvm_ubsan.h | 10 ++++++--
arch/arm64/kvm/hyp/nvhe/ubsan.c | 40 ++++++++++++++++++++++++++----
arch/arm64/kvm/kvm_ubsan_buffer.c | 18 ++++++++++++++
3 files changed, 61 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/include/asm/kvm_ubsan.h b/arch/arm64/include/asm/kvm_ubsan.h
index a9f499f4ef6d..4abdbff38f79 100644
--- a/arch/arm64/include/asm/kvm_ubsan.h
+++ b/arch/arm64/include/asm/kvm_ubsan.h
@@ -24,7 +24,8 @@ struct kvm_ubsan_info {
UBSAN_UNREACHABLE_DATA,
UBSAN_SHIFT_OUT_OF_BOUNDS,
UBSAN_INVALID_DATA,
- UBSAN_TYPE_MISMATCH
+ UBSAN_TYPE_MISMATCH,
+ UBSAN_OVERFLOW_DATA
} type;
union {
struct out_of_bounds_data out_of_bounds_data;
@@ -32,6 +33,7 @@ struct kvm_ubsan_info {
struct shift_out_of_bounds_data shift_out_of_bounds_data;
struct invalid_value_data invalid_value_data;
struct type_mismatch_data type_mismatch_data;
+ struct overflow_data overflow_data;
};
union {
struct ubsan_values u_val;
@@ -44,4 +46,8 @@ void __ubsan_handle_builtin_unreachable(void *_data);
void __ubsan_handle_shift_out_of_bounds(void *_data, void *lhs, void *rhs);
void __ubsan_handle_load_invalid_value(void *_data, void *val);
void __ubsan_handle_type_mismatch(struct type_mismatch_data *_data, void *ptr);
-
+void __ubsan_handle_add_overflow(void *data, void *lhs, void *rhs);
+void __ubsan_handle_sub_overflow(void *data, void *lhs, void *rhs);
+void __ubsan_handle_mul_overflow(void *data, void *lhs, void *rhs);
+void __ubsan_handle_negate_overflow(void *_data, void *old_val);
+void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs);
diff --git a/arch/arm64/kvm/hyp/nvhe/ubsan.c b/arch/arm64/kvm/hyp/nvhe/ubsan.c
index c99d919105aa..dd2dae60864f 100644
--- a/arch/arm64/kvm/hyp/nvhe/ubsan.c
+++ b/arch/arm64/kvm/hyp/nvhe/ubsan.c
@@ -47,15 +47,45 @@ static void write_type_mismatch_data(struct type_mismatch_data_common *data, voi
}
}

-void __ubsan_handle_add_overflow(void *_data, void *lhs, void *rhs) {}
+static void write_overflow_data(struct overflow_data *data, void *lval, void *rval, char op)
+{
+ struct kvm_ubsan_info *slot = kvm_ubsan_buffer_next_slot();
+
+ if (slot) {
+ slot->type = UBSAN_OVERFLOW_DATA;
+ slot->overflow_data = *data;
+ slot->u_val.op = op;
+ slot->u_val.lval = lval;
+ if (op != '!')
+ slot->u_val.rval = rval;
+ }
+}
+
+void __ubsan_handle_add_overflow(void *_data, void *lhs, void *rhs)
+{
+ write_overflow_data(_data, lhs, rhs, '+');
+}

-void __ubsan_handle_sub_overflow(void *_data, void *lhs, void *rhs) {}
+void __ubsan_handle_sub_overflow(void *_data, void *lhs, void *rhs)
+{
+ write_overflow_data(_data, lhs, rhs, '-');
+}

-void __ubsan_handle_mul_overflow(void *_data, void *lhs, void *rhs) {}
+void __ubsan_handle_mul_overflow(void *_data, void *lhs, void *rhs)
+{
+ write_overflow_data(_data, lhs, rhs, '*');
+}

-void __ubsan_handle_negate_overflow(void *_data, void *old_val) {}
+void __ubsan_handle_negate_overflow(void *_data, void *old_val)
+{
+ write_overflow_data(_data, old_val, NULL, '!');
+}
+
+void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs)
+{
+ write_overflow_data(_data, lhs, rhs, '/');
+}

-void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs) {}

void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, void *ptr)
{
diff --git a/arch/arm64/kvm/kvm_ubsan_buffer.c b/arch/arm64/kvm/kvm_ubsan_buffer.c
index 21c242c92f0a..bd847ac1321e 100644
--- a/arch/arm64/kvm/kvm_ubsan_buffer.c
+++ b/arch/arm64/kvm/kvm_ubsan_buffer.c
@@ -40,6 +40,24 @@ void __kvm_check_ubsan_data(struct kvm_ubsan_info *slot)
__ubsan_handle_type_mismatch(&slot->type_mismatch_data,
slot->u_val.lval);
break;
+ case UBSAN_OVERFLOW_DATA:
+ if (slot->u_val.op == '/') {
+ __ubsan_handle_divrem_overflow(&slot->overflow_data,
+ slot->u_val.lval, slot->u_val.rval);
+ } else if (slot->u_val.op == '!') {
+ __ubsan_handle_negate_overflow(&slot->overflow_data,
+ slot->u_val.lval);
+ } else if (slot->u_val.op == '+') {
+ __ubsan_handle_add_overflow(&slot->overflow_data,
+ slot->u_val.lval, slot->u_val.rval);
+ } else if (slot->u_val.op == '-') {
+ __ubsan_handle_sub_overflow(&slot->overflow_data,
+ slot->u_val.lval, slot->u_val.rval);
+ } else if (slot->u_val.op == '*') {
+ __ubsan_handle_mul_overflow(&slot->overflow_data,
+ slot->u_val.lval, slot->u_val.rval);
+ }
+ break;
}
}

--
2.28.0.618.gf4bc123cb7-goog