├── Makefile.x86 ├── third_party └── printf │ ├── METADATA │ ├── LICENSE │ ├── printf.h │ └── printf.c ├── Makefile.riscv32 ├── Makefile.riscv64 ├── Makefile.arm ├── Makefile.aarch64 ├── kasan.h ├── rt_utils.h ├── start_riscv32.S ├── start_riscv64.S ├── start_arm.S ├── start_aarch64.S ├── heap.h ├── sanitized_lib.h ├── kasan_test.c ├── start_x86.S ├── CONTRIBUTING ├── heap.c ├── common.h ├── kasan_test.ld ├── sanitized_lib.c ├── rt_utils.c ├── Makefile ├── README.md ├── kasan.c └── LICENSE /Makefile.x86: -------------------------------------------------------------------------------- 1 | TARGET_DRAM_START := 0x00100000 2 | TARGET_DRAM_END := 0x07ffffff 3 | 4 | KASAN_SHADOW_MAPPING_OFFSET := 0x06FE0000 5 | KASAN_SHADOW_MEMORY_START := 0x07000000 6 | KASAN_SHADOW_MEMORY_SIZE := 0x1000000 7 | 8 | ARCH_TARGET := i386-none-none 9 | KASAN_TARGET := i386-linux-gnu 10 | 11 | CFLAGS_ARCH := --target=$(ARCH_TARGET) 12 | QEMU := qemu-system-i386 -nographic -m 128M -kernel -------------------------------------------------------------------------------- /third_party/printf/METADATA: -------------------------------------------------------------------------------- 1 | name: "printf" 2 | description: 3 | "A printf / sprintf Implementation for Embedded Systems." 4 | 5 | third_party { 6 | identifier { 7 | type: "Git" 8 | value: "https://github.com/mpaland/printf" 9 | primary_source: true 10 | } 11 | version: "d3b984684bb8a8bdc48cc7a1abecb93ce59bbe3e" 12 | last_upgrade_date { year: 2024 month: 2 day: 23 } 13 | license_type: NOTICE 14 | } -------------------------------------------------------------------------------- /Makefile.riscv32: -------------------------------------------------------------------------------- 1 | TARGET_DRAM_START := 0x80000000 2 | TARGET_DRAM_END := 0x8fffffff 3 | UART_BASE_ADDRESS := 0x10000000 4 | 5 | KASAN_SHADOW_MAPPING_OFFSET := 0x77000000 6 | KASAN_SHADOW_MEMORY_START := 0x87000000 7 | KASAN_SHADOW_MEMORY_SIZE := 0x2000000 8 | 9 | ARCH_TARGET := riscv32-none-none 10 | KASAN_TARGET := riscv32-linux-gnu 11 | 12 | CFLAGS_ARCH := --target=$(ARCH_TARGET) -march=rv32imafd 13 | CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS) 14 | QEMU := qemu-system-riscv32 -M virt -bios none -m 256M -nographic -kernel -------------------------------------------------------------------------------- /Makefile.riscv64: -------------------------------------------------------------------------------- 1 | TARGET_DRAM_START := 0x80000000 2 | TARGET_DRAM_END := 0x8fffffff 3 | UART_BASE_ADDRESS := 0x10000000 4 | 5 | KASAN_SHADOW_MAPPING_OFFSET := 0x77000000 6 | KASAN_SHADOW_MEMORY_START := 0x87000000 7 | KASAN_SHADOW_MEMORY_SIZE := 0x2000000 8 | 9 | ARCH_TARGET := riscv64-none-none 10 | KASAN_TARGET := riscv64-linux-gnu 11 | 12 | CFLAGS_ARCH := --target=$(ARCH_TARGET) -march=rv64imafd -fPIC 13 | CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS) 14 | QEMU := qemu-system-riscv64 -M virt -bios none -m 256M -nographic -kernel -------------------------------------------------------------------------------- /Makefile.arm: -------------------------------------------------------------------------------- 1 | TARGET_DRAM_START := 0x40000000 2 | TARGET_DRAM_END := 0x4fffffff 3 | UART_BASE_ADDRESS := 0x09000000 4 | 5 | KASAN_SHADOW_MAPPING_OFFSET := 0x42700000 6 | KASAN_SHADOW_MEMORY_START := 0x4A700000 7 | KASAN_SHADOW_MEMORY_SIZE := 0x2000000 8 | 9 | ARCH_TARGET := arm-arm-none-eabi 10 | KASAN_TARGET := arm-arm-linux-gnu 11 | 12 | CFLAGS_ARCH := --target=$(ARCH_TARGET) -mfloat-abi=soft -mcpu=cortex-a7 13 | CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS) 14 | 15 | QEMU := qemu-system-arm -M virt -cpu cortex-a7 -m 256M -nographic -kernel 16 | -------------------------------------------------------------------------------- /Makefile.aarch64: -------------------------------------------------------------------------------- 1 | TARGET_DRAM_START := 0x40000000 2 | TARGET_DRAM_END := 0x4fffffff 3 | UART_BASE_ADDRESS := 0x09000000 4 | 5 | KASAN_SHADOW_MAPPING_OFFSET := 0x42700000 6 | KASAN_SHADOW_MEMORY_START := 0x4A700000 7 | KASAN_SHADOW_MEMORY_SIZE := 0x2000000 8 | 9 | ARCH_TARGET := aarch64-arm-none-eabi 10 | KASAN_TARGET := aarch64-arm-linux-gnu 11 | 12 | CFLAGS_ARCH := --target=$(ARCH_TARGET) -mcpu=cortex-a53+nofp+nosimd 13 | CFLAGS_ARCH += -DUART_BASE_ADDRESS=$(UART_BASE_ADDRESS) 14 | 15 | QEMU := qemu-system-aarch64 -M virt -cpu cortex-a53 -m 256M -nographic -kernel -------------------------------------------------------------------------------- /kasan.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | void initialize_kasan(void); 15 | 16 | void *kasan_malloc_hook(unsigned long size); 17 | 18 | void kasan_free_hook(void *ptr); -------------------------------------------------------------------------------- /rt_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __RT_UTILS_H__ 15 | #define __RT_UTILS_H__ 16 | 17 | void call_global_ctors(void); 18 | 19 | #endif // __RT_UTILS_H__ -------------------------------------------------------------------------------- /start_riscv32.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | .section .reset, "ax" 15 | .global reset_handler 16 | reset_handler: 17 | la sp, __stack_top 18 | call main 19 | wait: 20 | j wait -------------------------------------------------------------------------------- /start_riscv64.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | .section .reset, "ax" 15 | .global reset_handler 16 | reset_handler: 17 | la sp, __stack_top 18 | call main 19 | wait: 20 | j wait -------------------------------------------------------------------------------- /start_arm.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | .section .reset, "ax" 15 | .global reset_handler 16 | reset_handler: 17 | ldr sp, =__stack_top 18 | bl main 19 | wait: 20 | b wait 21 | -------------------------------------------------------------------------------- /start_aarch64.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | .section .reset, "ax" 15 | .global reset_handler 16 | reset_handler: 17 | ldr x0, =__stack_top 18 | mov sp, x0 19 | bl main 20 | wait: 21 | b wait -------------------------------------------------------------------------------- /heap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __KASAN_HEAP_H__ 15 | #define __KASAN_HEAP_H__ 16 | 17 | void initialize_heap(void); 18 | 19 | void *allocate_chunk(unsigned long size); 20 | void free_chunk(void *ptr); 21 | 22 | void *malloc(unsigned long size); 23 | void free(void *ptr); 24 | 25 | #endif // __KASAN_HEAP_H__ -------------------------------------------------------------------------------- /sanitized_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __SANITIZED_LIB_H__ 15 | #define __SANITIZED_LIB_H__ 16 | 17 | void test_heap_overflow(void); 18 | void test_stack_overflow(void); 19 | void test_globals_overflow(void); 20 | void test_memset_overflow(void); 21 | void test_memcpy_overflow(void); 22 | 23 | #endif // __SANITIZED_LIB_H__ 24 | -------------------------------------------------------------------------------- /kasan_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "kasan.h" 15 | 16 | #include "heap.h" 17 | #include "printf.h" 18 | #include "rt_utils.h" 19 | #include "sanitized_lib.h" 20 | 21 | int main(void) { 22 | printf("Starting bare-metal KASan test driver.\n"); 23 | 24 | // Needed to invoke KASan globals instrumentation. 25 | call_global_ctors(); 26 | 27 | initialize_heap(); 28 | 29 | initialize_kasan(); 30 | 31 | test_heap_overflow(); 32 | test_stack_overflow(); 33 | test_globals_overflow(); 34 | test_memset_overflow(); 35 | test_memcpy_overflow(); 36 | 37 | printf("Press ctrl + a then x to exit.\n"); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /third_party/printf/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marco Paland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /start_x86.S: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 15 | #define MULTIBOOT_FLAG_PAGE_ALIGN 1 16 | #define MULTIBOOT_FLAG_MEMORY_INFO 2 17 | #define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_FLAG_PAGE_ALIGN | \ 18 | MULTIBOOT_FLAG_MEMORY_INFO) 19 | #define MULTIBOOT_HEADER_CHECKSUM (0 - MULTIBOOT_HEADER_MAGIC - \ 20 | MULTIBOOT_HEADER_FLAGS) 21 | 22 | .section .reset, "ax" 23 | // Start with a multiboot header 24 | .long MULTIBOOT_HEADER_MAGIC 25 | .long MULTIBOOT_HEADER_FLAGS 26 | .long MULTIBOOT_HEADER_CHECKSUM 27 | 28 | .global reset_handler 29 | reset_handler: 30 | mov $__stack_top, %esp 31 | call main 32 | wait: 33 | jmp wait -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our community guidelines 22 | 23 | This project follows 24 | [Google's Open Source Community Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use GitHub pull requests for this purpose. Consult 32 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 33 | information on using pull requests. -------------------------------------------------------------------------------- /heap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "common.h" 15 | #include "kasan.h" 16 | 17 | // These symbols are defined in the linker script. 18 | extern char __heap_start; 19 | extern char __heap_end; 20 | 21 | static void *heap_head; 22 | static size_t heap_size; 23 | 24 | void initialize_heap(void) { 25 | heap_head = (void *)&__heap_start; 26 | heap_size = (void *)&__heap_end - (void *)&__heap_start; 27 | } 28 | 29 | void *allocate_chunk(unsigned long size) { 30 | void *result = heap_head; 31 | if (size > heap_size) return NULL; 32 | 33 | size = (size + 7) & (~7UL); 34 | heap_head += size; 35 | heap_size -= size; 36 | return result; 37 | } 38 | 39 | void free_chunk(void *ptr) { (void)ptr; } 40 | 41 | void *malloc(unsigned long size) { return kasan_malloc_hook(size); } 42 | 43 | void free(void *ptr) { return kasan_free_hook(ptr); } -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifndef __KASAN_COMMON_H__ 15 | #define __KASAN_COMMON_H__ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #define CALLER_PC ((unsigned long)__builtin_return_address(0)) 22 | 23 | #ifdef KASAN_ENABLED 24 | 25 | void *__kasan_memcpy(void *dst, const void *src, unsigned int size, 26 | uintptr_t pc); 27 | void *__kasan_memset(void *buf, int c, unsigned int size, uintptr_t pc); 28 | 29 | #define memcpy(dst, src, size) __kasan_memcpy(dst, src, size, CALLER_PC) 30 | #define memset(buf, c, size) __kasan_memset(buf, c, size, CALLER_PC) 31 | 32 | #else // KASAN_ENABLED 33 | 34 | void *memcpy(void *dst, const void *src, unsigned long size); 35 | void *memset(void *buf, int c, unsigned long size); 36 | 37 | #endif // KASAN_ENABLED 38 | 39 | #endif // __KASAN_COMMON_H__ -------------------------------------------------------------------------------- /kasan_test.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #ifdef TARGET_ARCH_arm 15 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 16 | OUTPUT_ARCH(arm) 17 | #endif 18 | 19 | #ifdef TARGET_ARCH_aarch64 20 | OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64") 21 | OUTPUT_ARCH(aarch64) 22 | #endif 23 | 24 | #ifdef TARGET_ARCH_riscv32 25 | OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") 26 | OUTPUT_ARCH(riscv) 27 | #endif 28 | 29 | #ifdef TARGET_ARCH_riscv64 30 | OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv") 31 | OUTPUT_ARCH(riscv) 32 | #endif 33 | 34 | #ifdef TARGET_ARCH_x86 35 | OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") 36 | OUTPUT_ARCH(i386) 37 | #endif 38 | 39 | ENTRY(reset_handler) 40 | SECTIONS 41 | { 42 | . = TARGET_DRAM_START; 43 | 44 | .start : { *(.reset) } 45 | .text : { *(.text) } 46 | .rodata : { *(.rodata*) } 47 | 48 | .init_array : { 49 | __global_ctors_start = .; 50 | *(.init_array*) 51 | __global_ctors_end = .; 52 | } 53 | 54 | .data : { *(.data) } 55 | .bss : { *(.bss COMMON) } 56 | 57 | . = ALIGN(0x1000); 58 | __heap_start = .; 59 | . = . + 0x10000; 60 | __heap_end = .; 61 | 62 | . = . + 0x1000; 63 | __stack_top = .; 64 | 65 | . = KASAN_SHADOW_MEMORY_START; 66 | __kasan_shadow_memory_start = .; 67 | . = . + KASAN_SHADOW_MEMORY_SIZE; 68 | __kasan_shadow_memory_end = .; 69 | } -------------------------------------------------------------------------------- /sanitized_lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "common.h" 15 | #include "heap.h" 16 | #include "printf.h" 17 | 18 | void test_heap_overflow(void) { 19 | int oob_index = 18; 20 | int size = 17; 21 | unsigned char *ptr = malloc(size); 22 | printf("\nKASan test: heap OOB write\n"); 23 | printf("Writing 1 byte at offset %d in %d-byte heap buffer allocated at %x\n", 24 | oob_index, size, ptr); 25 | ptr[oob_index] = 0; 26 | } 27 | 28 | char oob_value; 29 | 30 | void test_stack_overflow(void) { 31 | char buffer[17]; 32 | int oob_index = 18; 33 | printf("\nKASan test: stack OOB read\n"); 34 | printf("Reading 1 byte at offset %d in %d-byte stack buffer at %x\n", 35 | oob_index, sizeof(buffer), buffer); 36 | oob_value = buffer[oob_index]; 37 | } 38 | 39 | int global_array[17]; 40 | 41 | void test_globals_overflow(void) { 42 | int oob_index = 18; 43 | printf("\nKASan test: global OOB write\n"); 44 | printf( 45 | "Writing an integer at index %d in %d-element global integer array at " 46 | "%x\n", 47 | oob_index, sizeof(global_array) / sizeof(int), global_array); 48 | global_array[oob_index] = 0; 49 | } 50 | 51 | char global_char_buffer[17]; 52 | 53 | void test_memset_overflow(void) { 54 | int oob_size = 18; 55 | printf("\nKASan test: memset OOB write in globals\n"); 56 | printf("Memsetting global %d-byte buffer at %x with %d values of 0xaa\n", 57 | sizeof(global_char_buffer), global_char_buffer, oob_size); 58 | memset(global_char_buffer, 0xaa, oob_size); 59 | } 60 | 61 | void test_memcpy_overflow(void) { 62 | char buffer[18]; 63 | int oob_size = sizeof(buffer); 64 | printf("\nKASan test: memcpy OOB read from globals\n"); 65 | printf("Memcopying %d bytes from %d-byte global buffer into local array\n", 66 | oob_size, sizeof(global_char_buffer)); 67 | memcpy(buffer, global_char_buffer, oob_size); 68 | } -------------------------------------------------------------------------------- /rt_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "common.h" 15 | 16 | void my_memset(void *dest, int c, unsigned long n) { 17 | unsigned char *ptr = (unsigned char *)dest; 18 | for (size_t i = 0; i < n; i++) ptr[i] = (unsigned char)c; 19 | } 20 | 21 | void my_memcpy(void *dest, const void *src, unsigned long n) { 22 | unsigned char *ptr_dest = (unsigned char *)dest; 23 | const unsigned char *ptr_src = (const unsigned char *)src; 24 | for (size_t i = 0; i < n; i++) ptr_dest[i] = ptr_src[i]; 25 | } 26 | 27 | #ifdef TARGET_ARCH_arm 28 | 29 | void __aeabi_memset(void *dest, unsigned long n, int c) { 30 | my_memset(dest, c, n); 31 | } 32 | 33 | void __aeabi_memclr(void *dest, unsigned long n) { my_memset(dest, 0, n); } 34 | 35 | void __aeabi_memcpy(void *dest, const void *src, unsigned long n) { 36 | my_memcpy(dest, src, n); 37 | } 38 | 39 | #endif // TARGET_ARCH_arm 40 | 41 | void *memset(void *dest, int c, unsigned long n) { 42 | my_memset(dest, c, n); 43 | return dest; 44 | } 45 | 46 | void *memcpy(void *dest, const void *src, unsigned long n) { 47 | my_memcpy(dest, src, n); 48 | return dest; 49 | } 50 | 51 | typedef void (*global_ctor)(void); 52 | 53 | // These symbols are defined in the linker script. 54 | extern char __global_ctors_start; 55 | extern char __global_ctors_end; 56 | 57 | void call_global_ctors(void) { 58 | global_ctor *ctor = (global_ctor *)&__global_ctors_start; 59 | 60 | while (ctor != (global_ctor *)&__global_ctors_end) { 61 | (*ctor)(); 62 | ctor++; 63 | } 64 | } 65 | 66 | #ifdef TARGET_ARCH_x86 67 | 68 | void _putchar(char c) { 69 | uint16_t com1_port = 0x3f8; 70 | asm("outb %0, %1" : : "a"(c), "Nd"(com1_port)); 71 | } 72 | 73 | #else // TARGET_ARCH_x86 74 | 75 | // Macro UART_BASE_ADDRESS is defined in Makefile.$(ARCH) file 76 | void _putchar(char character) { 77 | volatile unsigned int *UART0DR_ADDRESS = 78 | (volatile unsigned int *)UART_BASE_ADDRESS; 79 | *UART0DR_ADDRESS = (unsigned int)character; 80 | } 81 | 82 | #endif // TARGET_ARCH_x86 -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # This program is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU General Public License 5 | # version 2 as published by the Free Software Foundation. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | 12 | CC := clang 13 | LD := ld.lld 14 | 15 | ARCH ?= arm 16 | SUPPORTED_ARCH := arm aarch64 riscv32 riscv64 x86 17 | 18 | ifeq ($(findstring $(ARCH),$(SUPPORTED_ARCH)),) 19 | $(error Unsupported architecture $(ARCH), choose one from $(SUPPORTED_ARCH)) 20 | endif 21 | 22 | include Makefile.$(ARCH) 23 | 24 | CFLAGS := $(CFLAGS_ARCH) 25 | CFLAGS += -ffreestanding 26 | 27 | CFLAGS += -DTARGET_ARCH_$(ARCH) 28 | CFLAGS += -DKASAN_SHADOW_MAPPING_OFFSET=$(KASAN_SHADOW_MAPPING_OFFSET) 29 | CFLAGS += -DKASAN_SHADOW_MEMORY_START=$(KASAN_SHADOW_MEMORY_START) 30 | CFLAGS += -DKASAN_SHADOW_MEMORY_SIZE=$(KASAN_SHADOW_MEMORY_SIZE) 31 | CFLAGS += -DTARGET_DRAM_START=$(TARGET_DRAM_START) 32 | CFLAGS += -DTARGET_DRAM_END=$(TARGET_DRAM_END) 33 | 34 | CFLAGS += -DPRINTF_DISABLE_SUPPORT_FLOAT 35 | CFLAGS += -DPRINTF_DISABLE_SUPPORT_EXPONENTIAL 36 | CFLAGS += -DPRINTF_DISABLE_SUPPORT_PTRDIFF_T 37 | CFLAGS += -DPRINTF_DISABLE_SUPPORT_LONG_LONG 38 | 39 | CFLAGS += -Wno-incompatible-library-redeclaration 40 | 41 | CFLAGS += -Ithird_party/printf -I./ 42 | 43 | LDFLAGS := -nostdlib 44 | 45 | # KASan-specific compiler options 46 | KASAN_SANITIZE_STACK := 1 47 | KASAN_SANITIZE_GLOBALS := 1 48 | 49 | KASAN_CC_FLAGS := -fsanitize=kernel-address 50 | KASAN_CC_FLAGS += -fno-builtin 51 | KASAN_CC_FLAGS += -mllvm -asan-mapping-offset=$(KASAN_SHADOW_MAPPING_OFFSET) 52 | KASAN_CC_FLAGS += -mllvm -asan-instrumentation-with-call-threshold=0 53 | KASAN_CC_FLAGS += -mllvm -asan-stack=$(KASAN_SANITIZE_STACK) 54 | KASAN_CC_FLAGS += -mllvm -asan-globals=$(KASAN_SANITIZE_GLOBALS) 55 | KASAN_CC_FLAGS += -DKASAN_ENABLED 56 | 57 | SRCS := kasan.c \ 58 | heap.c \ 59 | kasan_test.c \ 60 | sanitized_lib.c \ 61 | rt_utils.c \ 62 | start_$(ARCH).S \ 63 | third_party/printf/printf.c 64 | 65 | OBJS := $(SRCS:.c=.o) 66 | OBJS := $(OBJS:.S=.o) 67 | 68 | LD_SCRIPT := kasan_test.ld 69 | LD_SCRIPT_GEN := kasan_test.lds 70 | 71 | # Use KASAN_CC_FLAGS for the code we would like to cover with KASan 72 | sanitized_lib.o: CFLAGS := $(CFLAGS) $(KASAN_CC_FLAGS) 73 | 74 | # This workaround is not needed if you build the project with the LLVM 75 | # toolchain of version 18 and higher (i.e. which includes 76 | # https://github.com/llvm/llvm-project/pull/72933) 77 | sanitized_lib.o: CFLAGS := $(subst $(ARCH_TARGET),$(KASAN_TARGET),$(CFLAGS)) 78 | 79 | %.o: %.c 80 | $(CC) $(CFLAGS) -c $< -o $@ 81 | 82 | %.o: %.S 83 | $(CC) $(CFLAGS) -c $< -o $@ 84 | 85 | $(LD_SCRIPT_GEN): $(LD_SCRIPT) 86 | $(CC) -E -P -x c $(CFLAGS) $< >> $@ 87 | 88 | kasan_test: $(LD_SCRIPT_GEN) $(OBJS) 89 | $(LD) -T $(LD_SCRIPT_GEN) $(LDFLAGS) $(OBJS) -o $@ 90 | 91 | .PHONY: run 92 | run: kasan_test 93 | $(QEMU) $< 94 | 95 | .PHONY: clean 96 | clean: 97 | rm -rf kasan_test $(OBJS) $(LD_SCRIPT_GEN) 98 | rm -rf $(foreach arch,$(SUPPORTED_ARCH),start_$(arch).o) 99 | -------------------------------------------------------------------------------- /third_party/printf/printf.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // \author (c) Marco Paland (info@paland.com) 3 | // 2014-2019, PALANDesign Hannover, Germany 4 | // 5 | // \license The MIT License (MIT) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on 26 | // embedded systems with a very limited resources. 27 | // Use this instead of bloated standard/newlib printf. 28 | // These routines are thread safe and reentrant. 29 | // 30 | /////////////////////////////////////////////////////////////////////////////// 31 | 32 | #ifndef _PRINTF_H_ 33 | #define _PRINTF_H_ 34 | 35 | #include 36 | #include 37 | 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | 44 | /** 45 | * Output a character to a custom device like UART, used by the printf() function 46 | * This function is declared here only. You have to write your custom implementation somewhere 47 | * \param character Character to output 48 | */ 49 | void _putchar(char character); 50 | 51 | 52 | /** 53 | * Tiny printf implementation 54 | * You have to implement _putchar if you use printf() 55 | * To avoid conflicts with the regular printf() API it is overridden by macro defines 56 | * and internal underscore-appended functions like printf_() are used 57 | * \param format A string that specifies the format of the output 58 | * \return The number of characters that are written into the array, not counting the terminating null character 59 | */ 60 | #define printf printf_ 61 | int printf_(const char* format, ...); 62 | 63 | 64 | /** 65 | * Tiny sprintf implementation 66 | * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! 67 | * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! 68 | * \param format A string that specifies the format of the output 69 | * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character 70 | */ 71 | #define sprintf sprintf_ 72 | int sprintf_(char* buffer, const char* format, ...); 73 | 74 | 75 | /** 76 | * Tiny snprintf/vsnprintf implementation 77 | * \param buffer A pointer to the buffer where to store the formatted string 78 | * \param count The maximum number of characters to store in the buffer, including a terminating null character 79 | * \param format A string that specifies the format of the output 80 | * \param va A value identifying a variable arguments list 81 | * \return The number of characters that COULD have been written into the buffer, not counting the terminating 82 | * null character. A value equal or larger than count indicates truncation. Only when the returned value 83 | * is non-negative and less than count, the string has been completely written. 84 | */ 85 | #define snprintf snprintf_ 86 | #define vsnprintf vsnprintf_ 87 | int snprintf_(char* buffer, size_t count, const char* format, ...); 88 | int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); 89 | 90 | 91 | /** 92 | * Tiny vprintf implementation 93 | * \param format A string that specifies the format of the output 94 | * \param va A value identifying a variable arguments list 95 | * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character 96 | */ 97 | #define vprintf vprintf_ 98 | int vprintf_(const char* format, va_list va); 99 | 100 | 101 | /** 102 | * printf with output function 103 | * You may use this as dynamic alternative to printf() with its fixed _putchar() output 104 | * \param out An output function which takes one character and an argument pointer 105 | * \param arg An argument pointer for user data passed to output function 106 | * \param format A string that specifies the format of the output 107 | * \return The number of characters that are sent to the output function, not counting the terminating null character 108 | */ 109 | int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); 110 | 111 | 112 | #ifdef __cplusplus 113 | } 114 | #endif 115 | 116 | 117 | #endif // _PRINTF_H_ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bare-metal KASan implementation 2 | 3 | *This is not an officially supported Google product.* 4 | 5 | This project demonstrates how to enable Kernel Address Sanitizer (KASan) for 6 | bare-metal code running on ARM, RISC-V and x86 architectures. It implements a 7 | set of KASan test cases that catch various classes of memory corruption bugs 8 | at runtime. 9 | 10 | The implementation of KASan run-time routines in this project is inspired by 11 | the corresponding implementation of 12 | [KASan](https://www.kernel.org/doc/html/v4.14/dev-tools/kasan.html) in Linux 13 | kernel. 14 | 15 | ## Prerequisites 16 | 17 | To build and run the program you would need to use LLVM toolchain (`clang` and 18 | `ld.lld`) for cross complitation and QEMU system emulator for supported 19 | architectures. 20 | 21 | For example, here are the necessary Debian package names needed to build and 22 | run the project: 23 | 24 | ``` 25 | sudo apt-get install build-essential gcc-multilib llvm clang lld \ 26 | qemu-system-arm qemu-system-misc qemu-system-x86 27 | ``` 28 | 29 | ## Project layout 30 | 31 | The project constists of the following components: 32 | 33 | * `kasan_test.c` -- main test driver which runs KASan test cases 34 | * `sanitized_lib.c` -- this module implements the test cases and is built with 35 | the KASan instrumentation 36 | * `kasan.c` -- implementation of runtime routines needed for KASan sanitizer 37 | * `heap.c` -- simple implementation of heap management routines for testing 38 | KASan 39 | * `third_party/printf.c` -- a compact implementation of `printf` function 40 | * `rt_utils.c` -- run-time utility functions 41 | * `start_arch.S` -- architecture-specific low-level entry point in assembly 42 | for the bare-metal program 43 | * `kasan_test.ld` -- linker script for the program 44 | * `Makefile` -- in addition to instructions on how to build and run the project 45 | this file contains definitions of some important parameters, 46 | such as KASan shadow memory address, DRAM start address and KASan 47 | configuration options. 48 | * `Makefile.arch` -- Makefile fragments whith architecture-specific parameters 49 | for building and running the project in the emulator 50 | 51 | 52 | ## Running 53 | 54 | To build and execute the test suite run `ARCH=target_arch make clean run` 55 | where `target_arch` is one of the supported architectures: `arm`, `aarch64`, 56 | `riscv32`, `riscv64` and `x86`. If the target architecture isn't specified 57 | (i.e. `make clean run`) then `arm` is assumed as default option. 58 | 59 | As an example, running `make clean run` should build the bare-metal program 60 | and execute it in QEMU ARM system emulator with the following expected output: 61 | 62 | ``` 63 | qemu-system-arm -M virt-8.2 -cpu cortex-a7 -m 256M -nographic -kernel kasan_test 64 | Starting bare-metal KASan test driver. 65 | 66 | KASan test: heap OOB write 67 | Writing 1 byte at offset 18 in 17-byte heap buffer allocated at 40004020 68 | [KASan] =================================================== 69 | [KASan] ERROR: Invalid memory access: address 0x40004032, size 0x1, is_write 1, ip 0x40000D98 70 | [KASan] Shadow bytes around the buggy address 0x40004030 (shadow 0x4A700806): 71 | [KASan] 0x4A7007D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 72 | [KASan] 0x4A7007E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 73 | [KASan] 0x4A7007F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 74 | [KASan] =>0x4A700800: FA FA FA FA 00 00[01]FB FB FB FB 00 00 00 00 00 75 | [KASan] 0x4A700810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 76 | [KASan] 0x4A700820: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 77 | [KASan] 0x4A700830: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 | 79 | KASan test: stack OOB read 80 | Reading 1 byte at offset 18 in 17-byte stack buffer at 40014f90 81 | [KASan] =================================================== 82 | [KASan] ERROR: Invalid memory access: address 0x40014FA2, size 0x1, is_write 0, ip 0x40000E6C 83 | [KASan] Shadow bytes around the buggy address 0x40014FA0 (shadow 0x4A7029F4): 84 | [KASan] 0x4A7029C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 85 | [KASan] 0x4A7029D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 86 | [KASan] 0x4A7029E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 87 | [KASan] =>0x4A7029F0: F1 F1 00 00[01]F3 F3 F3 F3 F3 00 00 00 00 00 00 88 | [KASan] 0x4A702A00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 89 | [KASan] 0x4A702A10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90 | [KASan] 0x4A702A20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 91 | 92 | KASan test: global OOB write 93 | Writing an integer at index 18 in 17-element global integer array at 400035a0 94 | [KASan] =================================================== 95 | [KASan] ERROR: Invalid memory access: address 0x400035E8, size 0x4, is_write 1, ip 0x40000F38 96 | [KASan] Shadow bytes around the buggy address 0x400035E8 (shadow 0x4A7006BD): 97 | [KASan] 0x4A700680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 98 | [KASan] 0x4A700690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 99 | [KASan] 0x4A7006A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 100 | [KASan] =>0x4A7006B0: 01 F9 F9 F9 00 00 00 00 00 00 00 00 04[F9]F9 F9 101 | [KASan] 0x4A7006C0: F9 F9 F9 F9 00 00 01 F9 F9 F9 F9 F9 00 00 00 00 102 | [KASan] 0x4A7006D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 103 | [KASan] 0x4A7006E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 104 | 105 | KASan test: memset OOB write in globals 106 | Memsetting global 17-byte buffer at 40003620 with 18 values of 0xaa 107 | [KASan] =================================================== 108 | [KASan] ERROR: Invalid memory access: address 0x40003620, size 0x12, is_write 1, ip 0x40000D1C 109 | [KASan] Shadow bytes around the buggy address 0x40003630 (shadow 0x4A7006C6): 110 | [KASan] 0x4A700690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 111 | [KASan] 0x4A7006A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 112 | [KASan] 0x4A7006B0: 01 F9 F9 F9 00 00 00 00 00 00 00 00 04 F9 F9 F9 113 | [KASan] =>0x4A7006C0: F9 F9 F9 F9 00 00[01]F9 F9 F9 F9 F9 00 00 00 00 114 | [KASan] 0x4A7006D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 115 | [KASan] 0x4A7006E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 116 | [KASan] 0x4A7006F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 117 | 118 | KASan test: memcpy OOB read from globals 119 | Memcopying 18 bytes from 17-byte global buffer into local array 120 | [KASan] =================================================== 121 | [KASan] ERROR: Invalid memory access: address 0x40003620, size 0x12, is_write 0, ip 0x40000D20 122 | [KASan] Shadow bytes around the buggy address 0x40003630 (shadow 0x4A7006C6): 123 | [KASan] 0x4A700690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 124 | [KASan] 0x4A7006A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 125 | [KASan] 0x4A7006B0: 01 F9 F9 F9 00 00 00 00 00 00 00 00 04 F9 F9 F9 126 | [KASan] =>0x4A7006C0: F9 F9 F9 F9 00 00[01]F9 F9 F9 F9 F9 00 00 00 00 127 | [KASan] 0x4A7006D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 128 | [KASan] 0x4A7006E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 129 | [KASan] 0x4A7006F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 130 | Press ctrl + a then x to exit. 131 | ``` -------------------------------------------------------------------------------- /kasan.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Google LLC 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * version 2 as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | */ 13 | 14 | #include "common.h" 15 | #include "heap.h" 16 | #include "printf.h" 17 | 18 | #define KASAN_SHADOW_SHIFT 3 19 | #define KASAN_SHADOW_GRANULE_SIZE (1UL << KASAN_SHADOW_SHIFT) 20 | #define KASAN_SHADOW_MASK (KASAN_SHADOW_GRANULE_SIZE - 1) 21 | 22 | #define ASAN_SHADOW_UNPOISONED_MAGIC 0x00 23 | #define ASAN_SHADOW_RESERVED_MAGIC 0xff 24 | #define ASAN_SHADOW_GLOBAL_REDZONE_MAGIC 0xf9 25 | #define ASAN_SHADOW_HEAP_HEAD_REDZONE_MAGIC 0xfa 26 | #define ASAN_SHADOW_HEAP_TAIL_REDZONE_MAGIC 0xfb 27 | #define ASAN_SHADOW_HEAP_FREE_MAGIC 0xfd 28 | 29 | #define KASAN_HEAP_HEAD_REDZONE_SIZE 0x20 30 | #define KASAN_HEAP_TAIL_REDZONE_SIZE 0x20 31 | 32 | #define KASAN_MEM_TO_SHADOW(addr) \ 33 | (((addr) >> KASAN_SHADOW_SHIFT) + KASAN_SHADOW_MAPPING_OFFSET) 34 | #define KASAN_SHADOW_TO_MEM(shadow) \ 35 | (((shadow) - KASAN_SHADOW_MAPPING_OFFSET) << KASAN_SHADOW_SHIFT) 36 | 37 | void kasan_bug_report(unsigned long addr, size_t size, 38 | unsigned long buggy_shadow_address, uint8_t is_write, 39 | unsigned long ip); 40 | 41 | static inline unsigned long get_poisoned_shadow_address(unsigned long addr, 42 | size_t size) { 43 | unsigned long addr_shadow_start = KASAN_MEM_TO_SHADOW(addr); 44 | unsigned long addr_shadow_end = KASAN_MEM_TO_SHADOW(addr + size - 1) + 1; 45 | unsigned long non_zero_shadow_addr = 0; 46 | 47 | for (unsigned long i = 0; i < addr_shadow_end - addr_shadow_start; i++) { 48 | if (*(uint8_t *)(addr_shadow_start + i) != 0) { 49 | non_zero_shadow_addr = addr_shadow_start + i; 50 | break; 51 | } 52 | } 53 | 54 | if (non_zero_shadow_addr) { 55 | unsigned long last_byte = addr + size - 1; 56 | int8_t *last_shadow_byte = (int8_t *)KASAN_MEM_TO_SHADOW(last_byte); 57 | 58 | // Non-zero bytes in shadow memory may indicate either: 59 | // 1) invalid memory access (0xff, 0xfa, ...) 60 | // 2) access to a 8-byte region which isn't entirely accessible, i.e. only 61 | // n bytes can be read/written in the 8-byte region, where n < 8 62 | // (in this case shadow byte encodes how much bytes in an 8-byte region 63 | // are accessible). 64 | // Thus, if there is a non-zero shadow byte we need to check if it 65 | // corresponds to the last byte in the checked region: 66 | // not last - OOB memory access 67 | // last - check if we don't access beyond what's encoded in the shadow 68 | // byte. 69 | if (non_zero_shadow_addr != (unsigned long)last_shadow_byte || 70 | ((int8_t)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow_byte)) 71 | return non_zero_shadow_addr; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | // Both `address` and `size` must be 8-byte aligned. 78 | static void poison_shadow(unsigned long address, size_t size, uint8_t value) { 79 | unsigned long shadow_start, shadow_end; 80 | size_t shadow_length = 0; 81 | 82 | shadow_start = KASAN_MEM_TO_SHADOW(address); 83 | shadow_end = KASAN_MEM_TO_SHADOW(address + size - 1) + 1; 84 | shadow_length = shadow_end - shadow_start; 85 | 86 | memset((void *)shadow_start, value, shadow_length); 87 | } 88 | 89 | // `address` must be 8-byte aligned 90 | static void unpoison_shadow(unsigned long address, size_t size) { 91 | poison_shadow(address, size & (~KASAN_SHADOW_MASK), 92 | ASAN_SHADOW_UNPOISONED_MAGIC); 93 | 94 | if (size & KASAN_SHADOW_MASK) { 95 | uint8_t *shadow = (uint8_t *)KASAN_MEM_TO_SHADOW(address + size); 96 | *shadow = size & KASAN_SHADOW_MASK; 97 | } 98 | } 99 | 100 | static inline int kasan_check_memory(unsigned long addr, size_t size, 101 | uint8_t write, unsigned long pc) { 102 | int buggy_shadow_address; 103 | if (size == 0) return 1; 104 | 105 | // there is 256 MB of RAM starting at 0x40000000 106 | if (addr < TARGET_DRAM_START || addr > TARGET_DRAM_END) return 1; 107 | 108 | buggy_shadow_address = get_poisoned_shadow_address(addr, size); 109 | if (buggy_shadow_address == 0) return 1; 110 | 111 | kasan_bug_report(addr, size, buggy_shadow_address, write, pc); 112 | return 0; 113 | } 114 | 115 | // Implement necessary routines for KASan sanitization of globals. 116 | 117 | // See struct __asan_global definition at 118 | // https://github.com/llvm-mirror/compiler-rt/blob/master/lib/asan/asan_interface_internal.h. 119 | struct kasan_global_info { 120 | // Starting address of the variable 121 | const void *start; 122 | // Variable size 123 | size_t size; 124 | // 32-bit aligned size of global including the redzone 125 | size_t size_with_redzone; 126 | // Symbol name 127 | const void *name; 128 | const void *module_name; 129 | unsigned long has_dynamic_init; 130 | void *location; 131 | unsigned int odr_indicator; 132 | }; 133 | 134 | static void asan_register_global(struct kasan_global_info *global) { 135 | unpoison_shadow((unsigned long)global->start, global->size); 136 | 137 | size_t aligned_size = (global->size + KASAN_SHADOW_MASK) & ~KASAN_SHADOW_MASK; 138 | poison_shadow((unsigned long)global->start + aligned_size, 139 | global->size_with_redzone - aligned_size, 140 | ASAN_SHADOW_GLOBAL_REDZONE_MAGIC); 141 | } 142 | 143 | void __asan_register_globals(struct kasan_global_info *globals, size_t size) { 144 | for (size_t i = 0; i < size; i++) asan_register_global(&globals[i]); 145 | } 146 | 147 | void __asan_unregister_globals(void *globals, size_t size) {} 148 | 149 | // Empty placeholder implementation to supress linker error for undefined symbol 150 | void __asan_handle_no_return(void) {} 151 | 152 | // KASan memcpy/memset hooks. 153 | 154 | void *__kasan_memcpy(void *dst, const void *src, unsigned int size, 155 | unsigned long pc) { 156 | kasan_check_memory((unsigned long)dst, size, /*is_write*/ true, pc); 157 | kasan_check_memory((unsigned long)src, size, /*is_write*/ false, pc); 158 | 159 | return memcpy(dst, src, size); 160 | } 161 | 162 | void *__kasan_memset(void *buf, int c, unsigned int size, unsigned long pc) { 163 | kasan_check_memory((unsigned long)buf, size, /*is_write*/ true, pc); 164 | 165 | return memset(buf, c, size); 166 | } 167 | 168 | // Implement KASan heap management hooks. 169 | 170 | struct KASAN_HEAP_HEADER { 171 | unsigned int aligned_size; 172 | }; 173 | 174 | void *kasan_malloc_hook(unsigned int size) { 175 | struct KASAN_HEAP_HEADER *kasan_heap_hdr = NULL; 176 | unsigned int algined_size = (size + KASAN_SHADOW_MASK) & (~KASAN_SHADOW_MASK); 177 | unsigned int total_size = algined_size + KASAN_HEAP_HEAD_REDZONE_SIZE + 178 | KASAN_HEAP_TAIL_REDZONE_SIZE; 179 | 180 | void *ptr = allocate_chunk(total_size); 181 | if (ptr == NULL) return NULL; 182 | 183 | kasan_heap_hdr = (struct KASAN_HEAP_HEADER *)ptr; 184 | kasan_heap_hdr->aligned_size = algined_size; 185 | 186 | unpoison_shadow((unsigned long)(ptr + KASAN_HEAP_HEAD_REDZONE_SIZE), size); 187 | poison_shadow((unsigned long)ptr, KASAN_HEAP_HEAD_REDZONE_SIZE, 188 | ASAN_SHADOW_HEAP_HEAD_REDZONE_MAGIC); 189 | poison_shadow( 190 | (unsigned long)(ptr + KASAN_HEAP_HEAD_REDZONE_SIZE + algined_size), 191 | KASAN_HEAP_TAIL_REDZONE_SIZE, ASAN_SHADOW_HEAP_TAIL_REDZONE_MAGIC); 192 | 193 | return ptr + KASAN_HEAP_HEAD_REDZONE_SIZE; 194 | } 195 | 196 | void kasan_free_hook(void *ptr) { 197 | struct KASAN_HEAP_HEADER *kasan_heap_hdr = NULL; 198 | unsigned int aligned_size = 0; 199 | 200 | if (ptr == NULL) return; 201 | 202 | kasan_heap_hdr = 203 | (struct KASAN_HEAP_HEADER *)(ptr - KASAN_HEAP_HEAD_REDZONE_SIZE); 204 | aligned_size = kasan_heap_hdr->aligned_size; 205 | 206 | free_chunk(kasan_heap_hdr); 207 | poison_shadow((unsigned long)ptr, aligned_size, ASAN_SHADOW_HEAP_FREE_MAGIC); 208 | 209 | return; 210 | } 211 | 212 | // Implement KAsan error reporting routines. 213 | 214 | static void kasan_print_16_bytes_no_bug(const char *prefix, 215 | unsigned long address) { 216 | printf("%s0x%X:", prefix, address); 217 | for (int i = 0; i < 16; i++) printf(" %02X", *(uint8_t *)(address + i)); 218 | printf("\n"); 219 | } 220 | 221 | static void kasan_print_16_bytes_with_bug(const char *prefix, 222 | unsigned long address, 223 | int buggy_offset) { 224 | printf("%s0x%X:", prefix, address); 225 | for (int i = 0; i < buggy_offset; i++) 226 | printf(" %02X", *(uint8_t *)(address + i)); 227 | printf("[%02X]", *(uint8_t *)(address + buggy_offset)); 228 | if (buggy_offset < 15) 229 | printf("%02X", *(uint8_t *)(address + buggy_offset + 1)); 230 | for (int i = buggy_offset + 2; i < 16; i++) 231 | printf(" %02X", *(uint8_t *)(address + i)); 232 | printf("\n"); 233 | } 234 | 235 | static void kasan_print_shadow_memory(unsigned long address, int range_before, 236 | int range_after) { 237 | unsigned long shadow_address = KASAN_MEM_TO_SHADOW(address); 238 | unsigned long aligned_shadow = shadow_address & 0xfffffff0; 239 | int buggy_offset = shadow_address - aligned_shadow; 240 | 241 | printf("[KASan] Shadow bytes around the buggy address 0x%X (shadow 0x%X):\n", 242 | address, shadow_address); 243 | 244 | for (int i = range_before; i > 0; i--) { 245 | kasan_print_16_bytes_no_bug("[KASan] ", aligned_shadow - i * 16); 246 | } 247 | 248 | kasan_print_16_bytes_with_bug("[KASan] =>", aligned_shadow, buggy_offset); 249 | 250 | for (int i = 1; i <= range_after; i++) { 251 | kasan_print_16_bytes_no_bug("[KASan] ", aligned_shadow + i * 16); 252 | } 253 | } 254 | 255 | void kasan_bug_report(unsigned long addr, size_t size, 256 | unsigned long buggy_shadow_address, uint8_t is_write, 257 | unsigned long ip) { 258 | unsigned long buggy_address = KASAN_SHADOW_TO_MEM(buggy_shadow_address); 259 | printf("[KASan] ===================================================\n"); 260 | printf( 261 | "[KASan] ERROR: Invalid memory access: address 0x%X, size 0x%X, is_write " 262 | "%d, ip 0x%X\n", 263 | addr, size, is_write, ip); 264 | 265 | kasan_print_shadow_memory(buggy_address, 3, 3); 266 | } 267 | 268 | void initialize_kasan(void) { 269 | // Mark shadow memory region not accessible by the sanitized code. 270 | poison_shadow(KASAN_SHADOW_MEMORY_START, KASAN_SHADOW_MEMORY_SIZE, 271 | ASAN_SHADOW_RESERVED_MAGIC); 272 | } 273 | 274 | // Define KASan handlers exposed used by the compiler instrumentation. 275 | 276 | void __asan_loadN_noabort(unsigned int addr, unsigned int size) { 277 | kasan_check_memory(addr, size, /*is_write*/ false, CALLER_PC); 278 | } 279 | 280 | void __asan_storeN_noabort(unsigned int addr, size_t size) { 281 | kasan_check_memory(addr, size, /*is_write*/ true, CALLER_PC); 282 | } 283 | 284 | #define DEFINE_KASAN_LOAD_STORE_ROUTINES(size) \ 285 | void __asan_load##size##_noabort(unsigned long addr) { \ 286 | kasan_check_memory(addr, size, /*is_write*/ false, CALLER_PC); \ 287 | } \ 288 | void __asan_store##size##_noabort(unsigned long addr) { \ 289 | kasan_check_memory(addr, size, /*is_write*/ true, CALLER_PC); \ 290 | } 291 | 292 | DEFINE_KASAN_LOAD_STORE_ROUTINES(1) 293 | DEFINE_KASAN_LOAD_STORE_ROUTINES(2) 294 | DEFINE_KASAN_LOAD_STORE_ROUTINES(4) 295 | DEFINE_KASAN_LOAD_STORE_ROUTINES(8) 296 | DEFINE_KASAN_LOAD_STORE_ROUTINES(16) 297 | 298 | // Local variable KASan instrumentation 299 | #define DEFINE_KASAN_SET_SHADOW_ROUTINE(byte) \ 300 | void __asan_set_shadow_##byte(void *addr, size_t size) { \ 301 | memset(addr, 0x##byte, size); \ 302 | } 303 | 304 | DEFINE_KASAN_SET_SHADOW_ROUTINE(00) // addressable memory 305 | DEFINE_KASAN_SET_SHADOW_ROUTINE(f1) // stack left redzone 306 | DEFINE_KASAN_SET_SHADOW_ROUTINE(f2) // stack mid redzone 307 | DEFINE_KASAN_SET_SHADOW_ROUTINE(f3) // stack right redzone -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /third_party/printf/printf.c: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // \author (c) Marco Paland (info@paland.com) 3 | // 2014-2019, PALANDesign Hannover, Germany 4 | // 5 | // \license The MIT License (MIT) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | // \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on 26 | // embedded systems with a very limited resources. These routines are thread 27 | // safe and reentrant! 28 | // Use this instead of the bloated standard/newlib printf cause these use 29 | // malloc for printf (and may not be thread safe). 30 | // 31 | /////////////////////////////////////////////////////////////////////////////// 32 | 33 | #include 34 | #include 35 | 36 | #include "printf.h" 37 | 38 | 39 | // define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the 40 | // printf_config.h header file 41 | // default: undefined 42 | #ifdef PRINTF_INCLUDE_CONFIG_H 43 | #include "printf_config.h" 44 | #endif 45 | 46 | 47 | // 'ntoa' conversion buffer size, this must be big enough to hold one converted 48 | // numeric number including padded zeros (dynamically created on stack) 49 | // default: 32 byte 50 | #ifndef PRINTF_NTOA_BUFFER_SIZE 51 | #define PRINTF_NTOA_BUFFER_SIZE 32U 52 | #endif 53 | 54 | // 'ftoa' conversion buffer size, this must be big enough to hold one converted 55 | // float number including padded zeros (dynamically created on stack) 56 | // default: 32 byte 57 | #ifndef PRINTF_FTOA_BUFFER_SIZE 58 | #define PRINTF_FTOA_BUFFER_SIZE 32U 59 | #endif 60 | 61 | // support for the floating point type (%f) 62 | // default: activated 63 | #ifndef PRINTF_DISABLE_SUPPORT_FLOAT 64 | #define PRINTF_SUPPORT_FLOAT 65 | #endif 66 | 67 | // support for exponential floating point notation (%e/%g) 68 | // default: activated 69 | #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL 70 | #define PRINTF_SUPPORT_EXPONENTIAL 71 | #endif 72 | 73 | // define the default floating point precision 74 | // default: 6 digits 75 | #ifndef PRINTF_DEFAULT_FLOAT_PRECISION 76 | #define PRINTF_DEFAULT_FLOAT_PRECISION 6U 77 | #endif 78 | 79 | // define the largest float suitable to print with %f 80 | // default: 1e9 81 | #ifndef PRINTF_MAX_FLOAT 82 | #define PRINTF_MAX_FLOAT 1e9 83 | #endif 84 | 85 | // support for the long long types (%llu or %p) 86 | // default: activated 87 | #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG 88 | #define PRINTF_SUPPORT_LONG_LONG 89 | #endif 90 | 91 | // support for the ptrdiff_t type (%t) 92 | // ptrdiff_t is normally defined in as long or long long type 93 | // default: activated 94 | #ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T 95 | #define PRINTF_SUPPORT_PTRDIFF_T 96 | #endif 97 | 98 | /////////////////////////////////////////////////////////////////////////////// 99 | 100 | // internal flag definitions 101 | #define FLAGS_ZEROPAD (1U << 0U) 102 | #define FLAGS_LEFT (1U << 1U) 103 | #define FLAGS_PLUS (1U << 2U) 104 | #define FLAGS_SPACE (1U << 3U) 105 | #define FLAGS_HASH (1U << 4U) 106 | #define FLAGS_UPPERCASE (1U << 5U) 107 | #define FLAGS_CHAR (1U << 6U) 108 | #define FLAGS_SHORT (1U << 7U) 109 | #define FLAGS_LONG (1U << 8U) 110 | #define FLAGS_LONG_LONG (1U << 9U) 111 | #define FLAGS_PRECISION (1U << 10U) 112 | #define FLAGS_ADAPT_EXP (1U << 11U) 113 | 114 | 115 | // import float.h for DBL_MAX 116 | #if defined(PRINTF_SUPPORT_FLOAT) 117 | #include 118 | #endif 119 | 120 | 121 | // output function type 122 | typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); 123 | 124 | 125 | // wrapper (used as buffer) for output function type 126 | typedef struct { 127 | void (*fct)(char character, void* arg); 128 | void* arg; 129 | } out_fct_wrap_type; 130 | 131 | 132 | // internal buffer output 133 | static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) 134 | { 135 | if (idx < maxlen) { 136 | ((char*)buffer)[idx] = character; 137 | } 138 | } 139 | 140 | 141 | // internal null output 142 | static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) 143 | { 144 | (void)character; (void)buffer; (void)idx; (void)maxlen; 145 | } 146 | 147 | 148 | // internal _putchar wrapper 149 | static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) 150 | { 151 | (void)buffer; (void)idx; (void)maxlen; 152 | if (character) { 153 | _putchar(character); 154 | } 155 | } 156 | 157 | 158 | // internal output function wrapper 159 | static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) 160 | { 161 | (void)idx; (void)maxlen; 162 | if (character) { 163 | // buffer is the output fct pointer 164 | ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); 165 | } 166 | } 167 | 168 | 169 | // internal secure strlen 170 | // \return The length of the string (excluding the terminating 0) limited by 'maxsize' 171 | static inline unsigned int _strnlen_s(const char* str, size_t maxsize) 172 | { 173 | const char* s; 174 | for (s = str; *s && maxsize--; ++s); 175 | return (unsigned int)(s - str); 176 | } 177 | 178 | 179 | // internal test if char is a digit (0-9) 180 | // \return true if char is a digit 181 | static inline bool _is_digit(char ch) 182 | { 183 | return (ch >= '0') && (ch <= '9'); 184 | } 185 | 186 | 187 | // internal ASCII string to unsigned int conversion 188 | static unsigned int _atoi(const char** str) 189 | { 190 | unsigned int i = 0U; 191 | while (_is_digit(**str)) { 192 | i = i * 10U + (unsigned int)(*((*str)++) - '0'); 193 | } 194 | return i; 195 | } 196 | 197 | 198 | // output the specified string in reverse, taking care of any zero-padding 199 | static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) 200 | { 201 | const size_t start_idx = idx; 202 | 203 | // pad spaces up to given width 204 | if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { 205 | for (size_t i = len; i < width; i++) { 206 | out(' ', buffer, idx++, maxlen); 207 | } 208 | } 209 | 210 | // reverse string 211 | while (len) { 212 | out(buf[--len], buffer, idx++, maxlen); 213 | } 214 | 215 | // append pad spaces up to given width 216 | if (flags & FLAGS_LEFT) { 217 | while (idx - start_idx < width) { 218 | out(' ', buffer, idx++, maxlen); 219 | } 220 | } 221 | 222 | return idx; 223 | } 224 | 225 | 226 | // internal itoa format 227 | static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) 228 | { 229 | // pad leading zeros 230 | if (!(flags & FLAGS_LEFT)) { 231 | if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { 232 | width--; 233 | } 234 | while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 235 | buf[len++] = '0'; 236 | } 237 | while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 238 | buf[len++] = '0'; 239 | } 240 | } 241 | 242 | // handle hash 243 | if (flags & FLAGS_HASH) { 244 | if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { 245 | len--; 246 | if (len && (base == 16U)) { 247 | len--; 248 | } 249 | } 250 | if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 251 | buf[len++] = 'x'; 252 | } 253 | else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 254 | buf[len++] = 'X'; 255 | } 256 | else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { 257 | buf[len++] = 'b'; 258 | } 259 | if (len < PRINTF_NTOA_BUFFER_SIZE) { 260 | buf[len++] = '0'; 261 | } 262 | } 263 | 264 | if (len < PRINTF_NTOA_BUFFER_SIZE) { 265 | if (negative) { 266 | buf[len++] = '-'; 267 | } 268 | else if (flags & FLAGS_PLUS) { 269 | buf[len++] = '+'; // ignore the space if the '+' exists 270 | } 271 | else if (flags & FLAGS_SPACE) { 272 | buf[len++] = ' '; 273 | } 274 | } 275 | 276 | return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); 277 | } 278 | 279 | 280 | // internal itoa for 'long' type 281 | static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) 282 | { 283 | char buf[PRINTF_NTOA_BUFFER_SIZE]; 284 | size_t len = 0U; 285 | 286 | // no hash for 0 values 287 | if (!value) { 288 | flags &= ~FLAGS_HASH; 289 | } 290 | 291 | // write if precision != 0 and value is != 0 292 | if (!(flags & FLAGS_PRECISION) || value) { 293 | do { 294 | const char digit = (char)(value % base); 295 | buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; 296 | value /= base; 297 | } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); 298 | } 299 | 300 | return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); 301 | } 302 | 303 | 304 | // internal itoa for 'long long' type 305 | #if defined(PRINTF_SUPPORT_LONG_LONG) 306 | static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) 307 | { 308 | char buf[PRINTF_NTOA_BUFFER_SIZE]; 309 | size_t len = 0U; 310 | 311 | // no hash for 0 values 312 | if (!value) { 313 | flags &= ~FLAGS_HASH; 314 | } 315 | 316 | // write if precision != 0 and value is != 0 317 | if (!(flags & FLAGS_PRECISION) || value) { 318 | do { 319 | const char digit = (char)(value % base); 320 | buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; 321 | value /= base; 322 | } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); 323 | } 324 | 325 | return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); 326 | } 327 | #endif // PRINTF_SUPPORT_LONG_LONG 328 | 329 | 330 | #if defined(PRINTF_SUPPORT_FLOAT) 331 | 332 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 333 | // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT 334 | static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); 335 | #endif 336 | 337 | 338 | // internal ftoa for fixed decimal floating point 339 | static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) 340 | { 341 | char buf[PRINTF_FTOA_BUFFER_SIZE]; 342 | size_t len = 0U; 343 | double diff = 0.0; 344 | 345 | // powers of 10 346 | static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; 347 | 348 | // test for special values 349 | if (value != value) 350 | return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); 351 | if (value < -DBL_MAX) 352 | return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); 353 | if (value > DBL_MAX) 354 | return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); 355 | 356 | // test for very large values 357 | // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad 358 | if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { 359 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 360 | return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); 361 | #else 362 | return 0U; 363 | #endif 364 | } 365 | 366 | // test for negative 367 | bool negative = false; 368 | if (value < 0) { 369 | negative = true; 370 | value = 0 - value; 371 | } 372 | 373 | // set default precision, if not set explicitly 374 | if (!(flags & FLAGS_PRECISION)) { 375 | prec = PRINTF_DEFAULT_FLOAT_PRECISION; 376 | } 377 | // limit precision to 9, cause a prec >= 10 can lead to overflow errors 378 | while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { 379 | buf[len++] = '0'; 380 | prec--; 381 | } 382 | 383 | int whole = (int)value; 384 | double tmp = (value - whole) * pow10[prec]; 385 | unsigned long frac = (unsigned long)tmp; 386 | diff = tmp - frac; 387 | 388 | if (diff > 0.5) { 389 | ++frac; 390 | // handle rollover, e.g. case 0.99 with prec 1 is 1.0 391 | if (frac >= pow10[prec]) { 392 | frac = 0; 393 | ++whole; 394 | } 395 | } 396 | else if (diff < 0.5) { 397 | } 398 | else if ((frac == 0U) || (frac & 1U)) { 399 | // if halfway, round up if odd OR if last digit is 0 400 | ++frac; 401 | } 402 | 403 | if (prec == 0U) { 404 | diff = value - (double)whole; 405 | if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { 406 | // exactly 0.5 and ODD, then round up 407 | // 1.5 -> 2, but 2.5 -> 2 408 | ++whole; 409 | } 410 | } 411 | else { 412 | unsigned int count = prec; 413 | // now do fractional part, as an unsigned number 414 | while (len < PRINTF_FTOA_BUFFER_SIZE) { 415 | --count; 416 | buf[len++] = (char)(48U + (frac % 10U)); 417 | if (!(frac /= 10U)) { 418 | break; 419 | } 420 | } 421 | // add extra 0s 422 | while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { 423 | buf[len++] = '0'; 424 | } 425 | if (len < PRINTF_FTOA_BUFFER_SIZE) { 426 | // add decimal 427 | buf[len++] = '.'; 428 | } 429 | } 430 | 431 | // do whole part, number is reversed 432 | while (len < PRINTF_FTOA_BUFFER_SIZE) { 433 | buf[len++] = (char)(48 + (whole % 10)); 434 | if (!(whole /= 10)) { 435 | break; 436 | } 437 | } 438 | 439 | // pad leading zeros 440 | if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { 441 | if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { 442 | width--; 443 | } 444 | while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { 445 | buf[len++] = '0'; 446 | } 447 | } 448 | 449 | if (len < PRINTF_FTOA_BUFFER_SIZE) { 450 | if (negative) { 451 | buf[len++] = '-'; 452 | } 453 | else if (flags & FLAGS_PLUS) { 454 | buf[len++] = '+'; // ignore the space if the '+' exists 455 | } 456 | else if (flags & FLAGS_SPACE) { 457 | buf[len++] = ' '; 458 | } 459 | } 460 | 461 | return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); 462 | } 463 | 464 | 465 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 466 | // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse 467 | static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) 468 | { 469 | // check for NaN and special values 470 | if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { 471 | return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); 472 | } 473 | 474 | // determine the sign 475 | const bool negative = value < 0; 476 | if (negative) { 477 | value = -value; 478 | } 479 | 480 | // default precision 481 | if (!(flags & FLAGS_PRECISION)) { 482 | prec = PRINTF_DEFAULT_FLOAT_PRECISION; 483 | } 484 | 485 | // determine the decimal exponent 486 | // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) 487 | union { 488 | uint64_t U; 489 | double F; 490 | } conv; 491 | 492 | conv.F = value; 493 | int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 494 | conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) 495 | // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 496 | int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); 497 | // now we want to compute 10^expval but we want to be sure it won't overflow 498 | exp2 = (int)(expval * 3.321928094887362 + 0.5); 499 | const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; 500 | const double z2 = z * z; 501 | conv.U = (uint64_t)(exp2 + 1023) << 52U; 502 | // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex 503 | conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); 504 | // correct for rounding errors 505 | if (value < conv.F) { 506 | expval--; 507 | conv.F /= 10; 508 | } 509 | 510 | // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters 511 | unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; 512 | 513 | // in "%g" mode, "prec" is the number of *significant figures* not decimals 514 | if (flags & FLAGS_ADAPT_EXP) { 515 | // do we want to fall-back to "%f" mode? 516 | if ((value >= 1e-4) && (value < 1e6)) { 517 | if ((int)prec > expval) { 518 | prec = (unsigned)((int)prec - expval - 1); 519 | } 520 | else { 521 | prec = 0; 522 | } 523 | flags |= FLAGS_PRECISION; // make sure _ftoa respects precision 524 | // no characters in exponent 525 | minwidth = 0U; 526 | expval = 0; 527 | } 528 | else { 529 | // we use one sigfig for the whole part 530 | if ((prec > 0) && (flags & FLAGS_PRECISION)) { 531 | --prec; 532 | } 533 | } 534 | } 535 | 536 | // will everything fit? 537 | unsigned int fwidth = width; 538 | if (width > minwidth) { 539 | // we didn't fall-back so subtract the characters required for the exponent 540 | fwidth -= minwidth; 541 | } else { 542 | // not enough characters, so go back to default sizing 543 | fwidth = 0U; 544 | } 545 | if ((flags & FLAGS_LEFT) && minwidth) { 546 | // if we're padding on the right, DON'T pad the floating part 547 | fwidth = 0U; 548 | } 549 | 550 | // rescale the float value 551 | if (expval) { 552 | value /= conv.F; 553 | } 554 | 555 | // output the floating part 556 | const size_t start_idx = idx; 557 | idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); 558 | 559 | // output the exponent part 560 | if (minwidth) { 561 | // output the exponential symbol 562 | out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); 563 | // output the exponent value 564 | idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); 565 | // might need to right-pad spaces 566 | if (flags & FLAGS_LEFT) { 567 | while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); 568 | } 569 | } 570 | return idx; 571 | } 572 | #endif // PRINTF_SUPPORT_EXPONENTIAL 573 | #endif // PRINTF_SUPPORT_FLOAT 574 | 575 | 576 | // internal vsnprintf 577 | static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) 578 | { 579 | unsigned int flags, width, precision, n; 580 | size_t idx = 0U; 581 | 582 | if (!buffer) { 583 | // use null output function 584 | out = _out_null; 585 | } 586 | 587 | while (*format) 588 | { 589 | // format specifier? %[flags][width][.precision][length] 590 | if (*format != '%') { 591 | // no 592 | out(*format, buffer, idx++, maxlen); 593 | format++; 594 | continue; 595 | } 596 | else { 597 | // yes, evaluate it 598 | format++; 599 | } 600 | 601 | // evaluate flags 602 | flags = 0U; 603 | do { 604 | switch (*format) { 605 | case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; 606 | case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; 607 | case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; 608 | case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; 609 | case '#': flags |= FLAGS_HASH; format++; n = 1U; break; 610 | default : n = 0U; break; 611 | } 612 | } while (n); 613 | 614 | // evaluate width field 615 | width = 0U; 616 | if (_is_digit(*format)) { 617 | width = _atoi(&format); 618 | } 619 | else if (*format == '*') { 620 | const int w = va_arg(va, int); 621 | if (w < 0) { 622 | flags |= FLAGS_LEFT; // reverse padding 623 | width = (unsigned int)-w; 624 | } 625 | else { 626 | width = (unsigned int)w; 627 | } 628 | format++; 629 | } 630 | 631 | // evaluate precision field 632 | precision = 0U; 633 | if (*format == '.') { 634 | flags |= FLAGS_PRECISION; 635 | format++; 636 | if (_is_digit(*format)) { 637 | precision = _atoi(&format); 638 | } 639 | else if (*format == '*') { 640 | const int prec = (int)va_arg(va, int); 641 | precision = prec > 0 ? (unsigned int)prec : 0U; 642 | format++; 643 | } 644 | } 645 | 646 | // evaluate length field 647 | switch (*format) { 648 | case 'l' : 649 | flags |= FLAGS_LONG; 650 | format++; 651 | if (*format == 'l') { 652 | flags |= FLAGS_LONG_LONG; 653 | format++; 654 | } 655 | break; 656 | case 'h' : 657 | flags |= FLAGS_SHORT; 658 | format++; 659 | if (*format == 'h') { 660 | flags |= FLAGS_CHAR; 661 | format++; 662 | } 663 | break; 664 | #if defined(PRINTF_SUPPORT_PTRDIFF_T) 665 | case 't' : 666 | flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 667 | format++; 668 | break; 669 | #endif 670 | case 'j' : 671 | flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 672 | format++; 673 | break; 674 | case 'z' : 675 | flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); 676 | format++; 677 | break; 678 | default : 679 | break; 680 | } 681 | 682 | // evaluate specifier 683 | switch (*format) { 684 | case 'd' : 685 | case 'i' : 686 | case 'u' : 687 | case 'x' : 688 | case 'X' : 689 | case 'o' : 690 | case 'b' : { 691 | // set the base 692 | unsigned int base; 693 | if (*format == 'x' || *format == 'X') { 694 | base = 16U; 695 | } 696 | else if (*format == 'o') { 697 | base = 8U; 698 | } 699 | else if (*format == 'b') { 700 | base = 2U; 701 | } 702 | else { 703 | base = 10U; 704 | flags &= ~FLAGS_HASH; // no hash for dec format 705 | } 706 | // uppercase 707 | if (*format == 'X') { 708 | flags |= FLAGS_UPPERCASE; 709 | } 710 | 711 | // no plus or space flag for u, x, X, o, b 712 | if ((*format != 'i') && (*format != 'd')) { 713 | flags &= ~(FLAGS_PLUS | FLAGS_SPACE); 714 | } 715 | 716 | // ignore '0' flag when precision is given 717 | if (flags & FLAGS_PRECISION) { 718 | flags &= ~FLAGS_ZEROPAD; 719 | } 720 | 721 | // convert the integer 722 | if ((*format == 'i') || (*format == 'd')) { 723 | // signed 724 | if (flags & FLAGS_LONG_LONG) { 725 | #if defined(PRINTF_SUPPORT_LONG_LONG) 726 | const long long value = va_arg(va, long long); 727 | idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); 728 | #endif 729 | } 730 | else if (flags & FLAGS_LONG) { 731 | const long value = va_arg(va, long); 732 | idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); 733 | } 734 | else { 735 | const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); 736 | idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); 737 | } 738 | } 739 | else { 740 | // unsigned 741 | if (flags & FLAGS_LONG_LONG) { 742 | #if defined(PRINTF_SUPPORT_LONG_LONG) 743 | idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); 744 | #endif 745 | } 746 | else if (flags & FLAGS_LONG) { 747 | idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); 748 | } 749 | else { 750 | const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); 751 | idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); 752 | } 753 | } 754 | format++; 755 | break; 756 | } 757 | #if defined(PRINTF_SUPPORT_FLOAT) 758 | case 'f' : 759 | case 'F' : 760 | if (*format == 'F') flags |= FLAGS_UPPERCASE; 761 | idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); 762 | format++; 763 | break; 764 | #if defined(PRINTF_SUPPORT_EXPONENTIAL) 765 | case 'e': 766 | case 'E': 767 | case 'g': 768 | case 'G': 769 | if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; 770 | if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; 771 | idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); 772 | format++; 773 | break; 774 | #endif // PRINTF_SUPPORT_EXPONENTIAL 775 | #endif // PRINTF_SUPPORT_FLOAT 776 | case 'c' : { 777 | unsigned int l = 1U; 778 | // pre padding 779 | if (!(flags & FLAGS_LEFT)) { 780 | while (l++ < width) { 781 | out(' ', buffer, idx++, maxlen); 782 | } 783 | } 784 | // char output 785 | out((char)va_arg(va, int), buffer, idx++, maxlen); 786 | // post padding 787 | if (flags & FLAGS_LEFT) { 788 | while (l++ < width) { 789 | out(' ', buffer, idx++, maxlen); 790 | } 791 | } 792 | format++; 793 | break; 794 | } 795 | 796 | case 's' : { 797 | const char* p = va_arg(va, char*); 798 | unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); 799 | // pre padding 800 | if (flags & FLAGS_PRECISION) { 801 | l = (l < precision ? l : precision); 802 | } 803 | if (!(flags & FLAGS_LEFT)) { 804 | while (l++ < width) { 805 | out(' ', buffer, idx++, maxlen); 806 | } 807 | } 808 | // string output 809 | while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { 810 | out(*(p++), buffer, idx++, maxlen); 811 | } 812 | // post padding 813 | if (flags & FLAGS_LEFT) { 814 | while (l++ < width) { 815 | out(' ', buffer, idx++, maxlen); 816 | } 817 | } 818 | format++; 819 | break; 820 | } 821 | 822 | case 'p' : { 823 | width = sizeof(void*) * 2U; 824 | flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; 825 | #if defined(PRINTF_SUPPORT_LONG_LONG) 826 | const bool is_ll = sizeof(uintptr_t) == sizeof(long long); 827 | if (is_ll) { 828 | idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); 829 | } 830 | else { 831 | #endif 832 | idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(va_arg(va, void*)), false, 16U, precision, width, flags); 833 | #if defined(PRINTF_SUPPORT_LONG_LONG) 834 | } 835 | #endif 836 | format++; 837 | break; 838 | } 839 | 840 | case '%' : 841 | out('%', buffer, idx++, maxlen); 842 | format++; 843 | break; 844 | 845 | default : 846 | out(*format, buffer, idx++, maxlen); 847 | format++; 848 | break; 849 | } 850 | } 851 | 852 | // termination 853 | out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); 854 | 855 | // return written chars without terminating \0 856 | return (int)idx; 857 | } 858 | 859 | 860 | /////////////////////////////////////////////////////////////////////////////// 861 | 862 | int printf_(const char* format, ...) 863 | { 864 | va_list va; 865 | va_start(va, format); 866 | char buffer[1]; 867 | const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); 868 | va_end(va); 869 | return ret; 870 | } 871 | 872 | 873 | int sprintf_(char* buffer, const char* format, ...) 874 | { 875 | va_list va; 876 | va_start(va, format); 877 | const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); 878 | va_end(va); 879 | return ret; 880 | } 881 | 882 | 883 | int snprintf_(char* buffer, size_t count, const char* format, ...) 884 | { 885 | va_list va; 886 | va_start(va, format); 887 | const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); 888 | va_end(va); 889 | return ret; 890 | } 891 | 892 | 893 | int vprintf_(const char* format, va_list va) 894 | { 895 | char buffer[1]; 896 | return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); 897 | } 898 | 899 | 900 | int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) 901 | { 902 | return _vsnprintf(_out_buffer, buffer, count, format, va); 903 | } 904 | 905 | 906 | int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) 907 | { 908 | va_list va; 909 | va_start(va, format); 910 | const out_fct_wrap_type out_fct_wrap = { out, arg }; 911 | const int ret = _vsnprintf(_out_fct, (char*)&out_fct_wrap, (size_t)-1, format, va); 912 | va_end(va); 913 | return ret; 914 | } --------------------------------------------------------------------------------