├── 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 | }
--------------------------------------------------------------------------------