Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ubsan.c

  Sprache: C
 

/*
 * Copyright (c) 2019 Google Inc. All rights reserved
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */


#include "ubsan.h"

#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <trusty/string.h>

/*
 * in_ubsan_* is used to track whether we are currently processing a UBSan
 * report. This is useful information so that if UBSan gets tripped again
 * (due to e.g. a bug in printf, or the logging code) we don't sit in an
 * infinite recursion trying to report the bug over and over.
 */

static bool in_ubsan_get(void);
static void in_ubsan_set(bool);

#ifdef TRUSTY_USERSPACE
/* TODO Once TLS is available, make this __thread */
static bool in_ubsan = false;
static inline bool in_ubsan_get(void) {
    return in_ubsan;
}
static inline void in_ubsan_set(bool val) {
    in_ubsan = val;
}
#else
/*
 * Single copy of in_ubsan for when we don't have a current thread, e.g.,
 * during early boot
 */

static bool in_ubsan_early = false;

#include <kernel/thread.h>
static inline bool in_ubsan_get(void) {
    thread_t* curr = get_current_thread();
    return curr ? thread_tls_get(curr, TLS_ENTRY_UBSAN) : in_ubsan_early;
}
static inline void in_ubsan_set(bool val) {
    thread_t* curr = get_current_thread();
    if (curr) {
        thread_tls_set(curr, TLS_ENTRY_UBSAN, val);
    } else {
        in_ubsan_early = val;
    }
}
#endif

#define VALUE_RENDER_SIZE 64
#define DETAIL_RENDER_SIZE 1024

static int64_t val_signed(const struct type_descriptor* type,
                          value_handle_t val) {
    if (type_is_inline(type)) {
        /* Sign extend if smaller than ssize_t-bits */
        size_t undefined_bits = sizeof(size_t) * 8 - type_width_bits(type);
        val <<= undefined_bits;
        ssize_t out = (ssize_t)val;
        out >>= undefined_bits;
        return out;
    } else {
        /*
         * This truncates, but we don't have a good way to deal with
         * ints larger than 64 bits and it at least gets *some* data.
         *
         * For values larger than 64 bits, this will also have the wrong
         * sign.
         */

        return *(int64_t*)val;
    }
}

static uint64_t val_unsigned(const struct type_descriptor* type,
                             value_handle_t val) {
    if (type_is_inline(type)) {
        return (uint64_t)val;
    } else {
        /* This truncates, but gets some data out */
        return *(uint64_t*)val;
    }
}

static void render_val(char* out,
                       size_t out_size,
                       const struct type_descriptor* type,
                       value_handle_t val) {
    size_t width = type_width_bits(type);
    if (type_is_signed_integer(type)) {
        if (width > sizeof(int64_t) * 8) {
            size_t warn_len = scnprintf(out, out_size,
                                        "~int%zu_t->int64_t:truncated ", width);
            out += warn_len;
            out_size -= warn_len;
        }
        scnprintf(out, out_size, "%" PRId64, val_signed(type, val));
    } else if (type_is_unsigned_integer(type)) {
        if (width > sizeof(uint64_t) * 8) {
            size_t warn_len = scnprintf(
                    out, out_size, "~uint%zu_t->uint64_t:truncated ", width);
            out += warn_len;
            out_size -= warn_len;
        }
        scnprintf(out, out_size, "%" PRIu64, val_unsigned(type, val));
    } else if (type_is_float(type)) {
        /*
         * Printing floating point correctly requires a more powerful printf
         * which may not be available, and printing large floats will pull in
         * softfloat support.
         * Since it is unlikely the exact value of a float triggering a
         * sanitizer will be important, we don't format it.
         */

        scnprintf(out, out_size, "<floating point value>");
    } else {
        scnprintf(out, out_size, "<value with unknown type>");
    }
}

static void log(struct source_location* location,
                const char* kind,
                const char* details) {
    fprintf(stderr, "UBSan: (%s) %s:%d:%d\nDetails: %s\n", kind,
            location->filename, location->line, location->column, details);
}

static void ubsan_fail(const char* msg) {
#ifdef TRUSTY_USERSPACE
    fprintf(stderr, "ubsan panic: %s\n", msg);
    *(volatile char*)0 = 0;
#else
    panic("%s\n", msg);
#endif
}

static bool start(void) {
    if (in_ubsan_get()) {
        return false;
    }
    in_ubsan_set(true);
    return true;
}

static void finish(void) {
    assert(in_ubsan_get());
    ubsan_fail("UBSan violation");
}

/*
 * UBSAN_START should be used at the beginning of each ubsan handler.
 * It will abort if we are already processing a UBSan report, and set the
 * flag if we are.
 */

#define UBSAN_START \
    if (!start()) { \
        return;     \
    }

/*
 * UBSAN_FINISH should be used at the end of each ubsan handler.
 * It will mark us as having left the handler, and terminate due to the error
 * report.
 */

#define UBSAN_FINISH finish();

static void handle_overflow(struct overflow_data* data,
                            value_handle_t lhs,
                            value_handle_t rhs,
                            const char* op) {
    UBSAN_START;
    char rendered_lhs[VALUE_RENDER_SIZE];
    char rendered_rhs[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    char overflow_kind[16];

    scnprintf(overflow_kind, sizeof(overflow_kind), "overflow:%s", op);

    const struct type_descriptor* type = data->type;

    render_val(rendered_lhs, sizeof(rendered_lhs), type, lhs);
    render_val(rendered_rhs, sizeof(rendered_rhs), type, rhs);

    scnprintf(details, sizeof(details),
              "%s integer overflow: %s %s %s cannot be represented in type"
              " %s\n",
              type_is_signed_integer(type) ? "signed" : "unsigned",
              rendered_lhs, op, rendered_rhs, type->name);

    log(&data->loc, overflow_kind, details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(add_overflow,
              struct overflow_data* data,
              value_handle_t lhs,
              value_handle_t rhs) {
    handle_overflow(data, lhs, rhs, "+");
}

UBSAN_HANDLER(sub_overflow,
              struct overflow_data* data,
              value_handle_t lhs,
              value_handle_t rhs) {
    handle_overflow(data, lhs, rhs, "-");
}

UBSAN_HANDLER(mul_overflow,
              struct overflow_data* data,
              value_handle_t lhs,
              value_handle_t rhs) {
    handle_overflow(data, lhs, rhs, "*");
}

UBSAN_HANDLER(divrem_overflow,
              struct overflow_data* data,
              value_handle_t lhs,
              value_handle_t rhs) {
    handle_overflow(data, lhs, rhs, "/%");
}

UBSAN_HANDLER(negate_overflow, struct overflow_data* data, value_handle_t val) {
    UBSAN_START;
    char rendered_val[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    render_val(rendered_val, sizeof(rendered_val), data->type, val);
    scnprintf(details, sizeof(details),
              "negation of %s cannot be represented in type %s", rendered_val,
              data->type->name);

    log(&data->loc, "negation overflow", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(pointer_overflow,
              struct pointer_overflow_data* data,
              uintptr_t base,
              uintptr_t result) {
    UBSAN_START;
    char details[DETAIL_RENDER_SIZE];
    snprintf_filtered(details, sizeof(details),
                      "pointer arithmetic on %p overflowed resulting in %p",
                      (void*)base, (void*)result);
    log(&data->loc, "pointer_overflow", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(implicit_conversion,
              struct implicit_conversion_data* data,
              value_handle_t src,
              value_handle_t dst) {
    UBSAN_START;
    char rendered_src[VALUE_RENDER_SIZE];
    char rendered_dst[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];
    const char* kind_str;

    if (data->check_kind <
        sizeof(implicit_conversion_check_kinds) / sizeof(const char*)) {
        kind_str = implicit_conversion_check_kinds[data->check_kind];
    } else {
        kind_str = "unknown";
    }

    render_val(rendered_src, sizeof(rendered_src), data->from_type, src);
    render_val(rendered_dst, sizeof(rendered_dst), data->to_type, dst);
    scnprintf(details, sizeof(details),
              "implicit conversion (%s) from %s to %s\n", kind_str,
              rendered_src, rendered_dst);

    log(&data->loc, "implicit conversion", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(invalid_builtin, struct invalid_builtin_data* data) {
    UBSAN_START;
    const char* details;
    switch (data->check_kind) {
        case BCK_CTZ_PASSED_ZERO:
            details = "zero passed to ctz";
            break;
        case BCK_CLZ_PASSED_ZERO:
            details = "zero passed to clz";
            break;
        default:
            details = "unknown builtin misuse kind";
    }
    log(&data->loc, "invalid builtin usage", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(type_mismatch_v1,
              struct type_mismatch_data* data,
              value_handle_t ptr) {
    UBSAN_START;
    char details[DETAIL_RENDER_SIZE];

    intptr_t alignment = 1 << data->log_alignment;
    if (!ptr) {
        scnprintf(details, sizeof(details), "%s null pointer type %s",
                  type_check_kinds[data->type_check_kind], data->type->name);
    } else if (ptr & (alignment - 1)) {
        snprintf_filtered(
                details, sizeof(details),
                "%s misaligned pointer %p for type %s which requires %d byte alignment",
                type_check_kinds[data->type_check_kind], (void*)ptr,
                data->type->name, (int)alignment);
    } else {
        snprintf_filtered(
                details, sizeof(details),
                "%s pointer %p points at a region with insufficient space for a value of type %s",
                type_check_kinds[data->type_check_kind], (void*)ptr,
                data->type->name);
    }
    log(&data->loc, "type mismatch", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(builtin_unreachable, struct unreachable_data* data) {
    UBSAN_START;
    log(&data->loc, "hit a supposedly unreachable point""");
    UBSAN_FINISH;
    ubsan_fail("executing through unreachable would be unwise");
}

UBSAN_HANDLER(missing_return, struct unreachable_data* data) {
    UBSAN_START;
    log(&data->loc, "hit a missing return statement""");
    UBSAN_FINISH;
    ubsan_fail("executing past the end of a function would be unwise");
}

static bool is_negative(const struct type_descriptor* type,
                        value_handle_t val) {
    if (type_is_signed_integer(type)) {
        return val_signed(type, val) < 0;
    }
    return false;
}

UBSAN_HANDLER(shift_out_of_bounds,
              struct shift_out_of_bounds_data* data,
              value_handle_t lhs,
              value_handle_t rhs) {
    UBSAN_START;
    char rendered_lhs[VALUE_RENDER_SIZE];
    char rendered_rhs[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    render_val(rendered_lhs, sizeof(rendered_lhs), data->lhs_type, lhs);
    render_val(rendered_rhs, sizeof(rendered_rhs), data->rhs_type, rhs);

    uint64_t rhs_u64 = val_unsigned(data->rhs_type, rhs);

    if (is_negative(data->rhs_type, rhs)) {
        scnprintf(details, sizeof(details), "shift amount is negative: %s",
                  rendered_rhs);
    } else if (type_width_bits(data->lhs_type) < rhs_u64) {
        scnprintf(details, sizeof(details),
                  "shift amount %s is too large for type %s", rendered_rhs,
                  data->lhs_type->name);
    } else if (is_negative(data->lhs_type, lhs)) {
        /* At this point, we know we are dealing with a left shift, as right
         * shift is covered by the above two cases */

        scnprintf(details, sizeof(details),
                  "left shifting a negative value: %s", rendered_lhs);
    } else {
        scnprintf(details, sizeof(details), "%s << %s does not fit in %s",
                  rendered_lhs, rendered_rhs, data->lhs_type->name);
    }

    log(&data->loc, "shift out of bounds", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(out_of_bounds,
              struct out_of_bounds_data* data,
              value_handle_t index) {
    UBSAN_START;
    char rendered_index[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    render_val(rendered_index, sizeof(rendered_index), data->index_type, index);
    scnprintf(details, sizeof(details), "index %s out of bounds for %s\n",
              rendered_index, data->array_type->name);

    log(&data->loc, "out of bounds access", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(load_invalid_value,
              struct invalid_value_data* data,
              value_handle_t val) {
    UBSAN_START;
    char rendered_val[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    render_val(rendered_val, sizeof(rendered_val), data->type, val);
    scnprintf(details, sizeof(details),
              "load of value %s outside of range for type %s", rendered_val,
              data->type->name);

    log(&data->loc, "invalid value", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(float_cast_overflow,
              struct float_cast_overflow_data* data,
              value_handle_t val) {
    UBSAN_START;
    /* Since we aren't rendering floats, there's not much point in details */
    log(&data->loc, "float cast overflow""");
    UBSAN_FINISH;
}

UBSAN_HANDLER(cfi_check_fail_abort,
              struct cfi_check_fail_data* data,
              value_handle_t val,
              uintptr_t vtable_is_valid) {
    UBSAN_START;
    char rendered_val[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    render_val(rendered_val, sizeof(rendered_val), data->type, val);
    scnprintf(details, sizeof(details), "type of the value: %s  type name: %s",
              rendered_val, data->type->name);
    log(&data->loc, "cfi check fail abort", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(vla_bound_not_positive,
              struct vla_bound_data* data,
              value_handle_t val) {
    UBSAN_START;
    char rendered_val[VALUE_RENDER_SIZE];
    char details[DETAIL_RENDER_SIZE];

    render_val(rendered_val, sizeof(rendered_val), data->type, val);
    scnprintf(details, sizeof(details), "bound %s is not positive for type %s",
              rendered_val, data->type->name);
    log(&data->loc,
        "variable length array bound evaluates to non-positive value", details);
    UBSAN_FINISH;
}

UBSAN_HANDLER(alignment_assumption,
              struct alignment_assumption_data* data,
              value_handle_t pointer,
              value_handle_t alignment,
              value_handle_t offset) {
    UBSAN_START;
    char details[DETAIL_RENDER_SIZE];

    if (location_is_valid(data->assumption_loc)) {
        log(&data->assumption_loc, "alignment assumption was specified here",
            /* Details: */ "below");
    }

    if (!offset) {
        scnprintf(details, sizeof(details),
                  "assumption of %" PRIuPTR
                  " byte alignment for pointer of type %s failed",
                  alignment, data->type->name);
    } else {
        scnprintf(details, sizeof(details),
                  "assumption of %" PRIuPTR
                  " byte alignment (with offset of %" PRIuPTR
                  " bytes) for pointer of type %s failed",
                  alignment, offset, data->type->name);
    }

    log(&data->loc, "alignment assumption is incorrect", details);
    UBSAN_FINISH;
}

Messung V0.5 in Prozent
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet am  2026-06-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik