diff --git a/app/tests/v9fs_tests.c b/app/tests/v9fs_tests.c index c6d9ac9bda..5170be85c8 100644 --- a/app/tests/v9fs_tests.c +++ b/app/tests/v9fs_tests.c @@ -43,7 +43,7 @@ int v9fs_tests(int argc, const console_cmd_args *argv) { readbytes = fs_read_file(handle, buf, 0, BUF_SIZE); if (readbytes < 0) { - LOGF("failed to read the target file: %ld\n", readbytes); + LOGF("failed to read the target file: %zd\n", readbytes); return status; } diff --git a/arch/arm/arm/debug.c b/arch/arm/arm/debug.c index bcc6de61b3..d48dfd8ed6 100644 --- a/arch/arm/arm/debug.c +++ b/arch/arm/arm/debug.c @@ -165,7 +165,7 @@ static int cmd_dcc(int argc, const console_cmd_args *argv) { uint32_t buf[128]; ssize_t len = arm_dcc_read(buf, sizeof(buf), 1000); - printf("arm_dcc_read returns %ld\n", len); + printf("arm_dcc_read returns %zd\n", len); if (len > 0) { hexdump(buf, len); } @@ -181,4 +181,3 @@ STATIC_COMMAND_START STATIC_COMMAND("dcc", "dcc stuff", &cmd_dcc) #endif STATIC_COMMAND_END(dcc); - diff --git a/external/arch/arm/arm-m/CMSIS/Include/cmsis_gcc.h b/external/arch/arm/arm-m/CMSIS/Include/cmsis_gcc.h index dc3ae357fc..7fb51ac59e 100644 --- a/external/arch/arm/arm-m/CMSIS/Include/cmsis_gcc.h +++ b/external/arch/arm/arm-m/CMSIS/Include/cmsis_gcc.h @@ -27,6 +27,7 @@ /* LK: include lk's compiler.h first, which has some of the same #defines */ #include +__BEGIN_CDECLS /* ignore some GCC warnings */ #pragma GCC diagnostic push @@ -2211,4 +2212,5 @@ __STATIC_FORCEINLINE int32_t __SMMLA (int32_t op1, int32_t op2, int32_t op3) #pragma GCC diagnostic pop +__END_CDECLS #endif /* __CMSIS_GCC_H */ diff --git a/lib/bio/debug.c b/lib/bio/debug.c index 91d515d97a..0cf2e299a9 100644 --- a/lib/bio/debug.c +++ b/lib/bio/debug.c @@ -125,7 +125,7 @@ static int cmd_bio(int argc, const console_cmd_args *argv) { ssize_t err_len = bio_read(dev, buf, offset, amt); if (err_len < 0) { - dprintf(ALWAYS, "read error %s %zu@%zu (err_len %ld)\n", + dprintf(ALWAYS, "read error %s %zu@%zu (err_len %zd)\n", argv[2].str, amt, (size_t)offset, err_len); break; } @@ -413,7 +413,7 @@ static status_t memory_mapped_test(bdev_t *device) { // Erase the first page of the Device. ssize_t err = bio_erase(device, 0, device->block_size); if (err < (ssize_t)device->block_size) { - printf("Expected to erase at least %zu bytes but only erased %ld. " + printf("Expected to erase at least %zu bytes but only erased %zd. " "Not continuing to test memory mapped mode.\n", device->block_size, err); retcode = ERR_IO; @@ -430,7 +430,7 @@ static status_t memory_mapped_test(bdev_t *device) { err = bio_write_block(device, test_buffer, 0, 1); if (err != (ssize_t)device->block_size) { printf("Error while writing test pattern to device. Expected to write " - "%zu bytes but actually wrote %ld. Not continuing to test memory " + "%zu bytes but actually wrote %zd. Not continuing to test memory " "mapped mode.\n", device->block_size, err); retcode = ERR_IO; goto finish; @@ -474,7 +474,7 @@ static status_t memory_mapped_test(bdev_t *device) { // what we wrote back earlier. err = bio_read_block(device, reference_buffer, 0, 1); if (err != (ssize_t)device->block_size) { - printf("Expected to read %zu bytes, actually read %ld. Aborting.\n", + printf("Expected to read %zu bytes, actually read %zd. Aborting.\n", device->block_size, err); retcode = ERR_IO; goto finish; @@ -501,10 +501,10 @@ static status_t memory_mapped_test(bdev_t *device) { static int bio_test_device(bdev_t *device) { ssize_t num_errors = erase_test(device); if (num_errors < 0) { - printf("error %ld performing erase test\n", num_errors); + printf("error %zd performing erase test\n", num_errors); return -1; } - printf("discovered %ld error(s) while testing erase.\n", num_errors); + printf("discovered %zd error(s) while testing erase.\n", num_errors); if (num_errors) { // No point in continuing the tests if we couldn't erase the device. printf("not continuing to test writes.\n"); @@ -512,7 +512,7 @@ static int bio_test_device(bdev_t *device) { } num_errors = write_test(device); - printf("Discovered %ld error(s) while testing write.\n", num_errors); + printf("Discovered %zd error(s) while testing write.\n", num_errors); if (num_errors) { return -1; } diff --git a/lib/fs/9p/dir.c b/lib/fs/9p/dir.c index 5cdb328490..ddd2f701fb 100644 --- a/lib/fs/9p/dir.c +++ b/lib/fs/9p/dir.c @@ -268,7 +268,7 @@ status_t v9fs_read_dir(dircookie *dcookie, struct dirent *ent) } LTRACEF( - "head (%u) tail (%u) sread (%ld) offset (%llu) name " + "head (%u) tail (%u) sread (%zd) offset (%llu) name " "(%s)\n", dir->head, dir->tail, sread, p9_dent.offset, p9_dent.name); diff --git a/lib/fs/spifs/test/spifstest.c b/lib/fs/spifs/test/spifstest.c index 5258bc14d8..a3fcfad4af 100644 --- a/lib/fs/spifs/test/spifstest.c +++ b/lib/fs/spifs/test/spifstest.c @@ -731,7 +731,7 @@ static int spifs_bench(int argc, const console_cmd_args *argv) { if (n_bytes < 0) { printf("SPIFS Benchmark Failed to write to file at %s. " - "Reason = %ld.\n", test_file_path, n_bytes); + "Reason = %zd.\n", test_file_path, n_bytes); retcode = -1; fs_close_file(handle); goto finish; @@ -752,7 +752,7 @@ static int spifs_bench(int argc, const console_cmd_args *argv) { if (n_bytes < 0) { printf("SPIFS Benchmark Failed to read from file at %s. " - "Reason = %ld.\n", test_file_path, n_bytes); + "Reason = %zd.\n", test_file_path, n_bytes); retcode = -1; fs_close_file(handle); goto finish; diff --git a/lib/libc/include/sys/types.h b/lib/libc/include/sys/types.h index be9302de84..47dc97e8fe 100644 --- a/lib/libc/include/sys/types.h +++ b/lib/libc/include/sys/types.h @@ -44,7 +44,11 @@ enum handler_return { INT_RESCHEDULE, }; +#ifdef __LP64__ typedef signed long int ssize_t; +#else +typedef signed int ssize_t; +#endif typedef uint8_t u8; typedef uint16_t u16; diff --git a/lib/minip/tcp.c b/lib/minip/tcp.c index 8960faade7..2831dc146c 100644 --- a/lib/minip/tcp.c +++ b/lib/minip/tcp.c @@ -1330,7 +1330,7 @@ static int cmd_tcp(int argc, const console_cmd_args *argv) { uint8_t buf[512]; ssize_t err_len = tcp_read(accepted, buf, sizeof(buf)); - printf("tcp_read returns %ld\n", err_len); + printf("tcp_read returns %zd\n", err_len); if (err_len < 0) break; if (err_len > 0) { @@ -1338,7 +1338,7 @@ static int cmd_tcp(int argc, const console_cmd_args *argv) { } err_len = tcp_write(accepted, buf, err_len); - printf("tcp_write returns %ld\n", err_len); + printf("tcp_write returns %zd\n", err_len); if (err_len < 0) break; } @@ -1362,4 +1362,3 @@ static int cmd_tcp(int argc, const console_cmd_args *argv) { STATIC_COMMAND_START STATIC_COMMAND("tcp", "tcp commands", &cmd_tcp) STATIC_COMMAND_END(tcp); - diff --git a/lib/minip/udp.c b/lib/minip/udp.c index de4d5240d0..f7a63f3acf 100644 --- a/lib/minip/udp.c +++ b/lib/minip/udp.c @@ -148,7 +148,7 @@ status_t udp_send_iovec(const iovec_t *iov, uint iov_count, udp_socket_t *handle udp->chksum = rfc768_chksum(ip, udp); #endif - LTRACEF("packet paylod len %ld\n", len); + LTRACEF("packet paylod len %zd\n", len); minip_tx_handler(minip_tx_arg, p); diff --git a/lib/test_ubsan/rules.mk b/lib/test_ubsan/rules.mk new file mode 100644 index 0000000000..a326978b98 --- /dev/null +++ b/lib/test_ubsan/rules.mk @@ -0,0 +1,14 @@ +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/test_ubsan.c \ + $(LOCAL_DIR)/test_ubsan.cpp + +MODULE_DEPS += \ + lib/ubsan + +MODULE_COMPILEFLAGS += -fsanitize=undefined + +include make/module.mk diff --git a/lib/test_ubsan/test_ubsan.c b/lib/test_ubsan/test_ubsan.c new file mode 100644 index 0000000000..374b4c0786 --- /dev/null +++ b/lib/test_ubsan/test_ubsan.c @@ -0,0 +1,296 @@ +// Copyright 2024, Google LLC + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TESTCASE_UBSAN(x) { .func = x, .name = #x } + +typedef void (*test_ubsan_fp)(void); +struct test_ubsan_struct { + test_ubsan_fp func; + const char *const name; +}; + +static void add_overflow(void) { + volatile int val = INT_MAX; + volatile int val2 = 1; + + val += val2; +} + +static void sub_overflow(void) { + volatile int val = INT_MIN; + volatile int val2 = 1; + + val -= val2; +} + +static void mul_overflow(void) { + volatile int val = INT_MAX / 2; + volatile int val2 = 3; + + val *= val2; +} + +static void negate_overflow(void) { + volatile int val = INT_MIN; + + val = -val; +} + +static void divrem_overflow(void) { + volatile int val = 16; + volatile int val2 = 0; + + val /= val2; +} + +static void pointer_overflow(void) { + volatile uintptr_t offset = UINTPTR_MAX; + char buf[4]; + char *ptr = buf; + + ptr += offset; + (void) ptr; +} + +static void float_cast_overflow(void) { + volatile uint32_t val; + volatile uint32_t mul = 10; + + val = mul * 0xFFFF * 1e9; + (void) val; +} + +static void shift_out_of_bounds_overflow(void) { + volatile int pos = 1; + volatile int val = INT_MAX; + + val <<= pos; +} + +static void shift_out_of_bounds_negative(void) { + volatile int neg = -1; + volatile int val = 10; + + val <<= neg; +} + +static void out_of_bounds_overflow(void) { + volatile int val = 10; + volatile int overflow = 4; + volatile char above[4] = {}; + volatile int arr[4]; + volatile char below[4] = {}; + + above[0] = below[0]; + arr[overflow] = val; +} + +static void out_of_bounds_underflow(void) { + volatile int val = 10; + volatile int underflow = -1; + volatile char above[4] = {}; + volatile int arr[4]; + volatile char below[4] = {}; + + above[0] = below[0]; + arr[underflow] = val; +} + +static void load_invalid_value_bool(void) { + volatile char *dst, *src; + bool val, val2; + volatile char invalid_val = 2; + + src = &invalid_val; + dst = (char *)&val; + *dst = *src; + + val2 = val; + (void) val2; +} + +extern void load_invalid_value_enum(void); + +static void nullptr_deref(void) { + int *nullptr = NULL; + int val = *nullptr; + (void) val; +} + +static void misaligned_access(void) { + struct object { + int val; + }; + char buf[3]; + struct object *obj = (struct object *)buf; + + obj = (struct object*)(buf + 1); + obj->val = 0; +} + +static void object_size_mismatch(void) { + struct object { + int val; + }; + + char buf[3]; + struct object *obj = (struct object *)buf; + + obj->val = 0; +} + +static void func(int a) { return; } +static void function_type_mismatch(void) { + typedef void (*func_ptr)(void); + func_ptr f = (func_ptr) func; + f(); +} + +__attribute__((nonnull)) static void _nonnull_arg( void *ptr) { (void) ptr; } +static void nonnull_arg(void) { + volatile void *nullptr = NULL; + _nonnull_arg((void *)nullptr); +} + +__attribute__((returns_nonnull)) static void* _nonnull_return(void) { + volatile void *ret = NULL; + return (void *)ret; +} + +static void nonnull_return(void) { + _nonnull_return(); +} + +static void vla_bound_not_positive(void) { + volatile int size = 0; + int arr[size]; + + (void) arr; +} + +static void *_alignment_assumption(void) __attribute__((__assume_aligned__(32))); +static void *_alignment_assumption(void) { + volatile void *ptr = (void *)0x3; + return (void *)ptr; +} + +static void *_alignment_assumption_offset(void) __attribute__((__assume_aligned__(32, 4))); +static void *_alignment_assumption_offset(void) { + volatile void *ptr = (void *)0x3; + return (void *)ptr; +} + +static void alignment_assumption(void) { + void *ptr; + + ptr = _alignment_assumption(); + (void) ptr; +} + +static void alignment_assumption_offset(void) { + void *ptr; + + ptr = _alignment_assumption_offset(); + (void) ptr; +} + +// __builtin_clzg and __builtin_ctzg is not support +static void invalid_builtin_clz(void) { + int ret = __builtin_clz(0); + (void) ret; +} + +static void invalid_builtin_ctz(void) { + int ret = __builtin_ctz(0); + (void) ret; +} + +static void builtin_unreachable(void) { + __UNREACHABLE; +} + +static const struct test_ubsan_struct test_ubsan_array[] = { + TESTCASE_UBSAN(add_overflow), + TESTCASE_UBSAN(sub_overflow), + TESTCASE_UBSAN(mul_overflow), + TESTCASE_UBSAN(negate_overflow), + TESTCASE_UBSAN(pointer_overflow), + TESTCASE_UBSAN(float_cast_overflow), + TESTCASE_UBSAN(shift_out_of_bounds_overflow), + TESTCASE_UBSAN(shift_out_of_bounds_negative), + TESTCASE_UBSAN(out_of_bounds_overflow), + TESTCASE_UBSAN(out_of_bounds_underflow), + TESTCASE_UBSAN(load_invalid_value_bool), + TESTCASE_UBSAN(load_invalid_value_enum), + TESTCASE_UBSAN(nullptr_deref), + TESTCASE_UBSAN(misaligned_access), + TESTCASE_UBSAN(object_size_mismatch), + TESTCASE_UBSAN(function_type_mismatch), + TESTCASE_UBSAN(nonnull_arg), + TESTCASE_UBSAN(nonnull_return), + TESTCASE_UBSAN(vla_bound_not_positive), + TESTCASE_UBSAN(alignment_assumption), + TESTCASE_UBSAN(alignment_assumption_offset), + TESTCASE_UBSAN(invalid_builtin_clz), + TESTCASE_UBSAN(invalid_builtin_ctz), + TESTCASE_UBSAN(builtin_unreachable), +}; + + +static void show_usage(void) { + printf("Usage: test_ubsan [OPTION]\n"); + printf(" NUM - The testcase number you want to test\n"); + printf(" all - executing all ubsan test cases\n"); + printf("Available ubsan test cases:\n"); + printf("Num: testcase name\n"); + for (unsigned int i = 0; i < ARRAY_SIZE(test_ubsan_array); ++i) + printf("%3u: %s\n", i, test_ubsan_array[i].name); +} + +static int test_ubsan(int argc, const console_cmd_args *argv) { + + unsigned long id; + + if (argc != 2) { + show_usage(); + return argc == 1 ? NO_ERROR : ERR_INVALID_ARGS; + } + + if (!strcmp(argv[1].str, "all")) { + for (unsigned int i = 0; i < ARRAY_SIZE(test_ubsan_array); ++i) { + printf("start %s ...\n", test_ubsan_array[i].name); + test_ubsan_array[i].func(); + } + + return NO_ERROR; + } + + if (!isdigit(argv[1].str[0])) { + show_usage(); + return ERR_INVALID_ARGS; + } + + id = argv[1].u; + + if (id >= ARRAY_SIZE(test_ubsan_array)) { + show_usage(); + return ERR_INVALID_ARGS; + } + + printf("start %s ...\n", test_ubsan_array[id].name); + test_ubsan_array[id].func(); + return NO_ERROR; +} + +STATIC_COMMAND_START +STATIC_COMMAND("test_ubsan", "testing ubsan", test_ubsan) +STATIC_COMMAND_END(test_ubsan); diff --git a/lib/test_ubsan/test_ubsan.cpp b/lib/test_ubsan/test_ubsan.cpp new file mode 100644 index 0000000000..73ef63e4b7 --- /dev/null +++ b/lib/test_ubsan/test_ubsan.cpp @@ -0,0 +1,13 @@ +extern "C" +{ + +// https://cplusplus.github.io/CWG/issues/1766.html +// It is only since C++17 that loading an out-of-range value for an enum is undefined behavior. +// But clang treats this as UB even with C++14(LK compiles C++ files with --std=c++14 by default) +void load_invalid_value_enum(void) { + enum E {e1, e2, e3, e4}; + volatile int a = 5; + volatile enum E e = *((volatile enum E*)(&a)); + (void) &e; +} +} diff --git a/lib/ubsan/ubsan.cpp b/lib/ubsan/ubsan.cpp index 1f09f43f86..cab3b831cf 100644 --- a/lib/ubsan/ubsan.cpp +++ b/lib/ubsan/ubsan.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -172,7 +173,7 @@ static void ubsan_report_end() static void do_ubsan_type_mismatch_nullptr(const ubsan_type_mismatch_data *data, size_t ptr) { ubsan_report_start(&data->location, "Null pointer dereference"); - printf("%s null pointer of type %sn", type_check_kinds[data->type_check_kind], + printf("%s null pointer of type %s\n", type_check_kinds[data->type_check_kind], data->type->typename_); ubsan_report_end(); } @@ -189,7 +190,7 @@ static void do_ubsan_type_mismatch_unaligned(const ubsan_type_mismatch_data *dat static void do_ubsan_type_mismatch_objsize(const ubsan_type_mismatch_data *data, size_t ptr) { ubsan_report_start(&data->location, "Insufficient object size"); - printf("%s address %p with insuficient space for type %s\n", + printf("%s address %p with insufficient space for type %s\n", type_check_kinds[data->type_check_kind], (void *) ptr, data->type->typename_); ubsan_report_end(); } @@ -314,7 +315,7 @@ static void ubsan_handle_integer_overflow(const ubsan_overflow_data *data, size_ ubsan_report_start(&data->location, "Integer overflow"); - printf("%s integer overflow: %s %s %s can't be represented in type %s", + printf("%s integer overflow: %s %s %s can't be represented in type %s\n", signed_ ? "signed" : "unsigned", lhs_v.to_string().c_str(), op, rhs_v.to_string().c_str(), data->type->typename_); ubsan_report_end(); @@ -557,6 +558,79 @@ __USED void __ubsan_handle_vla_bound_not_positive_abort(ubsan_vla_bound_data *da ubsan_abort(); } +struct ubsan_function_type_mismatch_data { + ubsan_source_location location; + ubsan_type_descriptor *type; + val function_ptr; +}; + +__USED void __ubsan_handle_function_type_mismatch(ubsan_function_type_mismatch_data *data, size_t ptr) +{ + ubsan_report_start(&data->location, "function type mismatch"); + printf("Call function(%p) through pointer with incompatible type %s\n", + (void *) ptr, data->type->typename_); + ubsan_report_end(); +} + +__USED void __ubsan_handle_function_type_mismatch_abort(ubsan_function_type_mismatch_data *data, size_t ptr) +{ + __ubsan_handle_function_type_mismatch(data, ptr); + ubsan_abort(); +} + +struct ubsan_alignment_assumption_data { + struct ubsan_source_location location; + struct ubsan_source_location assumption_location; + struct ubsan_type_descriptor *type; +}; + +__USED void __ubsan_handle_alignment_assumption(ubsan_alignment_assumption_data *data, ssize_t ptr, + ssize_t align, ssize_t offset) +{ + ssize_t real_ptr; + ubsan_report_start(&data->location, "Alignment Error"); + if (offset) + printf("assumption of %zd byte alignment (with offset of %zd byte) for pointer of type %s failed", + align, offset, data->type->typename_); + else + printf("assumption of %zd byte alignment for pointer of type %s failed", + align, data->type->typename_); + + real_ptr = ptr - offset; + printf(" %saddress is %lu aligned, misalignment offset is %zd bytes\n", + offset ? "offset " : "", real_ptr ? (1 << ffs(real_ptr)) : 1, + real_ptr & (align - 1)); + + ubsan_report_end(); +} + +__USED void __ubsan_handle_alignment_assumption_abort(ubsan_alignment_assumption_data *data, + ssize_t ptr, ssize_t align, ssize_t offset) +{ + __ubsan_handle_alignment_assumption(data, ptr, align, offset); + ubsan_abort(); +} + +struct ubsan_float_cast_data { + struct ubsan_source_location location; + struct ubsan_type_descriptor *from_type; + struct ubsan_type_descriptor *to_type; +}; + +__USED void __ubsan_handle_float_cast_overflow(ubsan_float_cast_data *data, size_t ptr) +{ + ubsan_report_start(&data->location, "Float Cast Overflow"); + printf("value %s is outside of the range of representable value of type %s at %p\n", + data->from_type->typename_, data->to_type->typename_, (void *) ptr); + ubsan_report_end(); +} + +__USED void __ubsan_handle_float_cast_overflow_abort(ubsan_float_cast_data *data, size_t ptr) +{ + __ubsan_handle_float_cast_overflow(data, ptr); + ubsan_abort(); +} + // Note: we used to have __ubsan_handle_function_type_mismatch but it seems to have been dropped // from both GCC and clang