summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig5
-rw-r--r--lib/Makefile8
-rw-r--r--lib/cmdlinepart.c7
-rw-r--r--lib/hexdump.c212
-rw-r--r--lib/kasan/Kconfig16
-rw-r--r--lib/kasan/Makefile14
-rw-r--r--lib/kasan/common.c108
-rw-r--r--lib/kasan/generic.c315
-rw-r--r--lib/kasan/generic_report.c150
-rw-r--r--lib/kasan/kasan.h164
-rw-r--r--lib/kasan/report.c199
-rw-r--r--lib/kasan/test_kasan.c478
-rw-r--r--lib/libfile.c19
-rw-r--r--lib/list_sort.c149
-rw-r--r--lib/logo/Makefile21
-rw-r--r--lib/lzo/Kconfig4
-rw-r--r--lib/lzo/Makefile3
-rw-r--r--lib/lzo/lzo1x_compress.c279
-rw-r--r--lib/ratp.c4
-rw-r--r--lib/readkey.c2
-rw-r--r--lib/readline.c6
-rw-r--r--lib/string.c127
-rw-r--r--lib/ubsan.c88
-rw-r--r--lib/vsprintf.c64
24 files changed, 1869 insertions, 573 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index b4a8079700..887f50ff00 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -60,6 +60,9 @@ config REED_SOLOMON
config BASE64
bool "include base64 encode/decode support"
+config CONSTRUCTORS
+ bool
+
config GENERIC_FIND_NEXT_BIT
def_bool n
@@ -108,7 +111,7 @@ config LIBFDT
config RATP
select CRC_ITU_T
- select COMPILE_MEMORY
+ select DEV_MEM
select COMMAND_SUPPORT
select POLLER
depends on CONSOLE_FULL
diff --git a/lib/Makefile b/lib/Makefile
index 56040a0d5f..ba6af6f2ab 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,12 +1,13 @@
obj-y += bcd.o
obj-$(CONFIG_BOOTSTRAP) += bootstrap/
-obj-y += ctype.o
+obj-pbl-y += ctype.o
obj-y += rbtree.o
obj-y += display_options.o
obj-y += string.o
obj-y += strtox.o
obj-y += kstrtox.o
obj-y += vsprintf.o
+obj-$(CONFIG_KASAN) += kasan/
pbl-$(CONFIG_PBL_CONSOLE) += vsprintf.o
obj-y += div64.o
pbl-y += div64.o
@@ -86,8 +87,7 @@ UBSAN_SANITIZE_ubsan.o := n
libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
fdt_empty_tree.o
$(foreach file, $(libfdt_files), \
- $(eval CFLAGS_$(file) = -I $(srctree)/scripts/dtc/libfdt))
-$(foreach file, $(libfdt_files), \
- $(eval CFLAGS_pbl-$(file) = -I $(srctree)/scripts/dtc/libfdt))
+ $(eval CFLAGS_$(file) = -I $(srctree)/scripts/dtc/libfdt) \
+ $(eval CFLAGS_$(file:%.o=%.pbl.o) = -I $(srctree)/scripts/dtc/libfdt))
obj-pbl-$(CONFIG_LIBFDT) += $(libfdt_files)
diff --git a/lib/cmdlinepart.c b/lib/cmdlinepart.c
index 5a16462874..5e95760bae 100644
--- a/lib/cmdlinepart.c
+++ b/lib/cmdlinepart.c
@@ -30,7 +30,6 @@ int cmdlinepart_do_parse_one(const char *devname, const char *partstr,
char *end;
char buf[PATH_MAX] = {};
unsigned long flags = 0;
- int ret = 0;
struct cdev *cdev;
memset(buf, 0, PATH_MAX);
@@ -85,11 +84,11 @@ int cmdlinepart_do_parse_one(const char *devname, const char *partstr,
cdev = devfs_add_partition(devname, *offset, size, flags, buf);
if (IS_ERR(cdev)) {
- ret = PTR_ERR(cdev);
- printf("cannot create %s: %s\n", buf, strerror(-ret));
+ printf("cannot create %s: %pe\n", buf, cdev);
+ return PTR_ERR(cdev);
}
- return ret;
+ return 0;
}
int cmdlinepart_do_parse(const char *devname, const char *parts, loff_t devsize,
diff --git a/lib/hexdump.c b/lib/hexdump.c
index 3b1d5e6736..93f345e881 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -1,15 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* lib/hexdump.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
*/
#include <common.h>
-#include <linux/kernel.h>
+#include <linux/types.h>
#include <linux/ctype.h>
+#include <linux/log2.h>
+#include <printk.h>
+#include <asm/unaligned.h>
const char hex_asc[] = "0123456789abcdef";
EXPORT_SYMBOL(hex_asc);
@@ -40,7 +39,7 @@ EXPORT_SYMBOL(hex_to_bin);
* @src: ascii hexadecimal string
* @count: result length
*
- * Return 0 on success, -1 in case of bad input.
+ * Return 0 on success, -EINVAL in case of bad input.
*/
int hex2bin(u8 *dst, const char *src, size_t count)
{
@@ -49,7 +48,7 @@ int hex2bin(u8 *dst, const char *src, size_t count)
int lo = hex_to_bin(*src++);
if ((hi < 0) || (lo < 0))
- return -1;
+ return -EINVAL;
*dst++ = (hi << 4) | lo;
}
@@ -72,3 +71,200 @@ char *bin2hex(char *dst, const void *src, size_t count)
return dst;
}
EXPORT_SYMBOL(bin2hex);
+
+/**
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ * @ascii: include ASCII after the hex output
+ *
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ *
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
+ * to a hex + ASCII dump at the supplied memory location.
+ * The converted output is always NUL-terminated.
+ *
+ * E.g.:
+ * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
+ * linebuf, sizeof(linebuf), true);
+ *
+ * example output buffer:
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ *
+ * Return:
+ * The amount of bytes placed in the buffer without terminating NUL. If the
+ * output was truncated, then the return value is the number of bytes
+ * (excluding the terminating NUL) which would have been written to the final
+ * string if enough space had been available.
+ */
+int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
+ char *linebuf, size_t linebuflen, bool ascii)
+{
+ const u8 *ptr = buf;
+ int ngroups;
+ u8 ch;
+ int j, lx = 0;
+ int ascii_column;
+ int ret;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ if (len > rowsize) /* limit to one line at a time */
+ len = rowsize;
+ if (!is_power_of_2(groupsize) || groupsize > 8)
+ groupsize = 1;
+ if ((len % groupsize) != 0) /* no mixed size output */
+ groupsize = 1;
+
+ ngroups = len / groupsize;
+ ascii_column = rowsize * 2 + rowsize / groupsize + 1;
+
+ if (!linebuflen)
+ goto overflow1;
+
+ if (!len)
+ goto nil;
+
+ if (groupsize == 8) {
+ const u64 *ptr8 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%16.16llx", j ? " " : "",
+ get_unaligned(ptr8 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 4) {
+ const u32 *ptr4 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%8.8x", j ? " " : "",
+ get_unaligned(ptr4 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 2) {
+ const u16 *ptr2 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%4.4x", j ? " " : "",
+ get_unaligned(ptr2 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else {
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = hex_asc_hi(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = hex_asc_lo(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ if (j)
+ lx--;
+ }
+ if (!ascii)
+ goto nil;
+
+ while (lx < ascii_column) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
+ }
+nil:
+ linebuf[lx] = '\0';
+ return lx;
+overflow2:
+ linebuf[lx++] = '\0';
+overflow1:
+ return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
+}
+EXPORT_SYMBOL(hex_dump_to_buffer);
+
+/**
+ * print_hex_dump - print a text hex dump to syslog for a binary blob of data
+ * @level: kernel log level (e.g. KERN_DEBUG)
+ * @prefix_str: string to prefix each line with;
+ * caller supplies trailing spaces for alignment if desired
+ * @prefix_type: controls whether prefix of an offset, address, or none
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @ascii: include ASCII after the hex output
+ *
+ * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
+ * to the kernel log at the specified kernel log level, with an optional
+ * leading prefix.
+ *
+ * print_hex_dump() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ * print_hex_dump() iterates over the entire input @buf, breaking it into
+ * "line size" chunks to format and print.
+ *
+ * E.g.:
+ * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
+ * 16, 1, frame->data, frame->len, true);
+ *
+ * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
+ * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
+ * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.
+ */
+void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
+ int rowsize, int groupsize,
+ const void *buf, size_t len, bool ascii)
+{
+ const u8 *ptr = buf;
+ int i, linelen, remaining = len;
+ unsigned char linebuf[32 * 3 + 2 + 32 + 1];
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ for (i = 0; i < len; i += rowsize) {
+ linelen = min(remaining, rowsize);
+ remaining -= rowsize;
+
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
+ linebuf, sizeof(linebuf), ascii);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ printk("%s%s%p: %s\n",
+ level, prefix_str, ptr + i, linebuf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
+ break;
+ default:
+ printk("%s%s%s\n", level, prefix_str, linebuf);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL(print_hex_dump);
diff --git a/lib/kasan/Kconfig b/lib/kasan/Kconfig
new file mode 100644
index 0000000000..7a18cf95be
--- /dev/null
+++ b/lib/kasan/Kconfig
@@ -0,0 +1,16 @@
+source "scripts/Kconfig.include"
+
+config HAVE_ARCH_KASAN
+ bool
+
+config CC_HAS_KASAN_GENERIC
+ def_bool $(cc-option, -fsanitize=kernel-address)
+
+config KASAN
+ bool "KASAN: runtime memory debugger"
+ depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC)
+ depends on MALLOC_TLSF
+ select CONSTRUCTORS
+ help
+ Enables KASAN (KernelAddressSANitizer) - runtime memory debugger,
+ designed to find out-of-bounds accesses and use-after-free bugs.
diff --git a/lib/kasan/Makefile b/lib/kasan/Makefile
new file mode 100644
index 0000000000..31e9d890d5
--- /dev/null
+++ b/lib/kasan/Makefile
@@ -0,0 +1,14 @@
+
+obj-y += generic_report.o generic.o report.o common.o test_kasan.o
+KASAN_SANITIZE_generic_report.o := n
+KASAN_SANITIZE_generic.o := n
+KASAN_SANITIZE_report.o := n
+KASAN_SANITIZE_common.o := n
+
+CC_FLAGS_KASAN_RUNTIME := $(call cc-option, -fno-conserve-stack)
+CC_FLAGS_KASAN_RUNTIME += -fno-stack-protector
+
+CFLAGS_generic_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_common.o := $(CC_FLAGS_KASAN_RUNTIME)
diff --git a/lib/kasan/common.c b/lib/kasan/common.c
new file mode 100644
index 0000000000..1ebf66a7b8
--- /dev/null
+++ b/lib/kasan/common.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains common generic and tag-based KASAN code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <linux/kasan.h>
+#include <linux/kernel.h>
+
+#include "kasan.h"
+
+int kasan_depth;
+
+void kasan_enable_current(void)
+{
+ kasan_depth++;
+}
+
+void kasan_disable_current(void)
+{
+ kasan_depth--;
+}
+
+#undef memset
+void *memset(void *addr, int c, size_t len)
+{
+ if (!check_memory_region((unsigned long)addr, len, true, _RET_IP_))
+ return NULL;
+
+ return __memset(addr, c, len);
+}
+
+#ifdef __HAVE_ARCH_MEMMOVE
+#undef memmove
+void *memmove(void *dest, const void *src, size_t len)
+{
+ if (!check_memory_region((unsigned long)src, len, false, _RET_IP_) ||
+ !check_memory_region((unsigned long)dest, len, true, _RET_IP_))
+ return NULL;
+
+ return __memmove(dest, src, len);
+}
+#endif
+
+#undef memcpy
+void *memcpy(void *dest, const void *src, size_t len)
+{
+ if (!check_memory_region((unsigned long)src, len, false, _RET_IP_) ||
+ !check_memory_region((unsigned long)dest, len, true, _RET_IP_))
+ return NULL;
+
+ return __memcpy(dest, src, len);
+}
+
+/*
+ * Poisons the shadow memory for 'size' bytes starting from 'addr'.
+ * Memory addresses should be aligned to KASAN_SHADOW_SCALE_SIZE.
+ */
+void kasan_poison_shadow(const void *address, size_t size, u8 value)
+{
+ void *shadow_start, *shadow_end;
+
+ /*
+ * Perform shadow offset calculation based on untagged address, as
+ * some of the callers (e.g. kasan_poison_object_data) pass tagged
+ * addresses to this function.
+ */
+ address = reset_tag(address);
+
+ shadow_start = kasan_mem_to_shadow(address);
+ shadow_end = kasan_mem_to_shadow(address + size);
+
+ __memset(shadow_start, value, shadow_end - shadow_start);
+}
+
+void kasan_unpoison_shadow(const void *address, size_t size)
+{
+ u8 tag = get_tag(address);
+
+ /*
+ * Perform shadow offset calculation based on untagged address, as
+ * some of the callers (e.g. kasan_unpoison_object_data) pass tagged
+ * addresses to this function.
+ */
+ address = reset_tag(address);
+
+ kasan_poison_shadow(address, size, tag);
+
+ if (size & KASAN_SHADOW_MASK) {
+ u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
+
+ if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+ *shadow = tag;
+ else
+ *shadow = size & KASAN_SHADOW_MASK;
+ }
+}
diff --git a/lib/kasan/generic.c b/lib/kasan/generic.c
new file mode 100644
index 0000000000..b33a6c1a6c
--- /dev/null
+++ b/lib/kasan/generic.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains core generic KASAN code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+
+#include "kasan.h"
+
+unsigned long kasan_shadow_start;
+unsigned long kasan_shadow_base;
+
+/*
+ * All functions below always inlined so compiler could
+ * perform better optimizations in each of __asan_loadX/__assn_storeX
+ * depending on memory access size X.
+ */
+
+static __always_inline bool memory_is_poisoned_1(unsigned long addr)
+{
+ s8 shadow_value = *(s8 *)kasan_mem_to_shadow((void *)addr);
+
+ if (unlikely(shadow_value)) {
+ s8 last_accessible_byte = addr & KASAN_SHADOW_MASK;
+ return unlikely(last_accessible_byte >= shadow_value);
+ }
+
+ return false;
+}
+
+static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr,
+ unsigned long size)
+{
+ u8 *shadow_addr = (u8 *)kasan_mem_to_shadow((void *)addr);
+
+ /*
+ * Access crosses 8(shadow size)-byte boundary. Such access maps
+ * into 2 shadow bytes, so we need to check them both.
+ */
+ if (unlikely(((addr + size - 1) & KASAN_SHADOW_MASK) < size - 1))
+ return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
+
+ return memory_is_poisoned_1(addr + size - 1);
+}
+
+static __always_inline bool memory_is_poisoned_16(unsigned long addr)
+{
+ u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
+
+ /* Unaligned 16-bytes access maps into 3 shadow bytes. */
+ if (unlikely(!IS_ALIGNED(addr, KASAN_SHADOW_SCALE_SIZE)))
+ return *shadow_addr || memory_is_poisoned_1(addr + 15);
+
+ return *shadow_addr;
+}
+
+static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
+ size_t size)
+{
+ while (size) {
+ if (unlikely(*start))
+ return (unsigned long)start;
+ start++;
+ size--;
+ }
+
+ return 0;
+}
+
+static __always_inline unsigned long memory_is_nonzero(const void *start,
+ const void *end)
+{
+ unsigned int words;
+ unsigned long ret;
+ unsigned int prefix = (unsigned long)start % 8;
+
+ if (end - start <= 16)
+ return bytes_is_nonzero(start, end - start);
+
+ if (prefix) {
+ prefix = 8 - prefix;
+ ret = bytes_is_nonzero(start, prefix);
+ if (unlikely(ret))
+ return ret;
+ start += prefix;
+ }
+
+ words = (end - start) / 8;
+ while (words) {
+ if (unlikely(*(u64 *)start))
+ return bytes_is_nonzero(start, 8);
+ start += 8;
+ words--;
+ }
+
+ return bytes_is_nonzero(start, (end - start) % 8);
+}
+
+static __always_inline bool memory_is_poisoned_n(unsigned long addr,
+ size_t size)
+{
+ unsigned long ret;
+
+ ret = memory_is_nonzero(kasan_mem_to_shadow((void *)addr),
+ kasan_mem_to_shadow((void *)addr + size - 1) + 1);
+
+ if (unlikely(ret)) {
+ unsigned long last_byte = addr + size - 1;
+ s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte);
+
+ if (unlikely(ret != (unsigned long)last_shadow ||
+ ((long)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow)))
+ return true;
+ }
+ return false;
+}
+
+static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
+{
+ if (__builtin_constant_p(size)) {
+ switch (size) {
+ case 1:
+ return memory_is_poisoned_1(addr);
+ case 2:
+ case 4:
+ case 8:
+ return memory_is_poisoned_2_4_8(addr, size);
+ case 16:
+ return memory_is_poisoned_16(addr);
+ default:
+ BUILD_BUG();
+ }
+ }
+
+ return memory_is_poisoned_n(addr, size);
+}
+
+static bool kasan_initialized;
+
+static __always_inline bool check_memory_region_inline(unsigned long addr,
+ size_t size, bool write,
+ unsigned long ret_ip)
+{
+ if (!kasan_initialized)
+ return true;
+
+ if (addr < kasan_shadow_start)
+ return true;
+
+ if (unlikely(size == 0))
+ return true;
+
+ if (unlikely(addr + size < addr))
+ return !kasan_report(addr, size, write, ret_ip);
+
+ if (addr < kasan_shadow_base)
+ return true;
+
+ if (likely(!memory_is_poisoned(addr, size)))
+ return true;
+
+ return !kasan_report(addr, size, write, ret_ip);
+}
+
+void kasan_init(unsigned long membase, unsigned long memsize,
+ unsigned long shadow_base)
+{
+ kasan_shadow_start = membase;
+ kasan_shadow_base = shadow_base;
+
+ kasan_unpoison_shadow((void *)membase, memsize);
+ kasan_initialized = true;
+}
+
+bool __no_sanitize_address check_memory_region(unsigned long addr,
+ size_t size, bool write,
+ unsigned long ret_ip)
+{
+ return check_memory_region_inline(addr, size, write, ret_ip);
+}
+
+static void register_global(struct kasan_global *global)
+{
+ size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE);
+
+ kasan_unpoison_shadow(global->beg, global->size);
+
+ kasan_poison_shadow(global->beg + aligned_size,
+ global->size_with_redzone - aligned_size,
+ KASAN_GLOBAL_REDZONE);
+}
+
+void __asan_register_globals(struct kasan_global *globals, size_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ register_global(&globals[i]);
+}
+EXPORT_SYMBOL(__asan_register_globals);
+
+void __asan_unregister_globals(struct kasan_global *globals, size_t size)
+{
+}
+EXPORT_SYMBOL(__asan_unregister_globals);
+
+#define DEFINE_ASAN_LOAD_STORE(size) \
+ void __no_sanitize_address __asan_load##size(unsigned long addr) \
+ { \
+ check_memory_region_inline(addr, size, false, _RET_IP_);\
+ } \
+ EXPORT_SYMBOL(__asan_load##size); \
+ __alias(__asan_load##size) \
+ void __no_sanitize_address __asan_load##size##_noabort(unsigned long); \
+ EXPORT_SYMBOL(__asan_load##size##_noabort); \
+ void __asan_store##size(unsigned long addr) \
+ { \
+ check_memory_region_inline(addr, size, true, _RET_IP_); \
+ } \
+ EXPORT_SYMBOL(__asan_store##size); \
+ __alias(__asan_store##size) \
+ void __asan_store##size##_noabort(unsigned long); \
+ EXPORT_SYMBOL(__asan_store##size##_noabort)
+
+DEFINE_ASAN_LOAD_STORE(1);
+DEFINE_ASAN_LOAD_STORE(2);
+DEFINE_ASAN_LOAD_STORE(4);
+DEFINE_ASAN_LOAD_STORE(8);
+DEFINE_ASAN_LOAD_STORE(16);
+
+void __asan_loadN(unsigned long addr, size_t size)
+{
+ check_memory_region(addr, size, false, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_loadN);
+
+__alias(__asan_loadN)
+void __asan_loadN_noabort(unsigned long, size_t);
+EXPORT_SYMBOL(__asan_loadN_noabort);
+
+void __asan_storeN(unsigned long addr, size_t size)
+{
+ check_memory_region(addr, size, true, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_storeN);
+
+__alias(__asan_storeN)
+void __asan_storeN_noabort(unsigned long, size_t);
+EXPORT_SYMBOL(__asan_storeN_noabort);
+
+/* to shut up compiler complaints */
+void __asan_handle_no_return(void) {}
+EXPORT_SYMBOL(__asan_handle_no_return);
+
+/* Emitted by compiler to poison alloca()ed objects. */
+void __asan_alloca_poison(unsigned long addr, size_t size)
+{
+ size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+ size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
+ rounded_up_size;
+ size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+
+ const void *left_redzone = (const void *)(addr -
+ KASAN_ALLOCA_REDZONE_SIZE);
+ const void *right_redzone = (const void *)(addr + rounded_up_size);
+
+ WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
+
+ kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+ size - rounded_down_size);
+ kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_LEFT);
+ kasan_poison_shadow(right_redzone,
+ padding_size + KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_RIGHT);
+}
+EXPORT_SYMBOL(__asan_alloca_poison);
+
+/* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
+{
+ if (unlikely(!stack_top || stack_top > stack_bottom))
+ return;
+
+ kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+}
+EXPORT_SYMBOL(__asan_allocas_unpoison);
+
+/* Emitted by the compiler to [un]poison local variables. */
+#define DEFINE_ASAN_SET_SHADOW(byte) \
+ void __asan_set_shadow_##byte(const void *addr, size_t size) \
+ { \
+ __memset((void *)addr, 0x##byte, size); \
+ } \
+ EXPORT_SYMBOL(__asan_set_shadow_##byte)
+
+DEFINE_ASAN_SET_SHADOW(00);
+DEFINE_ASAN_SET_SHADOW(f1);
+DEFINE_ASAN_SET_SHADOW(f2);
+DEFINE_ASAN_SET_SHADOW(f3);
+DEFINE_ASAN_SET_SHADOW(f5);
+DEFINE_ASAN_SET_SHADOW(f8);
diff --git a/lib/kasan/generic_report.c b/lib/kasan/generic_report.c
new file mode 100644
index 0000000000..1cc5829e8d
--- /dev/null
+++ b/lib/kasan/generic_report.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains generic KASAN specific error reporting code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+
+#include <asm/sections.h>
+
+#include "kasan.h"
+
+void *find_first_bad_addr(void *addr, size_t size)
+{
+ void *p = addr;
+
+ while (p < addr + size && !(*(u8 *)kasan_mem_to_shadow(p)))
+ p += KASAN_SHADOW_SCALE_SIZE;
+ return p;
+}
+
+static const char *get_shadow_bug_type(struct kasan_access_info *info)
+{
+ const char *bug_type = "unknown-crash";
+ u8 *shadow_addr;
+
+ shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr);
+
+ /*
+ * If shadow byte value is in [0, KASAN_SHADOW_SCALE_SIZE) we can look
+ * at the next shadow byte to determine the type of the bad access.
+ */
+ if (*shadow_addr > 0 && *shadow_addr <= KASAN_SHADOW_SCALE_SIZE - 1)
+ shadow_addr++;
+
+ switch (*shadow_addr) {
+ case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
+ /*
+ * In theory it's still possible to see these shadow values
+ * due to a data race in the kernel code.
+ */
+ bug_type = "out-of-bounds";
+ break;
+ case KASAN_PAGE_REDZONE:
+ case KASAN_KMALLOC_REDZONE:
+ bug_type = "slab-out-of-bounds";
+ break;
+ case KASAN_GLOBAL_REDZONE:
+ bug_type = "global-out-of-bounds";
+ break;
+ case KASAN_STACK_LEFT:
+ case KASAN_STACK_MID:
+ case KASAN_STACK_RIGHT:
+ case KASAN_STACK_PARTIAL:
+ bug_type = "stack-out-of-bounds";
+ break;
+ case KASAN_FREE_PAGE:
+ case KASAN_KMALLOC_FREE:
+ case KASAN_KMALLOC_FREETRACK:
+ bug_type = "use-after-free";
+ break;
+ case KASAN_ALLOCA_LEFT:
+ case KASAN_ALLOCA_RIGHT:
+ bug_type = "alloca-out-of-bounds";
+ break;
+ case KASAN_VMALLOC_INVALID:
+ bug_type = "vmalloc-out-of-bounds";
+ break;
+ }
+
+ return bug_type;
+}
+
+static const char *get_wild_bug_type(struct kasan_access_info *info)
+{
+ const char *bug_type = "unknown-crash";
+
+ if ((unsigned long)info->access_addr < PAGE_SIZE)
+ bug_type = "null-ptr-deref";
+ else
+ bug_type = "wild-memory-access";
+
+ return bug_type;
+}
+
+const char *get_bug_type(struct kasan_access_info *info)
+{
+ /*
+ * If access_size is a negative number, then it has reason to be
+ * defined as out-of-bounds bug type.
+ *
+ * Casting negative numbers to size_t would indeed turn up as
+ * a large size_t and its value will be larger than ULONG_MAX/2,
+ * so that this can qualify as out-of-bounds.
+ */
+ if (info->access_addr + info->access_size < info->access_addr)
+ return "out-of-bounds";
+
+ if (addr_has_shadow(info->access_addr))
+ return get_shadow_bug_type(info);
+ return get_wild_bug_type(info);
+}
+
+#define DEFINE_ASAN_REPORT_LOAD(size) \
+void __asan_report_load##size##_noabort(unsigned long addr) \
+{ \
+ kasan_report(addr, size, false, _RET_IP_); \
+} \
+EXPORT_SYMBOL(__asan_report_load##size##_noabort)
+
+#define DEFINE_ASAN_REPORT_STORE(size) \
+void __asan_report_store##size##_noabort(unsigned long addr) \
+{ \
+ kasan_report(addr, size, true, _RET_IP_); \
+} \
+EXPORT_SYMBOL(__asan_report_store##size##_noabort)
+
+DEFINE_ASAN_REPORT_LOAD(1);
+DEFINE_ASAN_REPORT_LOAD(2);
+DEFINE_ASAN_REPORT_LOAD(4);
+DEFINE_ASAN_REPORT_LOAD(8);
+DEFINE_ASAN_REPORT_LOAD(16);
+DEFINE_ASAN_REPORT_STORE(1);
+DEFINE_ASAN_REPORT_STORE(2);
+DEFINE_ASAN_REPORT_STORE(4);
+DEFINE_ASAN_REPORT_STORE(8);
+DEFINE_ASAN_REPORT_STORE(16);
+
+void __asan_report_load_n_noabort(unsigned long addr, size_t size)
+{
+ kasan_report(addr, size, false, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_report_load_n_noabort);
+
+void __asan_report_store_n_noabort(unsigned long addr, size_t size)
+{
+ kasan_report(addr, size, true, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_report_store_n_noabort);
diff --git a/lib/kasan/kasan.h b/lib/kasan/kasan.h
new file mode 100644
index 0000000000..e17f49dbec
--- /dev/null
+++ b/lib/kasan/kasan.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __MM_KASAN_KASAN_H
+#define __MM_KASAN_KASAN_H
+
+#include <linux/kasan.h>
+#include <linux/linkage.h>
+
+#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
+#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)
+
+#define KASAN_ALLOCA_REDZONE_SIZE 32
+
+/*
+ * Stack frame marker (compiler ABI).
+ */
+#define KASAN_CURRENT_STACK_FRAME_MAGIC 0x41B58AB3
+
+/* Don't break randconfig/all*config builds */
+#ifndef KASAN_ABI_VERSION
+#define KASAN_ABI_VERSION 1
+#endif
+
+struct kasan_access_info {
+ const void *access_addr;
+ const void *first_bad_addr;
+ size_t access_size;
+ bool is_write;
+ unsigned long ip;
+};
+
+/* The layout of struct dictated by compiler */
+struct kasan_source_location {
+ const char *filename;
+ int line_no;
+ int column_no;
+};
+
+/* The layout of struct dictated by compiler */
+struct kasan_global {
+ const void *beg; /* Address of the beginning of the global variable. */
+ size_t size; /* Size of the global variable. */
+ size_t size_with_redzone; /* Size of the variable + size of the red zone. 32 bytes aligned */
+ const void *name;
+ const void *module_name; /* Name of the module where the global variable is declared. */
+ unsigned long has_dynamic_init; /* This needed for C++ */
+#if KASAN_ABI_VERSION >= 4
+ struct kasan_source_location *location;
+#endif
+#if KASAN_ABI_VERSION >= 5
+ char *odr_indicator;
+#endif
+};
+
+static inline const void *kasan_shadow_to_mem(const void *shadow_addr)
+{
+ unsigned long sa = (unsigned long)shadow_addr;
+
+ sa -= kasan_shadow_base;
+ sa <<= KASAN_SHADOW_SCALE_SHIFT;
+ sa += kasan_shadow_start;
+
+ return (void *)sa;
+}
+
+static inline bool addr_has_shadow(const void *addr)
+{
+ return (addr >= (void *)kasan_shadow_start);
+}
+
+/**
+ * check_memory_region - Check memory region, and report if invalid access.
+ * @addr: the accessed address
+ * @size: the accessed size
+ * @write: true if access is a write access
+ * @ret_ip: return address
+ * @return: true if access was valid, false if invalid
+ */
+bool check_memory_region(unsigned long addr, size_t size, bool write,
+ unsigned long ret_ip);
+
+void *find_first_bad_addr(void *addr, size_t size);
+const char *get_bug_type(struct kasan_access_info *info);
+
+bool kasan_report(unsigned long addr, size_t size,
+ bool is_write, unsigned long ip);
+void kasan_report_invalid_free(void *object, unsigned long ip);
+
+#ifndef arch_kasan_set_tag
+static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
+{
+ return addr;
+}
+#endif
+#ifndef arch_kasan_reset_tag
+#define arch_kasan_reset_tag(addr) ((void *)(addr))
+#endif
+#ifndef arch_kasan_get_tag
+#define arch_kasan_get_tag(addr) 0
+#endif
+
+#define set_tag(addr, tag) ((void *)arch_kasan_set_tag((addr), (tag)))
+#define reset_tag(addr) ((void *)arch_kasan_reset_tag(addr))
+#define get_tag(addr) arch_kasan_get_tag(addr)
+
+/*
+ * Exported functions for interfaces called from assembly or from generated
+ * code. Declarations here to avoid warning about missing declarations.
+ */
+asmlinkage void kasan_unpoison_task_stack_below(const void *watermark);
+void __asan_register_globals(struct kasan_global *globals, size_t size);
+void __asan_unregister_globals(struct kasan_global *globals, size_t size);
+void __asan_handle_no_return(void);
+void __asan_alloca_poison(unsigned long addr, size_t size);
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom);
+
+void __asan_load1(unsigned long addr);
+void __asan_store1(unsigned long addr);
+void __asan_load2(unsigned long addr);
+void __asan_store2(unsigned long addr);
+void __asan_load4(unsigned long addr);
+void __asan_store4(unsigned long addr);
+void __asan_load8(unsigned long addr);
+void __asan_store8(unsigned long addr);
+void __asan_load16(unsigned long addr);
+void __asan_store16(unsigned long addr);
+void __asan_loadN(unsigned long addr, size_t size);
+void __asan_storeN(unsigned long addr, size_t size);
+
+void __asan_load1_noabort(unsigned long addr);
+void __asan_store1_noabort(unsigned long addr);
+void __asan_load2_noabort(unsigned long addr);
+void __asan_store2_noabort(unsigned long addr);
+void __asan_load4_noabort(unsigned long addr);
+void __asan_store4_noabort(unsigned long addr);
+void __asan_load8_noabort(unsigned long addr);
+void __asan_store8_noabort(unsigned long addr);
+void __asan_load16_noabort(unsigned long addr);
+void __asan_store16_noabort(unsigned long addr);
+void __asan_loadN_noabort(unsigned long addr, size_t size);
+void __asan_storeN_noabort(unsigned long addr, size_t size);
+
+void __asan_report_load1_noabort(unsigned long addr);
+void __asan_report_store1_noabort(unsigned long addr);
+void __asan_report_load2_noabort(unsigned long addr);
+void __asan_report_store2_noabort(unsigned long addr);
+void __asan_report_load4_noabort(unsigned long addr);
+void __asan_report_store4_noabort(unsigned long addr);
+void __asan_report_load8_noabort(unsigned long addr);
+void __asan_report_store8_noabort(unsigned long addr);
+void __asan_report_load16_noabort(unsigned long addr);
+void __asan_report_store16_noabort(unsigned long addr);
+void __asan_report_load_n_noabort(unsigned long addr, size_t size);
+void __asan_report_store_n_noabort(unsigned long addr, size_t size);
+
+void __asan_set_shadow_00(const void *addr, size_t size);
+void __asan_set_shadow_f1(const void *addr, size_t size);
+void __asan_set_shadow_f2(const void *addr, size_t size);
+void __asan_set_shadow_f3(const void *addr, size_t size);
+void __asan_set_shadow_f5(const void *addr, size_t size);
+void __asan_set_shadow_f8(const void *addr, size_t size);
+
+extern int kasan_depth;
+
+#endif
diff --git a/lib/kasan/report.c b/lib/kasan/report.c
new file mode 100644
index 0000000000..b7b2d032ee
--- /dev/null
+++ b/lib/kasan/report.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains common generic and tag-based KASAN error reporting code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <printk.h>
+#include <asm-generic/sections.h>
+
+#include "kasan.h"
+
+/* Shadow layout customization. */
+#define SHADOW_BYTES_PER_BLOCK 1
+#define SHADOW_BLOCKS_PER_ROW 16
+#define SHADOW_BYTES_PER_ROW (SHADOW_BLOCKS_PER_ROW * SHADOW_BYTES_PER_BLOCK)
+#define SHADOW_ROWS_AROUND_ADDR 2
+
+static unsigned long kasan_flags;
+
+#define KASAN_BIT_REPORTED 0
+#define KASAN_BIT_MULTI_SHOT 1
+
+bool kasan_save_enable_multi_shot(void)
+{
+ return test_and_set_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
+}
+EXPORT_SYMBOL_GPL(kasan_save_enable_multi_shot);
+
+void kasan_restore_multi_shot(bool enabled)
+{
+ if (!enabled)
+ clear_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
+}
+EXPORT_SYMBOL_GPL(kasan_restore_multi_shot);
+
+static void print_error_description(struct kasan_access_info *info)
+{
+ pr_err("BUG: KASAN: %s in %pS\n",
+ get_bug_type(info), (void *)info->ip);
+ pr_err("%s of size %zu at addr %px\n",
+ info->is_write ? "Write" : "Read", info->access_size,
+ info->access_addr);
+}
+
+static void start_report(unsigned long *flags)
+{
+ /*
+ * Make sure we don't end up in loop.
+ */
+ kasan_disable_current();
+ pr_err("==================================================================\n");
+}
+
+static void end_report(unsigned long *flags)
+{
+ pr_err("==================================================================\n");
+ kasan_enable_current();
+}
+
+static inline bool kernel_or_module_addr(const void *addr)
+{
+ if (addr >= (void *)_stext && addr < (void *)_end)
+ return true;
+ return false;
+}
+
+static void print_address_description(void *addr, u8 tag)
+{
+ dump_stack();
+ pr_err("\n");
+
+ if (kernel_or_module_addr(addr)) {
+ pr_err("The buggy address belongs to the variable:\n");
+ pr_err(" %pS\n", addr);
+ }
+}
+
+static bool row_is_guilty(const void *row, const void *guilty)
+{
+ return (row <= guilty) && (guilty < row + SHADOW_BYTES_PER_ROW);
+}
+
+static int shadow_pointer_offset(const void *row, const void *shadow)
+{
+ /* The length of ">ff00ff00ff00ff00: " is
+ * 3 + (BITS_PER_LONG/8)*2 chars.
+ */
+ return 3 + (BITS_PER_LONG/8)*2 + (shadow - row)*2 +
+ (shadow - row) / SHADOW_BYTES_PER_BLOCK + 1;
+}
+
+static void print_shadow_for_address(const void *addr)
+{
+ int i;
+ const void *shadow = kasan_mem_to_shadow(addr);
+ const void *shadow_row;
+
+ shadow_row = (void *)round_down((unsigned long)shadow,
+ SHADOW_BYTES_PER_ROW)
+ - SHADOW_ROWS_AROUND_ADDR * SHADOW_BYTES_PER_ROW;
+
+ pr_err("Memory state around the buggy address:\n");
+
+ for (i = -SHADOW_ROWS_AROUND_ADDR; i <= SHADOW_ROWS_AROUND_ADDR; i++) {
+ const void *kaddr = kasan_shadow_to_mem(shadow_row);
+ char buffer[4 + (BITS_PER_LONG/8)*2];
+ char shadow_buf[SHADOW_BYTES_PER_ROW];
+
+ snprintf(buffer, sizeof(buffer),
+ (i == 0) ? ">%px: " : " %px: ", kaddr);
+ /*
+ * We should not pass a shadow pointer to generic
+ * function, because generic functions may try to
+ * access kasan mapping for the passed address.
+ */
+ memcpy(shadow_buf, shadow_row, SHADOW_BYTES_PER_ROW);
+ print_hex_dump(KERN_ERR, buffer,
+ DUMP_PREFIX_NONE, SHADOW_BYTES_PER_ROW, 1,
+ shadow_buf, SHADOW_BYTES_PER_ROW, 0);
+
+ if (row_is_guilty(shadow_row, shadow))
+ printf("%*c\n",
+ shadow_pointer_offset(shadow_row, shadow),
+ '^');
+
+ shadow_row += SHADOW_BYTES_PER_ROW;
+ }
+}
+
+static bool report_enabled(void)
+{
+ if (kasan_depth)
+ return false;
+ if (test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags))
+ return true;
+ return !test_and_set_bit(KASAN_BIT_REPORTED, &kasan_flags);
+}
+
+static void __kasan_report(unsigned long addr, size_t size, bool is_write,
+ unsigned long ip)
+{
+ struct kasan_access_info info;
+ void *tagged_addr;
+ void *untagged_addr;
+ unsigned long flags;
+
+ tagged_addr = (void *)addr;
+ untagged_addr = reset_tag(tagged_addr);
+
+ info.access_addr = tagged_addr;
+ if (addr_has_shadow(untagged_addr))
+ info.first_bad_addr = find_first_bad_addr(tagged_addr, size);
+ else
+ info.first_bad_addr = untagged_addr;
+ info.access_size = size;
+ info.is_write = is_write;
+ info.ip = ip;
+
+ start_report(&flags);
+
+ print_error_description(&info);
+ pr_err("\n");
+
+ if (addr_has_shadow(untagged_addr)) {
+ print_address_description(untagged_addr, get_tag(tagged_addr));
+ pr_err("\n");
+ print_shadow_for_address(info.first_bad_addr);
+ } else {
+ dump_stack();
+ }
+
+ end_report(&flags);
+}
+
+bool kasan_report(unsigned long addr, size_t size, bool is_write,
+ unsigned long ip)
+{
+ bool ret = false;
+
+ if (likely(report_enabled())) {
+ __kasan_report(addr, size, is_write, ip);
+ ret = true;
+ }
+
+ return ret;
+}
diff --git a/lib/kasan/test_kasan.c b/lib/kasan/test_kasan.c
new file mode 100644
index 0000000000..e472bb3499
--- /dev/null
+++ b/lib/kasan/test_kasan.c
@@ -0,0 +1,478 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <a.ryabinin@samsung.com>
+ */
+
+#define pr_fmt(fmt) "kasan test: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <complete.h>
+
+#include "kasan.h"
+
+/*
+ * We assign some test results to these globals to make sure the tests
+ * are not eliminated as dead code.
+ */
+
+int kasan_int_result;
+void *kasan_ptr_result;
+
+/*
+ * Note: test functions are marked noinline so that their names appear in
+ * reports.
+ */
+
+static noinline void malloc_oob_right(void)
+{
+ char *ptr;
+ size_t size = 123;
+
+ pr_info("out-of-bounds to right\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ ptr[size] = 'x';
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_left(void)
+{
+ char *ptr;
+ size_t size = 15;
+
+ pr_info("out-of-bounds to left\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ *ptr = *(ptr - 1);
+ free(ptr);
+}
+
+static noinline void malloc_oob_realloc_more(void)
+{
+ char *ptr1, *ptr2;
+ size_t size1 = 17;
+ size_t size2 = 19;
+
+ pr_info("out-of-bounds after krealloc more\n");
+ ptr1 = malloc(size1);
+ ptr2 = realloc(ptr1, size2);
+ if (!ptr1 || !ptr2) {
+ pr_err("Allocation failed\n");
+ free(ptr1);
+ free(ptr2);
+ return;
+ }
+
+ ptr2[size2] = 'x';
+
+ free(ptr2);
+}
+
+static noinline void malloc_oob_realloc_less(void)
+{
+ char *ptr1, *ptr2;
+ size_t size1 = 17;
+ size_t size2 = 15;
+
+ pr_info("out-of-bounds after krealloc less\n");
+ ptr1 = malloc(size1);
+ ptr2 = realloc(ptr1, size2);
+ if (!ptr1 || !ptr2) {
+ pr_err("Allocation failed\n");
+ free(ptr1);
+ return;
+ }
+
+ ptr2[size2] = 'x';
+
+ free(ptr2);
+}
+
+static noinline void malloc_oob_16(void)
+{
+ struct {
+ u64 words[2];
+ } *ptr1, *ptr2;
+
+ pr_info("malloc out-of-bounds for 16-bytes access\n");
+ ptr1 = malloc(sizeof(*ptr1) - 3);
+ ptr2 = malloc(sizeof(*ptr2));
+ if (!ptr1 || !ptr2) {
+ pr_err("Allocation failed\n");
+ free(ptr1);
+ free(ptr2);
+ return;
+ }
+ *ptr1 = *ptr2;
+ free(ptr1);
+ free(ptr2);
+}
+
+static noinline void malloc_oob_memset_2(void)
+{
+ char *ptr;
+ size_t size = 8;
+
+ pr_info("out-of-bounds in memset2\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 7, 0, 2);
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_memset_4(void)
+{
+ char *ptr;
+ size_t size = 8;
+
+ pr_info("out-of-bounds in memset4\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 5, 0, 4);
+
+ free(ptr);
+}
+
+
+static noinline void malloc_oob_memset_8(void)
+{
+ char *ptr;
+ size_t size = 8;
+
+ pr_info("out-of-bounds in memset8\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 1, 0, 8);
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_memset_16(void)
+{
+ char *ptr;
+ size_t size = 16;
+
+ pr_info("out-of-bounds in memset16\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 1, 0, 16);
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_in_memset(void)
+{
+ char *ptr;
+ size_t size = 666;
+
+ pr_info("out-of-bounds in memset\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr, 0, size + 5);
+
+ free(ptr);
+}
+
+static noinline void malloc_uaf(void)
+{
+ char *ptr;
+ size_t size = 10;
+
+ pr_info("use-after-free\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ free(ptr);
+ *(ptr + 8) = 'x';
+}
+
+static noinline void malloc_uaf_memset(void)
+{
+ char *ptr;
+ size_t size = 33;
+
+ pr_info("use-after-free in memset\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ free(ptr);
+ memset(ptr, 0, size);
+}
+
+static noinline void malloc_uaf2(void)
+{
+ char *ptr1, *ptr2;
+ size_t size = 43;
+
+ pr_info("use-after-free after another malloc\n");
+ ptr1 = malloc(size);
+ if (!ptr1) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ free(ptr1);
+ ptr2 = malloc(size);
+ if (!ptr2) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ ptr1[40] = 'x';
+ if (ptr1 == ptr2)
+ pr_err("Could not detect use-after-free: ptr1 == ptr2\n");
+ free(ptr2);
+}
+
+static char global_array[10];
+
+static noinline void kasan_global_oob(void)
+{
+ volatile int i = 3;
+ char *p = &global_array[ARRAY_SIZE(global_array) + i];
+
+ pr_info("out-of-bounds global variable\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_stack_oob(void)
+{
+ char stack_array[10];
+ volatile int i = 0;
+ char *p = &stack_array[ARRAY_SIZE(stack_array) + i];
+
+ pr_info("out-of-bounds on stack\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_alloca_oob_left(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array - 1;
+
+ pr_info("out-of-bounds to left on alloca\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_alloca_oob_right(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array + i;
+
+ pr_info("out-of-bounds to right on alloca\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_memchr(void)
+{
+ char *ptr;
+ size_t size = 24;
+
+ pr_info("out-of-bounds in memchr\n");
+ ptr = kzalloc(size, 0);
+ if (!ptr)
+ return;
+
+ kasan_ptr_result = memchr(ptr, '1', size + 1);
+ free(ptr);
+}
+
+static noinline void kasan_memcmp(void)
+{
+ char *ptr;
+ size_t size = 24;
+ int arr[9];
+
+ pr_info("out-of-bounds in memcmp\n");
+ ptr = kzalloc(size, 0);
+ if (!ptr)
+ return;
+
+ memset(arr, 0, sizeof(arr));
+ kasan_int_result = memcmp(ptr, arr, size + 1);
+ free(ptr);
+}
+
+static noinline void kasan_strings(void)
+{
+ char *ptr;
+ size_t size = 24;
+
+ pr_info("use-after-free in strchr\n");
+ ptr = malloc(size);
+ if (!ptr)
+ return;
+
+ free(ptr);
+
+ /*
+ * Try to cause only 1 invalid access (less spam in dmesg).
+ * For that we need ptr to point to zeroed byte.
+ * Skip metadata that could be stored in freed object so ptr
+ * will likely point to zeroed byte.
+ */
+ ptr += 16;
+ kasan_ptr_result = strchr(ptr, '1');
+
+ pr_info("use-after-free in strrchr\n");
+ kasan_ptr_result = strrchr(ptr, '1');
+
+ pr_info("use-after-free in strcmp\n");
+ kasan_int_result = strcmp(ptr, "2");
+
+ pr_info("use-after-free in strncmp\n");
+ kasan_int_result = strncmp(ptr, "2", 1);
+
+ pr_info("use-after-free in strlen\n");
+ kasan_int_result = strlen(ptr);
+
+ pr_info("use-after-free in strnlen\n");
+ kasan_int_result = strnlen(ptr, 1);
+}
+
+static noinline void kasan_bitops(void)
+{
+ /*
+ * Allocate 1 more byte, which causes kzalloc to round up to 16-bytes;
+ * this way we do not actually corrupt other memory.
+ */
+ long *bits = xzalloc(sizeof(*bits) + 1);
+ if (!bits)
+ return;
+
+ /*
+ * Below calls try to access bit within allocated memory; however, the
+ * below accesses are still out-of-bounds, since bitops are defined to
+ * operate on the whole long the bit is in.
+ */
+ pr_info("out-of-bounds in set_bit\n");
+ set_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in __set_bit\n");
+ __set_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in clear_bit\n");
+ clear_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in __clear_bit\n");
+ __clear_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in change_bit\n");
+ change_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in __change_bit\n");
+ __change_bit(BITS_PER_LONG, bits);
+
+ /*
+ * Below calls try to access bit beyond allocated memory.
+ */
+ pr_info("out-of-bounds in test_and_set_bit\n");
+ test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in __test_and_set_bit\n");
+ __test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in test_and_clear_bit\n");
+ test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in __test_and_clear_bit\n");
+ __test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in test_and_change_bit\n");
+ test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in __test_and_change_bit\n");
+ __test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in test_bit\n");
+ kasan_int_result = test_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+#if defined(clear_bit_unlock_is_negative_byte)
+ pr_info("out-of-bounds in clear_bit_unlock_is_negative_byte\n");
+ kasan_int_result = clear_bit_unlock_is_negative_byte(BITS_PER_LONG +
+ BITS_PER_BYTE, bits);
+#endif
+ free(bits);
+}
+
+static int do_kasan_test(int argc, char *argv[])
+{
+ /*
+ * Temporarily enable multi-shot mode. Otherwise, we'd only get a
+ * report for the first case.
+ */
+ bool multishot = kasan_save_enable_multi_shot();
+
+ malloc_oob_right();
+ malloc_oob_left();
+ malloc_oob_realloc_more();
+ malloc_oob_realloc_less();
+ malloc_oob_16();
+ malloc_oob_in_memset();
+ malloc_oob_memset_2();
+ malloc_oob_memset_4();
+ malloc_oob_memset_8();
+ malloc_oob_memset_16();
+ malloc_uaf();
+ malloc_uaf_memset();
+ malloc_uaf2();
+ kasan_stack_oob();
+ kasan_global_oob();
+ kasan_alloca_oob_left();
+ kasan_alloca_oob_right();
+ kasan_memchr();
+ kasan_memcmp();
+ kasan_strings();
+ kasan_bitops();
+
+ kasan_restore_multi_shot(multishot);
+
+ return 0;
+}
+
+BAREBOX_CMD_START(kasan_tests)
+ .cmd = do_kasan_test,
+ BAREBOX_CMD_DESC("Run KAsan tests")
+ BAREBOX_CMD_COMPLETE(empty_complete)
+BAREBOX_CMD_END
diff --git a/lib/libfile.c b/lib/libfile.c
index b4d87b624a..20bb689a79 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -353,7 +353,7 @@ int copy_file(const char *src, const char *dst, int verbose)
goto out;
}
- /* Set O_TRUNC only if file exist and is a regular file */
+ /* Set O_TRUNC only if file exists and is a regular file */
if (!s && S_ISREG(dststat.st_mode))
mode |= O_TRUNC;
@@ -364,14 +364,21 @@ int copy_file(const char *src, const char *dst, int verbose)
goto out;
}
- discard_range(dstfd, srcstat.st_size, 0);
+ ret = stat(src, &srcstat);
+ if (ret)
+ goto out;
- if (verbose) {
- if (stat(src, &srcstat) < 0)
- srcstat.st_size = 0;
+ if (srcstat.st_size != FILESIZE_MAX) {
+ discard_range(dstfd, srcstat.st_size, 0);
+ if (s || S_ISREG(dststat.st_mode)) {
+ ret = ftruncate(dstfd, srcstat.st_size);
+ if (ret)
+ goto out;
+ }
+ }
+ if (verbose)
init_progression_bar(srcstat.st_size);
- }
while (1) {
r = read(srcfd, rw_buf, RW_BUF_SIZE);
diff --git a/lib/list_sort.c b/lib/list_sort.c
index 84c6f6465b..f5122b2592 100644
--- a/lib/list_sort.c
+++ b/lib/list_sort.c
@@ -144,152 +144,3 @@ void list_sort(void *priv, struct list_head *head,
merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
}
EXPORT_SYMBOL(list_sort);
-
-#ifdef CONFIG_TEST_LIST_SORT
-
-#include <linux/random.h>
-
-/*
- * The pattern of set bits in the list length determines which cases
- * are hit in list_sort().
- */
-#define TEST_LIST_LEN (512+128+2) /* not including head */
-
-#define TEST_POISON1 0xDEADBEEF
-#define TEST_POISON2 0xA324354C
-
-struct debug_el {
- unsigned int poison1;
- struct list_head list;
- unsigned int poison2;
- int value;
- unsigned serial;
-};
-
-/* Array, containing pointers to all elements in the test list */
-static struct debug_el **elts __initdata;
-
-static int __init check(struct debug_el *ela, struct debug_el *elb)
-{
- if (ela->serial >= TEST_LIST_LEN) {
- printk(KERN_ERR "list_sort_test: error: incorrect serial %d\n",
- ela->serial);
- return -EINVAL;
- }
- if (elb->serial >= TEST_LIST_LEN) {
- printk(KERN_ERR "list_sort_test: error: incorrect serial %d\n",
- elb->serial);
- return -EINVAL;
- }
- if (elts[ela->serial] != ela || elts[elb->serial] != elb) {
- printk(KERN_ERR "list_sort_test: error: phantom element\n");
- return -EINVAL;
- }
- if (ela->poison1 != TEST_POISON1 || ela->poison2 != TEST_POISON2) {
- printk(KERN_ERR "list_sort_test: error: bad poison: %#x/%#x\n",
- ela->poison1, ela->poison2);
- return -EINVAL;
- }
- if (elb->poison1 != TEST_POISON1 || elb->poison2 != TEST_POISON2) {
- printk(KERN_ERR "list_sort_test: error: bad poison: %#x/%#x\n",
- elb->poison1, elb->poison2);
- return -EINVAL;
- }
- return 0;
-}
-
-static int __init cmp(void *priv, struct list_head *a, struct list_head *b)
-{
- struct debug_el *ela, *elb;
-
- ela = container_of(a, struct debug_el, list);
- elb = container_of(b, struct debug_el, list);
-
- check(ela, elb);
- return ela->value - elb->value;
-}
-
-static int __init list_sort_test(void)
-{
- int i, count = 1, err = -EINVAL;
- struct debug_el *el;
- struct list_head *cur, *tmp;
- LIST_HEAD(head);
-
- printk(KERN_DEBUG "list_sort_test: start testing list_sort()\n");
-
- elts = kmalloc(sizeof(void *) * TEST_LIST_LEN, GFP_KERNEL);
- if (!elts) {
- printk(KERN_ERR "list_sort_test: error: cannot allocate "
- "memory\n");
- goto exit;
- }
-
- for (i = 0; i < TEST_LIST_LEN; i++) {
- el = kmalloc(sizeof(*el), GFP_KERNEL);
- if (!el) {
- printk(KERN_ERR "list_sort_test: error: cannot "
- "allocate memory\n");
- goto exit;
- }
- /* force some equivalencies */
- el->value = prandom_u32() % (TEST_LIST_LEN / 3);
- el->serial = i;
- el->poison1 = TEST_POISON1;
- el->poison2 = TEST_POISON2;
- elts[i] = el;
- list_add_tail(&el->list, &head);
- }
-
- list_sort(NULL, &head, cmp);
-
- for (cur = head.next; cur->next != &head; cur = cur->next) {
- struct debug_el *el1;
- int cmp_result;
-
- if (cur->next->prev != cur) {
- printk(KERN_ERR "list_sort_test: error: list is "
- "corrupted\n");
- goto exit;
- }
-
- cmp_result = cmp(NULL, cur, cur->next);
- if (cmp_result > 0) {
- printk(KERN_ERR "list_sort_test: error: list is not "
- "sorted\n");
- goto exit;
- }
-
- el = container_of(cur, struct debug_el, list);
- el1 = container_of(cur->next, struct debug_el, list);
- if (cmp_result == 0 && el->serial >= el1->serial) {
- printk(KERN_ERR "list_sort_test: error: order of "
- "equivalent elements not preserved\n");
- goto exit;
- }
-
- if (check(el, el1)) {
- printk(KERN_ERR "list_sort_test: error: element check "
- "failed\n");
- goto exit;
- }
- count++;
- }
-
- if (count != TEST_LIST_LEN) {
- printk(KERN_ERR "list_sort_test: error: bad list length %d",
- count);
- goto exit;
- }
-
- err = 0;
-exit:
- kfree(elts);
- list_for_each_safe(cur, tmp, &head) {
- list_del(cur);
- kfree(container_of(cur, struct debug_el, list));
- }
- return err;
-}
-module_init(list_sort_test);
-#endif /* CONFIG_TEST_LIST_SORT */
diff --git a/lib/logo/Makefile b/lib/logo/Makefile
index eb7aee080e..4149d4ff6c 100644
--- a/lib/logo/Makefile
+++ b/lib/logo/Makefile
@@ -1,17 +1,17 @@
-OPTS_barebox-logo-w64.bblogo="-w 64"
+OPTS_barebox-logo-w64.bblogo = --export-width=64
bblogo-$(CONFIG_BAREBOX_LOGO_64) += barebox-logo-w64
-OPTS_barebox-logo-w240.bblogo="-w 240"
+OPTS_barebox-logo-w240.bblogo = --export-width=240
bblogo-$(CONFIG_BAREBOX_LOGO_240) += barebox-logo-w240
-OPTS_barebox-logo-w320.bblogo="-w 320"
+OPTS_barebox-logo-w320.bblogo = --export-width=320
bblogo-$(CONFIG_BAREBOX_LOGO_320) += barebox-logo-w320
-OPTS_barebox-logo-w400.bblogo="-w 400"
+OPTS_barebox-logo-w400.bblogo = --export-width=400
bblogo-$(CONFIG_BAREBOX_LOGO_400) += barebox-logo-w400
-OPTS_barebox-logo-w640.bblogo="-w 640"
+OPTS_barebox-logo-w640.bblogo = --export-width=640
bblogo-$(CONFIG_BAREBOX_LOGO_640) += barebox-logo-w640
obj-y += $(patsubst %,%.bblogo.o,$(bblogo-y))
@@ -21,7 +21,7 @@ extra-y += $(patsubst %,%.bblogo.o,$(bblogo-y))
obj-$(CONFIG_BAREBOX_LOGO) += logo.o
-quiet_cmd_logo_S = LOGO.S $@
+quiet_cmd_logo_S = LOGO.S $@
cmd_logo_S = \
( \
echo '\#include <asm-generic/barebox.lds.h>'; \
@@ -38,10 +38,15 @@ cmd_logo_S = \
%.bblogo.S: %.bblogo FORCE
$(call if_changed,logo_S)
-quiet_cmd_logo = LOGO.S $@
+# Inkscape 0.92.4 supports -z -e but Inkscape 1.0 uses --export-type=png
+INKSCAPEOPTS += $(call try-run, inkscape -z -e -,-z -e -,--export-type=png)
+# Inkscape 1.0 supports -o -
+INKSCAPEOPTS += $(call try-run, inkscape -o -,-o -,)
+
+quiet_cmd_logo = LOGO.S $@
cmd_logo = \
( \
- inkscape -z $(OPTS_$(@F)) -e $@ $< > /dev/null; \
+ inkscape $(OPTS_$(@F)) $(INKSCAPEOPTS) $< > $@; \
)
%.bblogo: $(srctree)/Documentation/barebox.svg FORCE
diff --git a/lib/lzo/Kconfig b/lib/lzo/Kconfig
index 9276b2128a..17b0083236 100644
--- a/lib/lzo/Kconfig
+++ b/lib/lzo/Kconfig
@@ -1,7 +1,3 @@
config LZO_DECOMPRESS
bool "include lzo uncompression support"
select UNCOMPRESS
-
-config LZO_COMPRESS
- bool
-
diff --git a/lib/lzo/Makefile b/lib/lzo/Makefile
index f2191f4a3d..0e576a1c10 100644
--- a/lib/lzo/Makefile
+++ b/lib/lzo/Makefile
@@ -1,4 +1 @@
-
-obj-$(CONFIG_LZO_COMPRESS) += lzo1x_compress.o
obj-$(CONFIG_LZO_DECOMPRESS) += lzo1x_decompress_safe.o
-
diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c
deleted file mode 100644
index 236eb21167..0000000000
--- a/lib/lzo/lzo1x_compress.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * LZO1X Compressor from LZO
- *
- * Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <markus@oberhumer.com>
- *
- * The full LZO package can be found at:
- * http://www.oberhumer.com/opensource/lzo/
- *
- * Changed for Linux kernel use by:
- * Nitin Gupta <nitingupta910@gmail.com>
- * Richard Purdie <rpurdie@openedhand.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <asm/unaligned.h>
-#include <linux/lzo.h>
-#include "lzodefs.h"
-
-static noinline size_t
-lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
- unsigned char *out, size_t *out_len,
- size_t ti, void *wrkmem)
-{
- const unsigned char *ip;
- unsigned char *op;
- const unsigned char * const in_end = in + in_len;
- const unsigned char * const ip_end = in + in_len - 20;
- const unsigned char *ii;
- lzo_dict_t * const dict = (lzo_dict_t *) wrkmem;
-
- op = out;
- ip = in;
- ii = ip;
- ip += ti < 4 ? 4 - ti : 0;
-
- for (;;) {
- const unsigned char *m_pos;
- size_t t, m_len, m_off;
- u32 dv;
-literal:
- ip += 1 + ((ip - ii) >> 5);
-next:
- if (unlikely(ip >= ip_end))
- break;
- dv = get_unaligned_le32(ip);
- t = ((dv * 0x1824429d) >> (32 - D_BITS)) & D_MASK;
- m_pos = in + dict[t];
- dict[t] = (lzo_dict_t) (ip - in);
- if (unlikely(dv != get_unaligned_le32(m_pos)))
- goto literal;
-
- ii -= ti;
- ti = 0;
- t = ip - ii;
- if (t != 0) {
- if (t <= 3) {
- op[-2] |= t;
- COPY4(op, ii);
- op += t;
- } else if (t <= 16) {
- *op++ = (t - 3);
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
- op += t;
- } else {
- if (t <= 18) {
- *op++ = (t - 3);
- } else {
- size_t tt = t - 18;
- *op++ = 0;
- while (unlikely(tt > 255)) {
- tt -= 255;
- *op++ = 0;
- }
- *op++ = tt;
- }
- do {
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
- op += 16;
- ii += 16;
- t -= 16;
- } while (t >= 16);
- if (t > 0) do {
- *op++ = *ii++;
- } while (--t > 0);
- }
- }
-
- m_len = 4;
- {
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ64)
- u64 v;
- v = get_unaligned((const u64 *) (ip + m_len)) ^
- get_unaligned((const u64 *) (m_pos + m_len));
- if (unlikely(v == 0)) {
- do {
- m_len += 8;
- v = get_unaligned((const u64 *) (ip + m_len)) ^
- get_unaligned((const u64 *) (m_pos + m_len));
- if (unlikely(ip + m_len >= ip_end))
- goto m_len_done;
- } while (v == 0);
- }
-# if defined(__LITTLE_ENDIAN)
- m_len += (unsigned) __builtin_ctzll(v) / 8;
-# elif defined(__BIG_ENDIAN)
- m_len += (unsigned) __builtin_clzll(v) / 8;
-# else
-# error "missing endian definition"
-# endif
-#elif defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ32)
- u32 v;
- v = get_unaligned((const u32 *) (ip + m_len)) ^
- get_unaligned((const u32 *) (m_pos + m_len));
- if (unlikely(v == 0)) {
- do {
- m_len += 4;
- v = get_unaligned((const u32 *) (ip + m_len)) ^
- get_unaligned((const u32 *) (m_pos + m_len));
- if (v != 0)
- break;
- m_len += 4;
- v = get_unaligned((const u32 *) (ip + m_len)) ^
- get_unaligned((const u32 *) (m_pos + m_len));
- if (unlikely(ip + m_len >= ip_end))
- goto m_len_done;
- } while (v == 0);
- }
-# if defined(__LITTLE_ENDIAN)
- m_len += (unsigned) __builtin_ctz(v) / 8;
-# elif defined(__BIG_ENDIAN)
- m_len += (unsigned) __builtin_clz(v) / 8;
-# else
-# error "missing endian definition"
-# endif
-#else
- if (unlikely(ip[m_len] == m_pos[m_len])) {
- do {
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (unlikely(ip + m_len >= ip_end))
- goto m_len_done;
- } while (ip[m_len] == m_pos[m_len]);
- }
-#endif
- }
-m_len_done:
-
- m_off = ip - m_pos;
- ip += m_len;
- ii = ip;
- if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
- m_off -= 1;
- *op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2));
- *op++ = (m_off >> 3);
- } else if (m_off <= M3_MAX_OFFSET) {
- m_off -= 1;
- if (m_len <= M3_MAX_LEN)
- *op++ = (M3_MARKER | (m_len - 2));
- else {
- m_len -= M3_MAX_LEN;
- *op++ = M3_MARKER | 0;
- while (unlikely(m_len > 255)) {
- m_len -= 255;
- *op++ = 0;
- }
- *op++ = (m_len);
- }
- *op++ = (m_off << 2);
- *op++ = (m_off >> 6);
- } else {
- m_off -= 0x4000;
- if (m_len <= M4_MAX_LEN)
- *op++ = (M4_MARKER | ((m_off >> 11) & 8)
- | (m_len - 2));
- else {
- m_len -= M4_MAX_LEN;
- *op++ = (M4_MARKER | ((m_off >> 11) & 8));
- while (unlikely(m_len > 255)) {
- m_len -= 255;
- *op++ = 0;
- }
- *op++ = (m_len);
- }
- *op++ = (m_off << 2);
- *op++ = (m_off >> 6);
- }
- goto next;
- }
- *out_len = op - out;
- return in_end - (ii - ti);
-}
-
-int lzo1x_1_compress(const unsigned char *in, size_t in_len,
- unsigned char *out, size_t *out_len,
- void *wrkmem)
-{
- const unsigned char *ip = in;
- unsigned char *op = out;
- size_t l = in_len;
- size_t t = 0;
-
- while (l > 20) {
- size_t ll = l <= (M4_MAX_OFFSET + 1) ? l : (M4_MAX_OFFSET + 1);
- uintptr_t ll_end = (uintptr_t) ip + ll;
- if ((ll_end + ((t + ll) >> 5)) <= ll_end)
- break;
- BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
- memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
- t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem);
- ip += ll;
- op += *out_len;
- l -= ll;
- }
- t += l;
-
- if (t > 0) {
- const unsigned char *ii = in + in_len - t;
-
- if (op == out && t <= 238) {
- *op++ = (17 + t);
- } else if (t <= 3) {
- op[-2] |= t;
- } else if (t <= 18) {
- *op++ = (t - 3);
- } else {
- size_t tt = t - 18;
- *op++ = 0;
- while (tt > 255) {
- tt -= 255;
- *op++ = 0;
- }
- *op++ = tt;
- }
- if (t >= 16) do {
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
- op += 16;
- ii += 16;
- t -= 16;
- } while (t >= 16);
- if (t > 0) do {
- *op++ = *ii++;
- } while (--t > 0);
- }
-
- *op++ = M4_MARKER | 1;
- *op++ = 0;
- *op++ = 0;
-
- *out_len = op - out;
- return LZO_E_OK;
-}
-EXPORT_SYMBOL_GPL(lzo1x_1_compress);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("LZO1X-1 Compressor");
diff --git a/lib/ratp.c b/lib/ratp.c
index 8167c5bd4c..ce30223bac 100644
--- a/lib/ratp.c
+++ b/lib/ratp.c
@@ -1109,6 +1109,8 @@ static int ratp_behaviour_h2(struct ratp_internal *ri, void *pkt)
ratp_state_change(ri, RATP_STATE_LAST_ACK);
+ ri->sn_received = ratp_sn(hdr);
+
return 1;
}
@@ -1714,6 +1716,8 @@ void ratp_close(struct ratp *ratp)
list_for_each_entry_safe(msg, tmp, &ri->sendmsg, list)
ratp_msg_done(ri, msg, -ECONNRESET);
+ free(ri->recvbuf);
+ free(ri->sendbuf);
free(ri);
ratp->internal = NULL;
diff --git a/lib/readkey.c b/lib/readkey.c
index fd72951046..c26e9d51ab 100644
--- a/lib/readkey.c
+++ b/lib/readkey.c
@@ -61,7 +61,7 @@ int read_key(void)
esc[i] = getchar();
if (esc[i++] == '~')
break;
- if (i == ARRAY_SIZE(esc))
+ if (i == ARRAY_SIZE(esc) - 1)
return -1;
}
}
diff --git a/lib/readline.c b/lib/readline.c
index 3d16c1838c..e5370f9c7b 100644
--- a/lib/readline.c
+++ b/lib/readline.c
@@ -3,7 +3,6 @@
#include <init.h>
#include <libbb.h>
#include <poller.h>
-#include <ratp_bb.h>
#include <xfuncs.h>
#include <complete.h>
#include <linux/ctype.h>
@@ -200,11 +199,8 @@ int readline(const char *prompt, char *buf, int len)
puts (prompt);
while (1) {
- while (!tstc()) {
+ while (!tstc())
poller_call();
- if (IS_ENABLED(CONFIG_CONSOLE_RATP))
- barebox_ratp_command_run();
- }
ichar = read_key();
diff --git a/lib/string.c b/lib/string.c
index 717b59aa50..d250e58643 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -13,6 +13,9 @@
* * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
* - Added strsep() which will replace strtok() soon (because strsep() is
* reentrant and should be faster). Use only strsep() in new code, please.
+ * * Mon Sep 14 2020, Ahmad Fatoum <a.fatoum@pengutronix.de>
+ * - Kissed strtok() goodbye
+ *
*/
#include <linux/types.h>
@@ -20,8 +23,6 @@
#include <linux/ctype.h>
#include <malloc.h>
-char * ___strtok;
-
#ifndef __HAVE_ARCH_STRNICMP
/**
* strnicmp - Case insensitive, length-limited string comparison
@@ -99,6 +100,19 @@ char * strcpy(char * dest,const char *src)
#endif
EXPORT_SYMBOL(strcpy);
+/**
+ * stpcpy - Copy a %NUL terminated string, but return pointer to %NUL
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char *stpcpy(char *dest, const char *src)
+{
+ while ((*dest++ = *src++) != '\0')
+ /* nothing */;
+ return dest - 1;
+}
+EXPORT_SYMBOL(stpcpy);
+
#ifndef __HAVE_ARCH_STRNCPY
/**
* strncpy - Copy a length-limited, %NUL-terminated string
@@ -208,6 +222,8 @@ int strcmp(const char * cs,const char * ct)
{
register signed char __res;
+ BUG_ON(!cs || !ct);
+
while (1) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
@@ -229,6 +245,8 @@ int strncmp(const char * cs, const char * ct, size_t count)
{
register signed char __res = 0;
+ BUG_ON(!cs || !ct);
+
while (count) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
@@ -392,36 +410,6 @@ char * strpbrk(const char * cs,const char * ct)
#endif
EXPORT_SYMBOL(strpbrk);
-#ifndef __HAVE_ARCH_STRTOK
-/**
- * strtok - Split a string into tokens
- * @s: The string to be searched
- * @ct: The characters to search for
- *
- * WARNING: strtok is deprecated, use strsep instead.
- */
-char * strtok(char * s, const char * ct)
-{
- char *sbegin, *send;
-
- sbegin = s ? s : ___strtok;
- if (!sbegin) {
- return NULL;
- }
- sbegin += strspn(sbegin,ct);
- if (*sbegin == '\0') {
- ___strtok = NULL;
- return( NULL );
- }
- send = strpbrk( sbegin, ct);
- if (send && *send != '\0')
- *send++ = '\0';
- ___strtok = send;
- return (sbegin);
-}
-#endif
-EXPORT_SYMBOL(strtok);
-
#ifndef __HAVE_ARCH_STRSEP
/**
* strsep - Split a string into tokens
@@ -451,6 +439,49 @@ char * strsep(char **s, const char *ct)
#endif
EXPORT_SYMBOL(strsep);
+/**
+ * strsep_unescaped - Split a string into tokens, while ignoring escaped delimiters
+ * @s: The string to be searched
+ * @ct: The delimiter characters to search for
+ *
+ * strsep_unescaped() behaves like strsep unless it meets an escaped delimiter.
+ * In that case, it shifts the string back in memory to overwrite the escape's
+ * backslash then continues the search until an unescaped delimiter is found.
+ */
+char *strsep_unescaped(char **s, const char *ct)
+{
+ char *sbegin = *s, *hay;
+ const char *needle;
+ size_t shift = 0;
+
+ if (sbegin == NULL)
+ return NULL;
+
+ for (hay = sbegin; *hay != '\0'; ++hay) {
+ *hay = hay[shift];
+
+ if (*hay == '\\') {
+ *hay = hay[++shift];
+ if (*hay != '\\')
+ continue;
+ }
+
+ for (needle = ct; *needle != '\0'; ++needle) {
+ if (*hay == *needle)
+ goto match;
+ }
+ }
+
+ *s = NULL;
+ return sbegin;
+
+match:
+ *hay = '\0';
+ *s = &hay[shift + 1];
+
+ return sbegin;
+}
+
#ifndef __HAVE_ARCH_STRSWAB
/**
* strswab - swap adjacent even and odd bytes in %NUL-terminated string
@@ -487,7 +518,7 @@ char *strswab(const char *s)
*
* Do not use memset() to access IO space, use memset_io() instead.
*/
-void *__default_memset(void * s,int c,size_t count)
+void *__default_memset(void * s, int c, size_t count)
{
char *xs = (char *) s;
@@ -498,8 +529,20 @@ void *__default_memset(void * s,int c,size_t count)
}
EXPORT_SYMBOL(__default_memset);
+void __no_sanitize_address *__nokasan_default_memset(void * s, int c, size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+EXPORT_SYMBOL(__nokasan_default_memset);
+
#ifndef __HAVE_ARCH_MEMSET
void *memset(void *s, int c, size_t count) __alias(__default_memset);
+void *__memset(void *s, int c, size_t count) __alias(__default_memset);
#endif
/**
@@ -511,7 +554,19 @@ void *memset(void *s, int c, size_t count) __alias(__default_memset);
* You should not use this function to access IO space, use memcpy_toio()
* or memcpy_fromio() instead.
*/
-void *__default_memcpy(void * dest,const void *src,size_t count)
+void *__default_memcpy(void * dest,const void *src, size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+EXPORT_SYMBOL(__default_memcpy);
+
+void __no_sanitize_address *__nokasan_default_memcpy(void * dest,
+ const void *src, size_t count)
{
char *tmp = (char *) dest, *s = (char *) src;
@@ -520,11 +575,13 @@ void *__default_memcpy(void * dest,const void *src,size_t count)
return dest;
}
-EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(__nokasan_default_memcpy);
#ifndef __HAVE_ARCH_MEMCPY
void *memcpy(void * dest, const void *src, size_t count)
__alias(__default_memcpy);
+void *__memcpy(void * dest, const void *src, size_t count)
+ __alias(__default_memcpy);
#endif
diff --git a/lib/ubsan.c b/lib/ubsan.c
index 41a5731dda..085d470cf7 100644
--- a/lib/ubsan.c
+++ b/lib/ubsan.c
@@ -60,8 +60,8 @@ static bool was_reported(struct source_location *location)
static void print_source_location(const char *prefix,
struct source_location *loc)
{
- pr_err("%s %s:%d:%d\n", prefix, loc->file_name,
- loc->line & LINE_MASK, loc->column & COLUMN_MASK);
+ printf("%s %s:%d:%d\n", prefix, loc->file_name,
+ loc->line & LINE_MASK, loc->column & COLUMN_MASK);
}
static bool suppress_report(struct source_location *loc)
@@ -157,16 +157,16 @@ static void ubsan_prologue(struct source_location *location,
{
in_ubsan++;
- pr_err("========================================"
- "========================================\n");
+ printf("========================================"
+ "========================================\n");
print_source_location("UBSAN: Undefined behaviour in", location);
}
static void ubsan_epilogue(unsigned long *flags)
{
dump_stack();
- pr_err("========================================"
- "========================================\n");
+ printf("========================================"
+ "========================================\n");
in_ubsan--;
}
@@ -186,13 +186,13 @@ static void handle_overflow(struct overflow_data *data, void *lhs,
val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs);
val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs);
- pr_err("%s integer overflow:\n",
- type_is_signed(type) ? "signed" : "unsigned");
- pr_err("%s %c %s cannot be represented in type %s\n",
- lhs_val_str,
- op,
- rhs_val_str,
- type->type_name);
+ printf("%s integer overflow:\n",
+ type_is_signed(type) ? "signed" : "unsigned");
+ printf("%s %c %s cannot be represented in type %s\n",
+ lhs_val_str,
+ op,
+ rhs_val_str,
+ type->type_name);
ubsan_epilogue(&flags);
}
@@ -232,8 +232,8 @@ void __ubsan_handle_negate_overflow(struct overflow_data *data,
val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val);
- pr_err("negation of %s cannot be represented in type %s:\n",
- old_val_str, data->type->type_name);
+ printf("negation of %s cannot be represented in type %s:\n",
+ old_val_str, data->type->type_name);
ubsan_epilogue(&flags);
}
@@ -254,10 +254,10 @@ void __ubsan_handle_divrem_overflow(struct overflow_data *data,
val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs);
if (type_is_signed(data->type) && get_signed_val(data->type, rhs) == -1)
- pr_err("division of %s by -1 cannot be represented in type %s\n",
- rhs_val_str, data->type->type_name);
+ printf("division of %s by -1 cannot be represented in type %s\n",
+ rhs_val_str, data->type->type_name);
else
- pr_err("division by zero\n");
+ printf("division by zero\n");
ubsan_epilogue(&flags);
}
@@ -272,9 +272,9 @@ static void handle_null_ptr_deref(struct type_mismatch_data_common *data)
ubsan_prologue(data->location, &flags);
- pr_err("%s null pointer of type %s\n",
- type_check_kinds[data->type_check_kind],
- data->type->type_name);
+ printf("%s null pointer of type %s\n",
+ type_check_kinds[data->type_check_kind],
+ data->type->type_name);
ubsan_epilogue(&flags);
}
@@ -289,10 +289,10 @@ static void handle_misaligned_access(struct type_mismatch_data_common *data,
ubsan_prologue(data->location, &flags);
- pr_err("%s misaligned address %p for type %s\n",
- type_check_kinds[data->type_check_kind],
- (void *)ptr, data->type->type_name);
- pr_err("which requires %ld byte alignment\n", data->alignment);
+ printf("%s misaligned address %p for type %s\n",
+ type_check_kinds[data->type_check_kind],
+ (void *)ptr, data->type->type_name);
+ printf("which requires %ld byte alignment\n", data->alignment);
ubsan_epilogue(&flags);
}
@@ -306,10 +306,10 @@ static void handle_object_size_mismatch(struct type_mismatch_data_common *data,
return;
ubsan_prologue(data->location, &flags);
- pr_err("%s address %p with insufficient space\n",
+ printf("%s address %p with insufficient space\n",
type_check_kinds[data->type_check_kind],
(void *) ptr);
- pr_err("for an object of type %s\n", data->type->type_name);
+ printf("for an object of type %s\n", data->type->type_name);
ubsan_epilogue(&flags);
}
@@ -364,8 +364,8 @@ void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, void *index)
ubsan_prologue(&data->location, &flags);
val_to_string(index_str, sizeof(index_str), data->index_type, index);
- pr_err("index %s is out of range for type %s\n", index_str,
- data->array_type->type_name);
+ printf("index %s is out of range for type %s\n", index_str,
+ data->array_type->type_name);
ubsan_epilogue(&flags);
}
EXPORT_SYMBOL(__ubsan_handle_out_of_bounds);
@@ -408,22 +408,22 @@ void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs);
if (val_is_negative(rhs_type, rhs))
- pr_err("shift exponent %s is negative\n", rhs_str);
+ printf("shift exponent %s is negative\n", rhs_str);
else if (get_unsigned_val(rhs_type, rhs) >=
- type_bit_width(lhs_type))
- pr_err("shift exponent %s is too large for %u-bit type %s\n",
- rhs_str,
- type_bit_width(lhs_type),
- lhs_type->type_name);
+ type_bit_width(lhs_type))
+ printf("shift exponent %s is too large for %u-bit type %s\n",
+ rhs_str,
+ type_bit_width(lhs_type),
+ lhs_type->type_name);
else if (val_is_negative(lhs_type, lhs))
- pr_err("left shift of negative value %s\n",
- lhs_str);
+ printf("left shift of negative value %s\n",
+ lhs_str);
else
- pr_err("left shift of %s by %s places cannot be"
- " represented in type %s\n",
- lhs_str, rhs_str,
- lhs_type->type_name);
+ printf("left shift of %s by %s places cannot be"
+ " represented in type %s\n",
+ lhs_str, rhs_str,
+ lhs_type->type_name);
ubsan_epilogue(&flags);
}
@@ -435,7 +435,7 @@ void __ubsan_handle_builtin_unreachable(struct unreachable_data *data)
unsigned long flags;
ubsan_prologue(&data->location, &flags);
- pr_err("calling __builtin_unreachable()\n");
+ printf("calling __builtin_unreachable()\n");
ubsan_epilogue(&flags);
panic("can't return from __builtin_unreachable()");
}
@@ -454,8 +454,8 @@ void __ubsan_handle_load_invalid_value(struct invalid_value_data *data,
val_to_string(val_str, sizeof(val_str), data->type, val);
- pr_err("load of value %s is not a valid value for type %s\n",
- val_str, data->type->type_name);
+ printf("load of value %s is not a valid value for type %s\n",
+ val_str, data->type->type_name);
ubsan_epilogue(&flags);
}
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 6fe0283e84..4834501ff1 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -41,7 +41,8 @@ static int skip_atoi(const char **s)
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
-static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)
+static char *number(char *buf, const char *end, unsigned long long num, int base, int size,
+ int precision, int type)
{
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
@@ -146,7 +147,8 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
#define PAGE_SIZE 4096
#endif
-static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+static char *string(char *buf, const char *end, const char *s, int field_width,
+ int precision, int flags)
{
int len, i;
@@ -175,8 +177,20 @@ static char *string(char *buf, char *end, char *s, int field_width, int precisio
return buf;
}
+static char *raw_pointer(char *buf, const char *end, const void *ptr, int field_width,
+ int precision, int flags)
+{
+ flags |= SMALL;
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
+}
+
#ifndef __PBL__
-static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+static char *symbol_string(char *buf, const char *end, const void *ptr, int field_width,
+ int precision, int flags)
{
unsigned long value = (unsigned long) ptr;
#ifdef CONFIG_KALLSYMS
@@ -191,7 +205,7 @@ static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int
}
static noinline_for_stack
-char *ip4_addr_string(char *buf, char *end, const u8 *addr, int field_width,
+char *ip4_addr_string(char *buf, const char *end, const u8 *addr, int field_width,
int precision, int flags, const char *fmt)
{
char ip4_addr[sizeof("255.255.255.255")];
@@ -211,9 +225,19 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr, int field_width,
return string(buf, end, ip4_addr, field_width, precision, flags);
}
+static
+char *error_string(char *buf, const char *end, const u8 *errptr, int field_width,
+ int precision, int flags, const char *fmt)
+{
+ if (!IS_ERR(errptr))
+ return raw_pointer(buf, end, errptr, field_width, precision, flags);
+
+ return string(buf, end, strerror(-PTR_ERR(errptr)), field_width, precision, flags);
+}
+
static noinline_for_stack
-char *uuid_string(char *buf, char *end, const u8 *addr, int field_width,
- int precision, int flags, const char *fmt)
+char *uuid_string(char *buf, const char *end, const u8 *addr, int field_width,
+ int precision, int flags, const char *fmt)
{
char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
char *p = uuid;
@@ -259,8 +283,8 @@ char *uuid_string(char *buf, char *end, const u8 *addr, int field_width,
}
static noinline_for_stack
-char *address_val(char *buf, char *end, const void *addr,
- int field_width, int precision, int flags, const char *fmt)
+char *address_val(char *buf, const char *end, const void *addr,
+ int field_width, int precision, int flags, const char *fmt)
{
unsigned long long num;
@@ -314,7 +338,8 @@ char *address_val(char *buf, char *end, const void *addr,
* function pointers are really function descriptors, which contain a
* pointer to the real address.
*/
-static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+static char *pointer(const char *fmt, char *buf, const char *end, const void *ptr,
+ int field_width, int precision, int flags)
{
switch (*fmt) {
case 'S':
@@ -341,23 +366,18 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field
return ip4_addr_string(buf, end, ptr, field_width, precision, flags, fmt);
}
break;
+ case 'e':
+ return error_string(buf, end, ptr, field_width, precision, flags, fmt);
}
- flags |= SMALL;
- if (field_width == -1) {
- field_width = 2*sizeof(void *);
- flags |= ZEROPAD;
- }
- return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
+
+ return raw_pointer(buf, end, ptr, field_width, precision, flags);
}
+
#else
-static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+static char *pointer(const char *fmt, char *buf, const char *end, const void *ptr,
+ int field_width, int precision, int flags)
{
- flags |= SMALL;
- if (field_width == -1) {
- field_width = 2*sizeof(void *);
- flags |= ZEROPAD;
- }
- return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
+ return raw_pointer(buf, end, ptr, field_width, precision, flags);
}
#endif