X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/a991bd8d3e7fe02dbca0644054bab73c5b75324a..refs/heads/master:/san/ubsan.c diff --git a/san/ubsan.c b/san/ubsan.c index d8e42d708..d245d424c 100644 --- a/san/ubsan.c +++ b/san/ubsan.c @@ -28,6 +28,7 @@ #include #include +#include #include #include "ubsan.h" @@ -35,14 +36,27 @@ static const bool ubsan_print = false; static const uint32_t line_acquired = 0x80000000UL; static const char *get_type_check_kind(uint8_t kind); -static size_t -format_loc(struct san_src_loc *loc, char *dst, size_t sz) +static void +ubsan_buf_log(struct ubsan_buf *ub, const char *fmt, ...) { - return scnprintf(dst, sz, ", file:\"%s\", line:%d, column:%d },\n", - loc->filename, - loc->line & ~line_acquired, - loc->col - ); + va_list ap; + + va_start(ap, fmt); + int n = vscnprintf(ub->ub_buf + ub->ub_logged, ub->ub_buf_size - ub->ub_logged, fmt, ap); + va_end(ap); + + ub->ub_logged += n; + assert(ub->ub_logged <= ub->ub_buf_size); +} + +static void +ubsan_buf_log_loc(struct ubsan_buf *ub, const char *desc, struct san_src_loc *loc) +{ + ubsan_buf_log(ub, "%s:{ file:\"%s\", line:%d, column:%d }", + desc, + loc->filename, + loc->line & ~line_acquired, + loc->col); } /* @@ -70,33 +84,30 @@ overflow_str[] = { NULL }; -static size_t -format_overflow(struct ubsan_violation *v, char *buf, size_t sz) +static void +format_overflow(struct ubsan_violation *v, struct ubsan_buf *ub) { struct san_type_desc *ty = v->overflow->ty; - return scnprintf(buf, sz, - "problem:\"%s overflow\", op:\"%s\", ty:\"%s\", width:%d, lhs:0x%llx, rhs:0x%llx, ", - ty->issigned ? "signed" : "unsigned", - overflow_str[v->ubsan_type], - ty->name, - 1 << ty->width, - v->lhs, - v->rhs - ); + ubsan_buf_log(ub, + "problem:\"%s overflow\", op:\"%s\", ty:\"%s\", width:%d, lhs:0x%llx, rhs:0x%llx", + ty->issigned ? "signed" : "unsigned", + overflow_str[v->ubsan_type], + ty->name, + 1 << ty->width, + v->lhs, + v->rhs + ); } -static size_t -format_shift(struct ubsan_violation *v, char *buf, size_t sz) +static void +format_shift(struct ubsan_violation *v, struct ubsan_buf *ub) { - size_t n = 0; struct san_type_desc *l = v->shift->lhs_t; struct san_type_desc *r = v->shift->rhs_t; - n += scnprintf(buf + n, sz - n, "problem:\"bad shift\", "); - n += scnprintf(buf + n, sz - n, "lhs:0x%llx, lty:\"%s\", lsigned:%d, lwidth:%d, ", v->lhs, l->name, l->issigned, 1 << l->width); - n += scnprintf(buf + n, sz - n, "rhs:0x%llx, rty:\"%s\", rsigned:%d, rwidth:%d, ", v->rhs, r->name, r->issigned, 1 << r->width); - - return n; + ubsan_buf_log(ub, "problem:\"bad shift\", "); + ubsan_buf_log(ub, "lhs:0x%llx, lty:\"%s\", lsigned:%d, lwidth:%d, ", v->lhs, l->name, l->issigned, 1 << l->width); + ubsan_buf_log(ub, "rhs:0x%llx, rty:\"%s\", rsigned:%d, rwidth:%d", v->rhs, r->name, r->issigned, 1 << r->width); } static const char * const @@ -114,89 +125,196 @@ get_type_check_kind(uint8_t kind) : "some"; } -static size_t -format_type_mismatch(struct ubsan_violation *v, char *buf, size_t sz) +static void +format_type_mismatch(struct ubsan_violation *v, struct ubsan_buf *ub) { - size_t n = 0; size_t alignment = 1 << v->align->align; void *ptr = (void*)v->lhs; - const char * kind = get_type_check_kind(v->align->kind); + const char *kind = get_type_check_kind(v->align->kind); + if (NULL == ptr) { //null pointer use - n += scnprintf(buf + n, sz - n, "problem:\"%s NULL pointer\", ty:\"%s\", ", kind, v->align->ty->name); + ubsan_buf_log(ub, "problem:\"%s NULL pointer\", ty:\"%s\"", kind, v->align->ty->name); } else if (alignment && ((uintptr_t)ptr & (alignment - 1))) { //misaligned pointer use - n += scnprintf(buf + n, sz - n, "problem:\"%s mis-aligned\", address:%p, ty:\"%s\", ", kind, (void*)v->lhs, v->align->ty->name); - n += scnprintf(buf + n, sz - n, "required_alignment:%d, ", 1 << v->align->align); + ubsan_buf_log(ub, "problem:\"%s mis-aligned\", address:%p, ty:\"%s\", ", + kind, (void*)v->lhs, v->align->ty->name); + ubsan_buf_log(ub, "required_alignment:%d", 1 << v->align->align); } else { //insufficient object size - n += scnprintf(buf + n, sz - n, "problem:\"%s insufficient object size\", ty:\"%s\", address:%p, ", + ubsan_buf_log(ub, "problem:\"%s insufficient object size\", ty:\"%s\", address:%p", kind, v->align->ty->name, ptr); } - - return n; } -static size_t -format_oob(struct ubsan_violation *v, char *buf, size_t sz) +static void +format_oob(struct ubsan_violation *v, struct ubsan_buf *ub) { - size_t n = 0; struct san_type_desc *aty = v->oob->array_ty; struct san_type_desc *ity = v->oob->index_ty; uintptr_t idx = v->lhs; - n += scnprintf(buf + n, sz - n, "problem:\"OOB array access\", "); - n += scnprintf(buf + n, sz - n, "idx:%ld, ", idx); - n += scnprintf(buf + n, sz - n, "aty:\"%s\", asigned:%d, awidth:%d, ", aty->name, aty->issigned, 1 << aty->width); - n += scnprintf(buf + n, sz - n, "ity:\"%s\", isigned:%d, iwidth:%d, ", ity->name, ity->issigned, 1 << ity->width); + ubsan_buf_log(ub, "problem:\"OOB array access\", "); + ubsan_buf_log(ub, "idx:%ld, ", idx); + ubsan_buf_log(ub, "aty:\"%s\", asigned:%d, awidth:%d, ", aty->name, aty->issigned, 1 << aty->width); + ubsan_buf_log(ub, "ity:\"%s\", isigned:%d, iwidth:%d", ity->name, ity->issigned, 1 << ity->width); +} - return n; +static void +format_nullability_arg(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + struct ubsan_nullability_arg_desc *data = v->nonnull_arg; + + const int arg_index = data->arg_index; + const char *attr_type = v->lhs ? "nonnull attribute" : "_Nonnull annotation"; + + ubsan_buf_log(ub, "problem:\"null in argument %d declared with %s\", ", arg_index, attr_type); + ubsan_buf_log_loc(ub, "declared", &data->attr_loc); } -static size_t -format_load_invalid_value(struct ubsan_violation *v, char *buf, size_t sz) +static void +format_nonnull_return(struct ubsan_violation *v, struct ubsan_buf *ub) { - return scnprintf(buf, sz, "problem:\"invalid value load\", type:\"%s\", value:0x%llx", - v->invalid->type->name, v->lhs); + struct san_src_loc *declaration = (struct san_src_loc *)v->rhs; + const char *return_type = v->lhs ? "returns_nonnull attribute" : "_Nonnull return type annotation"; + + ubsan_buf_log(ub, "problem:\"null returned from function declared with %s\", ", return_type); + ubsan_buf_log_loc(ub, "declared", declaration); } -size_t -ubsan_format(struct ubsan_violation *v, char *buf, size_t sz) +static void +format_load_invalid_value(struct ubsan_violation *v, struct ubsan_buf *ub) { - size_t n = scnprintf(buf, sz, "{ "); + ubsan_buf_log(ub, "problem:\"invalid value load\", type:\"%s\", value:0x%llx", + v->invalid->type->name, v->lhs); +} + +static void +format_missing_return(struct ubsan_violation *v __unused, struct ubsan_buf *ub) +{ + ubsan_buf_log(ub, "problem:\"no value returned from value-returning function\""); +} + +static void +format_float_cast_overflow(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + struct ubsan_float_desc *data = v->flt; + /* + * Cannot print out offending value (e.g. using %A, %f and so on) as kernel logging + * does not support float types (yet). + */ + ubsan_buf_log(ub, "problem:\"%s type value outside the range of %s\"", + data->type_from->name, data->type_to->name); +} + +static const char * +get_implicit_conv_type(unsigned char kind) +{ + static const char * const conv_types[] = { + "integer truncation", + "unsigned integer truncation", + "signed integer truncation", + "integer sign change", + "signed integer truncation or sign change" + }; + static const size_t conv_types_cnt = sizeof(conv_types) / sizeof(conv_types[0]); + + return kind < conv_types_cnt ? conv_types[kind] : "unknown implicit integer conversion"; +} + +static void +format_implicit_conversion(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + struct ubsan_implicit_conv_desc *data = v->implicit; + struct san_type_desc *from = data->type_from; + struct san_type_desc *to = data->type_to; + + ubsan_buf_log(ub, "problem:\"%s\", ", get_implicit_conv_type(data->kind)); + ubsan_buf_log(ub, "src value:%#llx type:\"%s\", signed:%d, width:%d, ", + v->lhs, from->name, from->issigned, 1 << from->width); + ubsan_buf_log(ub, "dst value:%#llx type:\"%s\", signed:%d, width:%d", + v->rhs, to->name, to->issigned, 1 << to->width); +} + +static void +format_function_type_mismatch(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + struct ubsan_func_type_mismatch_desc *data = v->func_mismatch; + ubsan_buf_log(ub, "problem:\"indirect function call through %p of a wrong type %s\"", + (void *)v->lhs, data->type->name); +} + +static void +format_vla_bound_not_positive(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + struct ubsan_vla_bound_desc *data = v->vla_bound; + ubsan_buf_log(ub, "problem:\"VLA %s bound %#llx not positive\"", data->type->name, v->lhs); +} + +static void +format_invalid_builtin(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + ubsan_buf_log(ub, "problem:\"passing invalid zero argument to %s\"", + v->invalid_builtin->kind == 0 ? "ctz()" : "clz()"); +} + +void +ubsan_format(struct ubsan_violation *v, struct ubsan_buf *ub) +{ + ubsan_buf_log(ub, "{ "); switch (v->ubsan_type) { case UBSAN_OVERFLOW_add ... UBSAN_OVERFLOW_negate: - n += format_overflow(v, buf + n, sz - n); + format_overflow(v, ub); break; case UBSAN_UNREACHABLE: - n += scnprintf(buf + n, sz - n, "problem:\"unreachable\", "); + ubsan_buf_log(ub, "problem:\"unreachable\", "); break; case UBSAN_SHIFT: - n += format_shift(v, buf + n, sz - n); + format_shift(v, ub); break; case UBSAN_TYPE_MISMATCH: - n += format_type_mismatch(v, buf + n, sz - n); + format_type_mismatch(v, ub); break; case UBSAN_POINTER_OVERFLOW: - n += scnprintf(buf + n, sz - n, "problem:\"pointer overflow\", before:0x%llx, after:0x%llx, ", v->lhs, v->rhs); + ubsan_buf_log(ub, "problem:\"pointer overflow\", before:0x%llx, after:0x%llx", v->lhs, v->rhs); break; case UBSAN_OOB: - n += format_oob(v, buf + n, sz - n); + format_oob(v, ub); break; - case UBSAN_LOAD_INVALID_VALUE: - n += format_load_invalid_value(v, buf + n, sz - n); + case UBSAN_NULLABILITY_ARG: + format_nullability_arg(v, ub); + break; + case UBSAN_NULLABILITY_RETURN: + format_nonnull_return(v, ub); + break; + case UBSAN_MISSING_RETURN: + format_missing_return(v, ub); + break; + case UBSAN_FLOAT_CAST_OVERFLOW: + format_float_cast_overflow(v, ub); + break; + case UBSAN_IMPLICIT_CONVERSION: + format_implicit_conversion(v, ub); break; - case UBSAN_GENERIC: - n += scnprintf(buf + n, sz - n, "problem:\"generic\", function:\"%s\", ", v->func); + case UBSAN_FUNCTION_TYPE_MISMATCH: + format_function_type_mismatch(v, ub); + break; + case UBSAN_VLA_BOUND_NOT_POSITIVE: + format_vla_bound_not_positive(v, ub); + break; + case UBSAN_INVALID_BUILTIN: + format_invalid_builtin(v, ub); + break; + case UBSAN_LOAD_INVALID_VALUE: + format_load_invalid_value(v, ub); break; default: panic("unknown violation"); } - n += format_loc(v->loc, buf + n, sz - n); - - return n; + ubsan_buf_log_loc(ub, ", found", v->loc); + ubsan_buf_log(ub, " },\n"); } enum UBFatality { Fatal, FleshWound }; @@ -212,10 +330,13 @@ ubsan_handle(struct ubsan_violation *v, enum UBFatality fatality) ubsan_log_append(v); if (ubsan_print || (fatality == Fatal)) { - const size_t sz = 256; - static char buf[sz]; - buf[0] = '\0'; - ubsan_format(v, buf, sz); + static char buf[256] = { 0 }; + struct ubsan_buf ubsan_buf = { + .ub_logged = 0, + .ub_buf_size = sizeof(buf), + .ub_buf = buf + }; + ubsan_format(v, &ubsan_buf); printf("UBSan: %s", buf); } } @@ -299,6 +420,146 @@ __ubsan_handle_out_of_bounds_abort(struct ubsan_oob_desc *desc, uint64_t idx) ubsan_handle(&v, Fatal); } +void +__ubsan_handle_nullability_arg(struct ubsan_nullability_arg_desc *desc) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_ARG, 0, 0, .nonnull_arg = desc, &desc->loc }; + ubsan_handle(&v, FleshWound); +} + +void +__ubsan_handle_nullability_arg_abort(struct ubsan_nullability_arg_desc *desc) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_ARG, 0, 0, .nonnull_arg = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_nonnull_arg(struct ubsan_nullability_arg_desc *desc) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_ARG, 1, 0, .nonnull_arg = desc, &desc->loc }; + ubsan_handle(&v, FleshWound); +} + +void +__ubsan_handle_nonnull_arg_abort(struct ubsan_nullability_arg_desc *desc) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_ARG, 1, 0, .nonnull_arg = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_nullability_return_v1(struct ubsan_nullability_ret_desc *desc, uint64_t declaration) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_RETURN, 0, (uint64_t)&desc->loc, .nonnull_ret = desc, (struct san_src_loc *)declaration }; + ubsan_handle(&v, FleshWound); +} + +void +__ubsan_handle_nullability_return_v1_abort(struct ubsan_nullability_ret_desc *desc, uint64_t declaration) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_RETURN, 0, (uint64_t)&desc->loc, .nonnull_ret = desc, (struct san_src_loc *)declaration }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_nonnull_return_v1(struct ubsan_nullability_ret_desc *desc, uint64_t declaration) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_RETURN, 1, (uint64_t)&desc->loc, .nonnull_ret = desc, (struct san_src_loc *)declaration }; + ubsan_handle(&v, FleshWound); +} + +void +__ubsan_handle_nonnull_return_v1_abort(struct ubsan_nullability_ret_desc *desc, uint64_t declaration) +{ + struct ubsan_violation v = { UBSAN_NULLABILITY_RETURN, 1, (uint64_t)&desc->loc, .nonnull_ret = desc, (struct san_src_loc *)declaration }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_missing_return(struct ubsan_missing_ret_desc *desc) +{ + struct ubsan_violation v = { UBSAN_MISSING_RETURN, 0, 0, .missing_ret = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_missing_return_abort(struct ubsan_missing_ret_desc *desc) +{ + struct ubsan_violation v = { UBSAN_MISSING_RETURN, 0, 0, .missing_ret = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_float_cast_overflow(struct ubsan_float_desc *desc, uint64_t value) +{ + struct ubsan_violation v = { UBSAN_FLOAT_CAST_OVERFLOW, value, 0, .flt = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_float_cast_overflow_abort(struct ubsan_float_desc *desc, uint64_t value) +{ + struct ubsan_violation v = { UBSAN_FLOAT_CAST_OVERFLOW, value, 0, .flt = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_implicit_conversion(struct ubsan_implicit_conv_desc *desc, uint64_t from, uint64_t to) +{ + struct ubsan_violation v = { UBSAN_IMPLICIT_CONVERSION, from, to, .implicit = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_implicit_conversion_abort(struct ubsan_implicit_conv_desc *desc, uint64_t from, uint64_t to) +{ + struct ubsan_violation v = { UBSAN_IMPLICIT_CONVERSION, from, to, .implicit = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_function_type_mismatch(struct ubsan_func_type_mismatch_desc *desc, uint64_t func) +{ + struct ubsan_violation v = { UBSAN_FUNCTION_TYPE_MISMATCH, func, 0, .func_mismatch = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_function_type_mismatch_abort(struct ubsan_func_type_mismatch_desc *desc, uint64_t func) +{ + struct ubsan_violation v = { UBSAN_FUNCTION_TYPE_MISMATCH, func, 0, .func_mismatch = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_vla_bound_not_positive(struct ubsan_vla_bound_desc *desc, uint64_t length) +{ + struct ubsan_violation v = { UBSAN_VLA_BOUND_NOT_POSITIVE, length, 0, .vla_bound = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_vla_bound_not_positive_abort(struct ubsan_vla_bound_desc *desc, uint64_t length) +{ + struct ubsan_violation v = { UBSAN_VLA_BOUND_NOT_POSITIVE, length, 0, .vla_bound = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_invalid_builtin(struct ubsan_invalid_builtin *desc) +{ + struct ubsan_violation v = { UBSAN_INVALID_BUILTIN, 0, 0, .invalid_builtin = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + +void +__ubsan_handle_invalid_builtin_abort(struct ubsan_invalid_builtin *desc) +{ + struct ubsan_violation v = { UBSAN_INVALID_BUILTIN, 0, 0, .invalid_builtin = desc, &desc->loc }; + ubsan_handle(&v, Fatal); +} + void __ubsan_handle_load_invalid_value(struct ubsan_load_invalid_desc *desc, uint64_t invalid_value) { @@ -312,26 +573,3 @@ __ubsan_handle_load_invalid_value_abort(struct ubsan_load_invalid_desc *desc, ui struct ubsan_violation v = { UBSAN_LOAD_INVALID_VALUE, invalid_value, 0, .invalid = desc, &desc->loc }; ubsan_handle(&v, Fatal); } - -#define DEFINE_GENERIC(check) \ - void __ubsan_handle_##check (struct san_src_loc* loc) \ - { \ - struct ubsan_violation v = { UBSAN_GENERIC, 0, 0, .func = __func__, loc }; \ - ubsan_handle(&v, FleshWound); \ - } \ - void __ubsan_handle_##check##_abort(struct san_src_loc* loc) \ - { \ - struct ubsan_violation v = { UBSAN_GENERIC, 0, 0, .func = __func__, loc }; \ - ubsan_handle(&v, Fatal); \ - } - -DEFINE_GENERIC(invalid_builtin) -DEFINE_GENERIC(nonnull_arg) -DEFINE_GENERIC(vla_bound_not_positive) -DEFINE_GENERIC(float_cast_overflow) -DEFINE_GENERIC(function_type_mismatch) -DEFINE_GENERIC(missing_return) -DEFINE_GENERIC(nonnull_return) -DEFINE_GENERIC(nullability_arg) -DEFINE_GENERIC(nullability_return) -DEFINE_GENERIC(implicit_conversion)