diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Kconfig | 16 | ||||
-rw-r--r-- | lib/Kconfig.debug | 113 | ||||
-rw-r--r-- | lib/Kconfig.kasan | 18 | ||||
-rw-r--r-- | lib/Kconfig.ubsan | 29 | ||||
-rw-r--r-- | lib/Makefile | 10 | ||||
-rw-r--r-- | lib/debugobjects.c | 2 | ||||
-rw-r--r-- | lib/hash.c | 39 | ||||
-rw-r--r-- | lib/iomap.c | 23 | ||||
-rw-r--r-- | lib/list_debug.c | 9 | ||||
-rw-r--r-- | lib/qmi_encdec.c | 880 | ||||
-rw-r--r-- | lib/qmi_encdec_priv.h | 66 | ||||
-rw-r--r-- | lib/radix-tree.c | 43 | ||||
-rw-r--r-- | lib/strncpy_from_user.c | 17 | ||||
-rw-r--r-- | lib/ubsan.c | 456 | ||||
-rw-r--r-- | lib/ubsan.h | 84 |
15 files changed, 1797 insertions, 8 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index 1a48744253d7..7f6feb7a4687 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -531,4 +531,20 @@ config ARCH_HAS_PMEM_API config ARCH_HAS_MMIO_FLUSH bool +config QMI_ENCDEC + bool "QMI Encode/Decode Library" + help + Library to encode & decode QMI messages from within + the kernel. The kernel drivers encode the C structure into + QMI message wire format and then send it over a transport. + The kernel drivers receive the QMI message over a transport + and then decode it into a C structure. + +config QMI_ENCDEC_DEBUG + bool "QMI Encode/Decode Library Debug" + help + Kernel config option to enable debugging QMI Encode/Decode + library. This will log the information regarding the element + and message being encoded & decoded. + endmenu diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 63d14d9b51d8..902657d4cac5 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -255,6 +255,13 @@ config PAGE_OWNER If unsure, say N. +config PAGE_OWNER_ENABLE_DEFAULT + bool "Enable Track page owner by default" + depends on PAGE_OWNER + ---help--- + Enable track page owner by default? This value + can be overridden by page_owner_disabled=off|on. + config DEBUG_FS bool "Debug Filesystem" help @@ -824,6 +831,17 @@ config BOOTPARAM_HUNG_TASK_PANIC_VALUE default 0 if !BOOTPARAM_HUNG_TASK_PANIC default 1 if BOOTPARAM_HUNG_TASK_PANIC +config WQ_WATCHDOG + bool "Detect Workqueue Stalls" + depends on DEBUG_KERNEL + help + Say Y here to enable stall detection on workqueues. If a + worker pool doesn't make forward progress on a pending work + item for over a given amount of time, 30s by default, a + warning message is printed along with dump of workqueue + state. This can be configured through kernel parameter + "workqueue.watchdog_thresh" and its sysfs counterpart. + endmenu # "Debug lockups and hangs" config PANIC_ON_OOPS @@ -867,6 +885,16 @@ config SCHED_INFO bool default n +config PANIC_ON_SCHED_BUG + bool "Panic on all bugs encountered by the scheduler" + help + Say Y here to panic on all 'BUG:' conditions encountered by the + scheduler, even potentially-recoverable ones such as scheduling + while atomic, sleeping from invalid context, and detection of + broken arch topologies. + + Say N if unsure. + config PANIC_ON_RT_THROTTLING bool "Panic on RT throttling" help @@ -876,6 +904,15 @@ config PANIC_ON_RT_THROTTLING Say N if unsure. +config SYSRQ_SCHED_DEBUG + bool "Print scheduling debugging info from sysrq-trigger" + depends on SCHED_DEBUG + default y + help + If you say Y here, the "show-task-states(T)" and + "show-blocked-tasks(W)" sysrq-triggers will print additional + scheduling statistics. + config SCHEDSTATS bool "Collect scheduler statistics" depends on DEBUG_KERNEL && PROC_FS @@ -928,6 +965,24 @@ config TIMER_STATS (it defaults to deactivated on bootup and will only be activated if some application like powertop activates it explicitly). +config DEBUG_TASK_STACK_SCAN_OFF + bool "Disable kmemleak task stack scan by default" + depends on DEBUG_KMEMLEAK + help + Say Y here to disable kmemleak task stack scan by default + at compile time. It can be enabled later if required by + writing to the debugfs entry : + echo "stack=on" > /sys/kernel/debug/kmemleak. + +config DEBUG_MODULE_SCAN_OFF + bool "Disable module memory scan for leaks by default" + depends on DEBUG_KMEMLEAK + help + Say Y here to disable scanning kernel modules area list + by default for memory leaks. Module scan an potentially + run with irq/preemption disabled for considerable amount + of time. + config DEBUG_PREEMPT bool "Debug preemptible kernel" depends on DEBUG_KERNEL && PREEMPT && TRACE_IRQFLAGS_SUPPORT @@ -957,6 +1012,28 @@ config DEBUG_SPINLOCK best used in conjunction with the NMI watchdog so that spinlock deadlocks are also debuggable. +choice + prompt "Perform Action on spinlock bug" + depends on DEBUG_SPINLOCK + + default DEBUG_SPINLOCK_BITE_ON_BUG + + config DEBUG_SPINLOCK_BITE_ON_BUG + bool "Cause a Watchdog Bite on Spinlock bug" + depends on QCOM_WATCHDOG_V2 + help + On a spinlock bug, cause a watchdog bite so that we can get the precise + state of the system captured at the time of spin dump. This is mutually + exclusive with the below DEBUG_SPINLOCK_PANIC_ON_BUG config. + + config DEBUG_SPINLOCK_PANIC_ON_BUG + bool "Cause a Kernel Panic on Spinlock bug" + help + On a spinlock bug, cause a kernel panic so that we can get the complete + information about the system at the time of spin dump in the dmesg. + This is mutually exclusive with the above DEBUG_SPINLOCK_BITE_ON_BUG. +endchoice + config DEBUG_MUTEXES bool "Mutex debugging: basic checks" depends on DEBUG_KERNEL @@ -1553,6 +1630,20 @@ config FAIL_MMC_REQUEST and to test how the mmc host driver handles retries from the block device. +config UFS_FAULT_INJECTION + bool "Fault-injection capability for UFS IO" + select DEBUG_FS + depends on FAULT_INJECTION && SCSI_UFSHCD + help + Provide fault-injection capability for UFS IO. + This will make the UFS host controller driver to randomly + abort ongoing commands in the host controller, update OCS + field according to the injected fatal error and can also + forcefully hang the command indefinitely till upper layer + timeout occurs. This is useful to test error handling in + the UFS contoller driver and test how the driver handles + the retries from block/SCSI mid layer. + config FAIL_FUTEX bool "Fault-injection capability for futexes" select DEBUG_FS @@ -1861,6 +1952,19 @@ config MEMTEST memtest=17, mean do 17 test patterns. If you are unsure how to answer this question, answer N. +config MEMTEST_ENABLE_DEFAULT + int "Enable Memtest pattern test by default? (0-17)" + range 0 17 + default "0" + depends on MEMTEST + help + This option helps to select Memtest to be enabled through + kernel defconfig options. Alternatively it can be enabled + using memtest=<patterns> kernel command line. + + Default value is kept as "0" so that it is kept as disabled. + To enable enter any value between 1-17 range. + config TEST_STATIC_KEYS tristate "Test static keys" default n @@ -1870,7 +1974,16 @@ config TEST_STATIC_KEYS If unsure, say N. +config PANIC_ON_DATA_CORRUPTION + bool "Cause a Kernel Panic When Data Corruption is detected" + help + Select this option to upgrade warnings for potentially + recoverable data corruption scenarios to system-halting panics, + for easier detection and debug. + source "samples/Kconfig" source "lib/Kconfig.kgdb" +source "lib/Kconfig.ubsan" + diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan index 0fee5acd5aa0..e6ba5447672f 100644 --- a/lib/Kconfig.kasan +++ b/lib/Kconfig.kasan @@ -16,6 +16,8 @@ config KASAN This feature consumes about 1/8 of available memory and brings about ~x3 performance slowdown. For better error detection enable CONFIG_STACKTRACE. + See KASAN_SANITIZE_ALL for selectively compiling files and directories + with this compiler feature enabled. choice prompt "Instrumentation type" @@ -42,6 +44,22 @@ config KASAN_INLINE endchoice +config KASAN_SANITIZE_ALL + bool "KASan: Enable Instrumentation for entire kernel" + depends on KASAN + default y + help + Enable compilation with $(CFLAGS_KASAN) by default. + KASAN_SANITIZE := n - exclude all files in a directory + KASAN_SANITIZE_file_name.o := n - exclude a single file + Setting KASAN_SANITIZE_ALL to 'n' allows enabling kasan in + only certain files or directories. + KASAN_SANITIZE := y - include all files in a directory + KASAN_SANITIZE_file_name.o := y - include single file + + KASAN_SANITIZE does not affect subdirectories. + KASAN_SANITIZE_file_name.o has priority over KASAN_SANITIZE. + config TEST_KASAN tristate "Module for testing kasan for bug detection" depends on m && KASAN diff --git a/lib/Kconfig.ubsan b/lib/Kconfig.ubsan new file mode 100644 index 000000000000..49518fb48cab --- /dev/null +++ b/lib/Kconfig.ubsan @@ -0,0 +1,29 @@ +config ARCH_HAS_UBSAN_SANITIZE_ALL + bool + +config UBSAN + bool "Undefined behaviour sanity checker" + help + This option enables undefined behaviour sanity checker + Compile-time instrumentation is used to detect various undefined + behaviours in runtime. Various types of checks may be enabled + via boot parameter ubsan_handle (see: Documentation/ubsan.txt). + +config UBSAN_SANITIZE_ALL + bool "Enable instrumentation for the entire kernel" + depends on UBSAN + depends on ARCH_HAS_UBSAN_SANITIZE_ALL + default y + help + This option activates instrumentation for the entire kernel. + If you don't enable this option, you have to explicitly specify + UBSAN_SANITIZE := y for the files/directories you want to check for UB. + +config UBSAN_ALIGNMENT + bool "Enable checking of pointers alignment" + depends on UBSAN + default y if !HAVE_EFFICIENT_UNALIGNED_ACCESS + help + This option enables detection of unaligned memory accesses. + Enabling this option on architectures that support unalligned + accesses may produce a lot of false positives. diff --git a/lib/Makefile b/lib/Makefile index 7f1de26613d2..03a447a234cc 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -22,12 +22,14 @@ lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o klist.o obj-y += lockref.o +KASAN_SANITIZE_find_bit.o := n + obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \ gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o memweight.o kfifo.o \ percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o \ - once.o + once.o hash.o obj-y += string_helpers.o obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o obj-y += hexdump.o @@ -208,3 +210,9 @@ quiet_cmd_build_OID_registry = GEN $@ clean-files += oid_registry_data.c obj-$(CONFIG_UCS2_STRING) += ucs2_string.o + +obj-$(CONFIG_QMI_ENCDEC) += qmi_encdec.o + +obj-$(CONFIG_UBSAN) += ubsan.o + +UBSAN_SANITIZE_ubsan.o := n diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 547f7f923dbc..9ddee8c271a7 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -17,6 +17,7 @@ #include <linux/debugfs.h> #include <linux/slab.h> #include <linux/hash.h> +#include <linux/kmemleak.h> #define ODEBUG_HASH_BITS 14 #define ODEBUG_HASH_SIZE (1 << ODEBUG_HASH_BITS) @@ -100,6 +101,7 @@ static void fill_pool(void) if (!new) return; + kmemleak_not_leak(new); raw_spin_lock_irqsave(&pool_lock, flags); hlist_add_head(&new->node, &obj_pool); obj_pool_free++; diff --git a/lib/hash.c b/lib/hash.c new file mode 100644 index 000000000000..fea973f4bd57 --- /dev/null +++ b/lib/hash.c @@ -0,0 +1,39 @@ +/* General purpose hashing library + * + * That's a start of a kernel hashing library, which can be extended + * with further algorithms in future. arch_fast_hash{2,}() will + * eventually resolve to an architecture optimized implementation. + * + * Copyright 2013 Francesco Fusco <ffusco@redhat.com> + * Copyright 2013 Daniel Borkmann <dborkman@redhat.com> + * Copyright 2013 Thomas Graf <tgraf@redhat.com> + * Licensed under the GNU General Public License, version 2.0 (GPLv2) + */ + +#include <linux/jhash.h> +#include <linux/hash.h> +#include <linux/cache.h> + +static struct fast_hash_ops arch_hash_ops __read_mostly = { + .hash = jhash, + .hash2 = jhash2, +}; + +u32 arch_fast_hash(const void *data, u32 len, u32 seed) +{ + return arch_hash_ops.hash(data, len, seed); +} +EXPORT_SYMBOL_GPL(arch_fast_hash); + +u32 arch_fast_hash2(const u32 *data, u32 len, u32 seed) +{ + return arch_hash_ops.hash2(data, len, seed); +} +EXPORT_SYMBOL_GPL(arch_fast_hash2); + +static int __init hashlib_init(void) +{ + setup_arch_fast_hash(&arch_hash_ops); + return 0; +} +early_initcall(hashlib_init); diff --git a/lib/iomap.c b/lib/iomap.c index fc3dcb4b238e..b29a91e63f01 100644 --- a/lib/iomap.c +++ b/lib/iomap.c @@ -5,6 +5,7 @@ */ #include <linux/pci.h> #include <linux/io.h> +#include <linux/msm_rtb.h> #include <linux/export.h> @@ -70,26 +71,31 @@ static void bad_io_access(unsigned long port, const char *access) unsigned int ioread8(void __iomem *addr) { - IO_COND(addr, return inb(port), return readb(addr)); + uncached_logk_pc(LOGK_READL, __builtin_return_address(0), addr); + IO_COND(addr, return inb(port), return readb_no_log(addr)); return 0xff; } unsigned int ioread16(void __iomem *addr) { - IO_COND(addr, return inw(port), return readw(addr)); + uncached_logk_pc(LOGK_READL, __builtin_return_address(0), addr); + IO_COND(addr, return inw(port), return readw_no_log(addr)); return 0xffff; } unsigned int ioread16be(void __iomem *addr) { + uncached_logk_pc(LOGK_READL, __builtin_return_address(0), addr); IO_COND(addr, return pio_read16be(port), return mmio_read16be(addr)); return 0xffff; } unsigned int ioread32(void __iomem *addr) { - IO_COND(addr, return inl(port), return readl(addr)); + uncached_logk_pc(LOGK_READL, __builtin_return_address(0), addr); + IO_COND(addr, return inl(port), return readl_no_log(addr)); return 0xffffffff; } unsigned int ioread32be(void __iomem *addr) { + uncached_logk_pc(LOGK_READL, __builtin_return_address(0), addr); IO_COND(addr, return pio_read32be(port), return mmio_read32be(addr)); return 0xffffffff; } @@ -111,22 +117,27 @@ EXPORT_SYMBOL(ioread32be); void iowrite8(u8 val, void __iomem *addr) { - IO_COND(addr, outb(val,port), writeb(val, addr)); + uncached_logk_pc(LOGK_WRITEL, __builtin_return_address(0), addr); + IO_COND(addr, outb(val, port), writeb_no_log(val, addr)); } void iowrite16(u16 val, void __iomem *addr) { - IO_COND(addr, outw(val,port), writew(val, addr)); + uncached_logk_pc(LOGK_WRITEL, __builtin_return_address(0), addr); + IO_COND(addr, outw(val, port), writew_no_log(val, addr)); } void iowrite16be(u16 val, void __iomem *addr) { + uncached_logk_pc(LOGK_WRITEL, __builtin_return_address(0), addr); IO_COND(addr, pio_write16be(val,port), mmio_write16be(val, addr)); } void iowrite32(u32 val, void __iomem *addr) { - IO_COND(addr, outl(val,port), writel(val, addr)); + uncached_logk_pc(LOGK_WRITEL, __builtin_return_address(0), addr); + IO_COND(addr, outl(val, port), writel_no_log(val, addr)); } void iowrite32be(u32 val, void __iomem *addr) { + uncached_logk_pc(LOGK_WRITEL, __builtin_return_address(0), addr); IO_COND(addr, pio_write32be(val,port), mmio_write32be(val, addr)); } EXPORT_SYMBOL(iowrite8); diff --git a/lib/list_debug.c b/lib/list_debug.c index c24c2f7e296f..8cf180bfaabe 100644 --- a/lib/list_debug.c +++ b/lib/list_debug.c @@ -11,6 +11,7 @@ #include <linux/bug.h> #include <linux/kernel.h> #include <linux/rculist.h> +#include <linux/bug.h> /* * Insert a new entry between two known consecutive entries. @@ -34,6 +35,10 @@ void __list_add(struct list_head *new, WARN(new == prev || new == next, "list_add double add: new=%p, prev=%p, next=%p.\n", new, prev, next); + + BUG_ON((prev->next != next || next->prev != prev || + new == prev || new == next) && PANIC_CORRUPTION); + next->prev = new; new->next = next; new->prev = prev; @@ -59,8 +64,10 @@ void __list_del_entry(struct list_head *entry) "but was %p\n", entry, prev->next) || WARN(next->prev != entry, "list_del corruption. next->prev should be %p, " - "but was %p\n", entry, next->prev)) + "but was %p\n", entry, next->prev)) { + BUG_ON(PANIC_CORRUPTION); return; + } __list_del(prev, next); } diff --git a/lib/qmi_encdec.c b/lib/qmi_encdec.c new file mode 100644 index 000000000000..72b506bececc --- /dev/null +++ b/lib/qmi_encdec.c @@ -0,0 +1,880 @@ +/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/string.h> +#include <linux/qmi_encdec.h> + +#include "qmi_encdec_priv.h" + +#define TLV_LEN_SIZE sizeof(uint16_t) +#define TLV_TYPE_SIZE sizeof(uint8_t) +#define OPTIONAL_TLV_TYPE_START 0x10 + +#ifdef CONFIG_QMI_ENCDEC_DEBUG + +#define qmi_encdec_dump(prefix_str, buf, buf_len) do { \ + const u8 *ptr = buf; \ + int i, linelen, remaining = buf_len; \ + int rowsize = 16, groupsize = 1; \ + unsigned char linebuf[256]; \ + for (i = 0; i < buf_len; i += rowsize) { \ + linelen = min(remaining, rowsize); \ + remaining -= linelen; \ + hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, \ + linebuf, sizeof(linebuf), false); \ + pr_debug("%s: %s\n", prefix_str, linebuf); \ + } \ +} while (0) + +#define QMI_ENCODE_LOG_MSG(buf, buf_len) do { \ + qmi_encdec_dump("QMI_ENCODE_MSG", buf, buf_len); \ +} while (0) + +#define QMI_DECODE_LOG_MSG(buf, buf_len) do { \ + qmi_encdec_dump("QMI_DECODE_MSG", buf, buf_len); \ +} while (0) + +#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \ + pr_debug("QMI_ENCODE_ELEM lvl: %d, len: %d, size: %d\n", \ + level, elem_len, elem_size); \ + qmi_encdec_dump("QMI_ENCODE_ELEM", buf, (elem_len * elem_size)); \ +} while (0) + +#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \ + pr_debug("QMI_DECODE_ELEM lvl: %d, len: %d, size: %d\n", \ + level, elem_len, elem_size); \ + qmi_encdec_dump("QMI_DECODE_ELEM", buf, (elem_len * elem_size)); \ +} while (0) + +#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) do { \ + pr_debug("QMI_ENCODE_TLV type: %d, len: %d\n", tlv_type, tlv_len); \ +} while (0) + +#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) do { \ + pr_debug("QMI_DECODE_TLV type: %d, len: %d\n", tlv_type, tlv_len); \ +} while (0) + +#else + +#define QMI_ENCODE_LOG_MSG(buf, buf_len) { } +#define QMI_DECODE_LOG_MSG(buf, buf_len) { } +#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) { } +#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) { } +#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) { } +#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) { } + +#endif + +static int _qmi_kernel_encode(struct elem_info *ei_array, + void *out_buf, void *in_c_struct, + uint32_t out_buf_len, int enc_level); + +static int _qmi_kernel_decode(struct elem_info *ei_array, + void *out_c_struct, + void *in_buf, uint32_t in_buf_len, + int dec_level); +static struct elem_info *skip_to_next_elem(struct elem_info *ei_array, + int level); + +/** + * qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message + * @ei_array: Struct info array describing the structure. + * @level: Level to identify the depth of the nested structures. + * + * @return: expected maximum length of the QMI message or 0 on failure. + */ +static int qmi_calc_max_msg_len(struct elem_info *ei_array, + int level) +{ + int max_msg_len = 0; + struct elem_info *temp_ei; + + if (!ei_array) + return max_msg_len; + + for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) { + /* Flag to identify the optional element is not encoded */ + if (temp_ei->data_type == QMI_OPT_FLAG) + continue; + + if (temp_ei->data_type == QMI_DATA_LEN) { + max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ? + sizeof(uint8_t) : sizeof(uint16_t)); + continue; + } else if (temp_ei->data_type == QMI_STRUCT) { + max_msg_len += (temp_ei->elem_len * + qmi_calc_max_msg_len(temp_ei->ei_array, + (level + 1))); + } else if (temp_ei->data_type == QMI_STRING) { + if (level > 1) + max_msg_len += temp_ei->elem_len <= U8_MAX ? + sizeof(uint8_t) : sizeof(uint16_t); + max_msg_len += temp_ei->elem_len * temp_ei->elem_size; + } else { + max_msg_len += (temp_ei->elem_len * temp_ei->elem_size); + } + + /* + * Type & Length info. not prepended for elements in the + * nested structure. + */ + if (level == 1) + max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE); + } + return max_msg_len; +} + +/** + * qmi_calc_min_msg_len() - Calculate the minimum length of a QMI message + * @ei_array: Struct info array describing the structure. + * @level: Level to identify the depth of the nested structures. + * + * @return: expected minimum length of the QMI message or 0 on failure. + */ +static int qmi_calc_min_msg_len(struct elem_info *ei_array, + int level) +{ + int min_msg_len = 0; + struct elem_info *temp_ei = ei_array; + + if (!ei_array) + return min_msg_len; + + while (temp_ei->data_type != QMI_EOTI) { + /* Optional elements do not count in minimum length */ + if (temp_ei->data_type == QMI_OPT_FLAG) { + temp_ei = skip_to_next_elem(temp_ei, level); + continue; + } + + if (temp_ei->data_type == QMI_DATA_LEN) { + min_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ? + sizeof(uint8_t) : sizeof(uint16_t)); + temp_ei++; + continue; + } else if (temp_ei->data_type == QMI_STRUCT) { + min_msg_len += qmi_calc_min_msg_len(temp_ei->ei_array, + (level + 1)); + temp_ei++; + } else if (temp_ei->data_type == QMI_STRING) { + if (level > 1) + min_msg_len += temp_ei->elem_len <= U8_MAX ? + sizeof(uint8_t) : sizeof(uint16_t); + min_msg_len += temp_ei->elem_len * temp_ei->elem_size; + temp_ei++; + } else { + min_msg_len += (temp_ei->elem_len * temp_ei->elem_size); + temp_ei++; + } + + /* + * Type & Length info. not prepended for elements in the + * nested structure. + */ + if (level == 1) + min_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE); + } + return min_msg_len; +} + +/** + * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message + * @desc: Pointer to structure descriptor. + * + * @return: true if the maximum message length embedded in structure + * descriptor matches the calculated value, else false. + */ +bool qmi_verify_max_msg_len(struct msg_desc *desc) +{ + int calc_max_msg_len; + + if (!desc) + return false; + + calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1); + if (calc_max_msg_len != desc->max_msg_len) { + pr_err("%s: Calc. len %d != Passed len %d\n", + __func__, calc_max_msg_len, desc->max_msg_len); + return false; + } + return true; +} + +/** + * qmi_kernel_encode() - Encode to QMI message wire format + * @desc: Pointer to structure descriptor. + * @out_buf: Buffer to hold the encoded QMI message. + * @out_buf_len: Length of the out buffer. + * @in_c_struct: C Structure to be encoded. + * + * @return: size of encoded message on success, < 0 for error. + */ +int qmi_kernel_encode(struct msg_desc *desc, + void *out_buf, uint32_t out_buf_len, + void *in_c_struct) +{ + int enc_level = 1; + int ret, calc_max_msg_len, calc_min_msg_len; + + if (!desc) + return -EINVAL; + + /* Check the possibility of a zero length QMI message */ + if (!in_c_struct) { + calc_min_msg_len = qmi_calc_min_msg_len(desc->ei_array, 1); + if (calc_min_msg_len) { + pr_err("%s: Calc. len %d != 0, but NULL in_c_struct\n", + __func__, calc_min_msg_len); + return -EINVAL; + } else { + return 0; + } + } + + /* + * Not a zero-length message. Ensure the output buffer and + * element information array are not NULL. + */ + if (!out_buf || !desc->ei_array) + return -EINVAL; + + if (desc->max_msg_len < out_buf_len) + return -ETOOSMALL; + + ret = _qmi_kernel_encode(desc->ei_array, out_buf, + in_c_struct, out_buf_len, enc_level); + if (ret == -ETOOSMALL) { + calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1); + pr_err("%s: Calc. len %d != Out buf len %d\n", + __func__, calc_max_msg_len, out_buf_len); + } + return ret; +} +EXPORT_SYMBOL(qmi_kernel_encode); + +/** + * qmi_encode_basic_elem() - Encodes elements of basic/primary data type + * @buf_dst: Buffer to store the encoded information. + * @buf_src: Buffer containing the elements to be encoded. + * @elem_len: Number of elements, in the buf_src, to be encoded. + * @elem_size: Size of a single instance of the element to be encoded. + * + * @return: number of bytes of encoded information. + * + * This function encodes the "elem_len" number of data elements, each of + * size "elem_size" bytes from the source buffer "buf_src" and stores the + * encoded information in the destination buffer "buf_dst". The elements are + * of primary data type which include uint8_t - uint64_t or similar. This + * function returns the number of bytes of encoded information. + */ +static int qmi_encode_basic_elem(void *buf_dst, void *buf_src, + uint32_t elem_len, uint32_t elem_size) +{ + uint32_t i, rc = 0; + + for (i = 0; i < elem_len; i++) { + QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size); + rc += elem_size; + } + + return rc; +} + +/** + * qmi_encode_struct_elem() - Encodes elements of struct data type + * @ei_array: Struct info array descibing the struct element. + * @buf_dst: Buffer to store the encoded information. + * @buf_src: Buffer containing the elements to be encoded. + * @elem_len: Number of elements, in the buf_src, to be encoded. + * @out_buf_len: Available space in the encode buffer. + * @enc_level: Depth of the nested structure from the main structure. + * + * @return: Mumber of bytes of encoded information, on success. + * < 0 on error. + * + * This function encodes the "elem_len" number of struct elements, each of + * size "ei_array->elem_size" bytes from the source buffer "buf_src" and + * stores the encoded information in the destination buffer "buf_dst". The + * elements are of struct data type which includes any C structure. This + * function returns the number of bytes of encoded information. + */ +static int qmi_encode_struct_elem(struct elem_info *ei_array, + void *buf_dst, void *buf_src, + uint32_t elem_len, uint32_t out_buf_len, + int enc_level) +{ + int i, rc, encoded_bytes = 0; + struct elem_info *temp_ei = ei_array; + + for (i = 0; i < elem_len; i++) { + rc = _qmi_kernel_encode(temp_ei->ei_array, buf_dst, buf_src, + (out_buf_len - encoded_bytes), + enc_level); + if (rc < 0) { + pr_err("%s: STRUCT Encode failure\n", __func__); + return rc; + } + buf_dst = buf_dst + rc; + buf_src = buf_src + temp_ei->elem_size; + encoded_bytes += rc; + } + + return encoded_bytes; +} + +/** + * qmi_encode_string_elem() - Encodes elements of string data type + * @ei_array: Struct info array descibing the string element. + * @buf_dst: Buffer to store the encoded information. + * @buf_src: Buffer containing the elements to be encoded. + * @out_buf_len: Available space in the encode buffer. + * @enc_level: Depth of the string element from the main structure. + * + * @return: Mumber of bytes of encoded information, on success. + * < 0 on error. + * + * This function encodes a string element of maximum length "ei_array->elem_len" + * bytes from the source buffer "buf_src" and stores the encoded information in + * the destination buffer "buf_dst". This function returns the number of bytes + * of encoded information. + */ +static int qmi_encode_string_elem(struct elem_info *ei_array, + void *buf_dst, void *buf_src, + uint32_t out_buf_len, int enc_level) +{ + int rc; + int encoded_bytes = 0; + struct elem_info *temp_ei = ei_array; + uint32_t string_len = 0; + uint32_t string_len_sz = 0; + + string_len = strlen(buf_src); + string_len_sz = temp_ei->elem_len <= U8_MAX ? + sizeof(uint8_t) : sizeof(uint16_t); + if (string_len > temp_ei->elem_len) { + pr_err("%s: String to be encoded is longer - %d > %d\n", + __func__, string_len, temp_ei->elem_len); + return -EINVAL; + } + + if (enc_level == 1) { + if (string_len + TLV_LEN_SIZE + TLV_TYPE_SIZE > + out_buf_len) { + pr_err("%s: Output len %d > Out Buf len %d\n", + __func__, string_len, out_buf_len); + return -ETOOSMALL; + } + } else { + if (string_len + string_len_sz > out_buf_len) { + pr_err("%s: Output len %d > Out Buf len %d\n", + __func__, string_len, out_buf_len); + return -ETOOSMALL; + } + rc = qmi_encode_basic_elem(buf_dst, &string_len, + 1, string_len_sz); + encoded_bytes += rc; + } + + rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src, + string_len, temp_ei->elem_size); + encoded_bytes += rc; + QMI_ENCODE_LOG_ELEM(enc_level, string_len, temp_ei->elem_size, buf_src); + return encoded_bytes; +} + +/** + * skip_to_next_elem() - Skip to next element in the structure to be encoded + * @ei_array: Struct info describing the element to be skipped. + * @level: Depth level of encoding/decoding to identify nested structures. + * + * @return: Struct info of the next element that can be encoded. + * + * This function is used while encoding optional elements. If the flag + * corresponding to an optional element is not set, then encoding the + * optional element can be skipped. This function can be used to perform + * that operation. + */ +static struct elem_info *skip_to_next_elem(struct elem_info *ei_array, + int level) +{ + struct elem_info *temp_ei = ei_array; + uint8_t tlv_type; + + if (level > 1) { + temp_ei = temp_ei + 1; + } else { + do { + tlv_type = temp_ei->tlv_type; + temp_ei = temp_ei + 1; + } while (tlv_type == temp_ei->tlv_type); + } + + return temp_ei; +} + +/** + * _qmi_kernel_encode() - Core Encode Function + * @ei_array: Struct info array describing the structure to be encoded. + * @out_buf: Buffer to hold the encoded QMI message. + * @in_c_struct: Pointer to the C structure to be encoded. + * @out_buf_len: Available space in the encode buffer. + * @enc_level: Encode level to indicate the depth of the nested structure, + * within the main structure, being encoded. + * + * @return: Number of bytes of encoded information, on success. + * < 0 on error. + */ +static int _qmi_kernel_encode(struct elem_info *ei_array, + void *out_buf, void *in_c_struct, + uint32_t out_buf_len, int enc_level) +{ + struct elem_info *temp_ei = ei_array; + uint8_t opt_flag_value = 0; + uint32_t data_len_value = 0, data_len_sz; + uint8_t *buf_dst = (uint8_t *)out_buf; + uint8_t *tlv_pointer; + uint32_t tlv_len; + uint8_t tlv_type; + uint32_t encoded_bytes = 0; + void *buf_src; + int encode_tlv = 0; + int rc; + + tlv_pointer = buf_dst; + tlv_len = 0; + if (enc_level == 1) + buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE); + + while (temp_ei->data_type != QMI_EOTI) { + buf_src = in_c_struct + temp_ei->offset; + tlv_type = temp_ei->tlv_type; + + if (temp_ei->is_array == NO_ARRAY) { + data_len_value = 1; + } else if (temp_ei->is_array == STATIC_ARRAY) { + data_len_value = temp_ei->elem_len; + } else if (data_len_value <= 0 || + temp_ei->elem_len < data_len_value) { + pr_err("%s: Invalid data length\n", __func__); + return -EINVAL; + } + + switch (temp_ei->data_type) { + case QMI_OPT_FLAG: + rc = qmi_encode_basic_elem(&opt_flag_value, buf_src, + 1, sizeof(uint8_t)); + if (opt_flag_value) + temp_ei = temp_ei + 1; + else + temp_ei = skip_to_next_elem(temp_ei, enc_level); + break; + + case QMI_DATA_LEN: + memcpy(&data_len_value, buf_src, temp_ei->elem_size); + data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ? + sizeof(uint8_t) : sizeof(uint16_t); + /* Check to avoid out of range buffer access */ + if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE + + TLV_TYPE_SIZE) > out_buf_len) { + pr_err("%s: Too Small Buffer @DATA_LEN\n", + __func__); + return -ETOOSMALL; + } + rc = qmi_encode_basic_elem(buf_dst, &data_len_value, + 1, data_len_sz); + UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, + encoded_bytes, tlv_len, encode_tlv, rc); + if (!data_len_value) + temp_ei = skip_to_next_elem(temp_ei, enc_level); + else + encode_tlv = 0; + break; + + case QMI_UNSIGNED_1_BYTE: + case QMI_UNSIGNED_2_BYTE: + case QMI_UNSIGNED_4_BYTE: + case QMI_UNSIGNED_8_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + case QMI_SIGNED_4_BYTE_ENUM: + /* Check to avoid out of range buffer access */ + if (((data_len_value * temp_ei->elem_size) + + encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) > + out_buf_len) { + pr_err("%s: Too Small Buffer @data_type:%d\n", + __func__, temp_ei->data_type); + return -ETOOSMALL; + } + rc = qmi_encode_basic_elem(buf_dst, buf_src, + data_len_value, temp_ei->elem_size); + QMI_ENCODE_LOG_ELEM(enc_level, data_len_value, + temp_ei->elem_size, buf_src); + UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, + encoded_bytes, tlv_len, encode_tlv, rc); + break; + + case QMI_STRUCT: + rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src, + data_len_value, (out_buf_len - encoded_bytes), + (enc_level + 1)); + if (rc < 0) + return rc; + UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, + encoded_bytes, tlv_len, encode_tlv, rc); + break; + + case QMI_STRING: + rc = qmi_encode_string_elem(temp_ei, buf_dst, buf_src, + out_buf_len - encoded_bytes, enc_level); + if (rc < 0) + return rc; + UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, + encoded_bytes, tlv_len, encode_tlv, rc); + break; + default: + pr_err("%s: Unrecognized data type\n", __func__); + return -EINVAL; + + } + + if (encode_tlv && enc_level == 1) { + QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer); + QMI_ENCODE_LOG_TLV(tlv_type, tlv_len); + encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE); + tlv_pointer = buf_dst; + tlv_len = 0; + buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE; + encode_tlv = 0; + } + } + QMI_ENCODE_LOG_MSG(out_buf, encoded_bytes); + return encoded_bytes; +} + +/** + * qmi_kernel_decode() - Decode to C Structure format + * @desc: Pointer to structure descriptor. + * @out_c_struct: Buffer to hold the decoded C structure. + * @in_buf: Buffer containg the QMI message to be decoded. + * @in_buf_len: Length of the incoming QMI message. + * + * @return: 0 on success, < 0 on error. + */ +int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct, + void *in_buf, uint32_t in_buf_len) +{ + int dec_level = 1; + int rc = 0; + + if (!desc || !desc->ei_array) + return -EINVAL; + + if (!out_c_struct || !in_buf || !in_buf_len) + return -EINVAL; + + if (desc->max_msg_len < in_buf_len) + return -EINVAL; + + rc = _qmi_kernel_decode(desc->ei_array, out_c_struct, + in_buf, in_buf_len, dec_level); + if (rc < 0) + return rc; + else + return 0; +} +EXPORT_SYMBOL(qmi_kernel_decode); + +/** + * qmi_decode_basic_elem() - Decodes elements of basic/primary data type + * @buf_dst: Buffer to store the decoded element. + * @buf_src: Buffer containing the elements in QMI wire format. + * @elem_len: Number of elements to be decoded. + * @elem_size: Size of a single instance of the element to be decoded. + * + * @return: Total size of the decoded data elements, in bytes. + * + * This function decodes the "elem_len" number of elements in QMI wire format, + * each of size "elem_size" bytes from the source buffer "buf_src" and stores + * the decoded elements in the destination buffer "buf_dst". The elements are + * of primary data type which include uint8_t - uint64_t or similar. This + * function returns the number of bytes of decoded information. + */ +static int qmi_decode_basic_elem(void *buf_dst, void *buf_src, + uint32_t elem_len, uint32_t elem_size) +{ + uint32_t i, rc = 0; + + for (i = 0; i < elem_len; i++) { + QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size); + rc += elem_size; + } + + return rc; +} + +/** + * qmi_decode_struct_elem() - Decodes elements of struct data type + * @ei_array: Struct info array descibing the struct element. + * @buf_dst: Buffer to store the decoded element. + * @buf_src: Buffer containing the elements in QMI wire format. + * @elem_len: Number of elements to be decoded. + * @tlv_len: Total size of the encoded inforation corresponding to + * this struct element. + * @dec_level: Depth of the nested structure from the main structure. + * + * @return: Total size of the decoded data elements, on success. + * < 0 on error. + * + * This function decodes the "elem_len" number of elements in QMI wire format, + * each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src" + * and stores the decoded elements in the destination buffer "buf_dst". The + * elements are of struct data type which includes any C structure. This + * function returns the number of bytes of decoded information. + */ +static int qmi_decode_struct_elem(struct elem_info *ei_array, void *buf_dst, + void *buf_src, uint32_t elem_len, + uint32_t tlv_len, int dec_level) +{ + int i, rc, decoded_bytes = 0; + struct elem_info *temp_ei = ei_array; + + for (i = 0; i < elem_len && decoded_bytes < tlv_len; i++) { + rc = _qmi_kernel_decode(temp_ei->ei_array, buf_dst, buf_src, + (tlv_len - decoded_bytes), dec_level); + if (rc < 0) + return rc; + buf_src = buf_src + rc; + buf_dst = buf_dst + temp_ei->elem_size; + decoded_bytes += rc; + } + + if ((dec_level <= 2 && decoded_bytes != tlv_len) || + (dec_level > 2 && (i < elem_len || decoded_bytes > tlv_len))) { + pr_err("%s: Fault in decoding: dl(%d), db(%d), tl(%d), i(%d), el(%d)\n", + __func__, dec_level, decoded_bytes, tlv_len, + i, elem_len); + return -EFAULT; + } + return decoded_bytes; +} + +/** + * qmi_decode_string_elem() - Decodes elements of string data type + * @ei_array: Struct info array descibing the string element. + * @buf_dst: Buffer to store the decoded element. + * @buf_src: Buffer containing the elements in QMI wire format. + * @tlv_len: Total size of the encoded inforation corresponding to + * this string element. + * @dec_level: Depth of the string element from the main structure. + * + * @return: Total size of the decoded data elements, on success. + * < 0 on error. + * + * This function decodes the string element of maximum length + * "ei_array->elem_len" from the source buffer "buf_src" and puts it into + * the destination buffer "buf_dst". This function returns number of bytes + * decoded from the input buffer. + */ +static int qmi_decode_string_elem(struct elem_info *ei_array, void *buf_dst, + void *buf_src, uint32_t tlv_len, + int dec_level) +{ + int rc; + int decoded_bytes = 0; + uint32_t string_len = 0; + uint32_t string_len_sz = 0; + struct elem_info *temp_ei = ei_array; + + if (dec_level == 1) { + string_len = tlv_len; + } else { + string_len_sz = temp_ei->elem_len <= U8_MAX ? + sizeof(uint8_t) : sizeof(uint16_t); + rc = qmi_decode_basic_elem(&string_len, buf_src, + 1, string_len_sz); + decoded_bytes += rc; + } + + if (string_len > temp_ei->elem_len) { + pr_err("%s: String len %d > Max Len %d\n", + __func__, string_len, temp_ei->elem_len); + return -ETOOSMALL; + } else if (string_len > tlv_len) { + pr_err("%s: String len %d > Input Buffer Len %d\n", + __func__, string_len, tlv_len); + return -EFAULT; + } + + rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes, + string_len, temp_ei->elem_size); + *((char *)buf_dst + string_len) = '\0'; + decoded_bytes += rc; + QMI_DECODE_LOG_ELEM(dec_level, string_len, temp_ei->elem_size, buf_dst); + return decoded_bytes; +} + +/** + * find_ei() - Find element info corresponding to TLV Type + * @ei_array: Struct info array of the message being decoded. + * @type: TLV Type of the element being searched. + * + * @return: Pointer to struct info, if found + * + * Every element that got encoded in the QMI message will have a type + * information associated with it. While decoding the QMI message, + * this function is used to find the struct info regarding the element + * that corresponds to the type being decoded. + */ +static struct elem_info *find_ei(struct elem_info *ei_array, + uint32_t type) +{ + struct elem_info *temp_ei = ei_array; + while (temp_ei->data_type != QMI_EOTI) { + if (temp_ei->tlv_type == (uint8_t)type) + return temp_ei; + temp_ei = temp_ei + 1; + } + return NULL; +} + +/** + * _qmi_kernel_decode() - Core Decode Function + * @ei_array: Struct info array describing the structure to be decoded. + * @out_c_struct: Buffer to hold the decoded C struct + * @in_buf: Buffer containing the QMI message to be decoded + * @in_buf_len: Length of the QMI message to be decoded + * @dec_level: Decode level to indicate the depth of the nested structure, + * within the main structure, being decoded + * + * @return: Number of bytes of decoded information, on success + * < 0 on error. + */ +static int _qmi_kernel_decode(struct elem_info *ei_array, + void *out_c_struct, + void *in_buf, uint32_t in_buf_len, + int dec_level) +{ + struct elem_info *temp_ei = ei_array; + uint8_t opt_flag_value = 1; + uint32_t data_len_value = 0, data_len_sz = 0; + uint8_t *buf_dst = out_c_struct; + uint8_t *tlv_pointer; + uint32_t tlv_len = 0; + uint32_t tlv_type; + uint32_t decoded_bytes = 0; + void *buf_src = in_buf; + int rc; + + QMI_DECODE_LOG_MSG(in_buf, in_buf_len); + while (decoded_bytes < in_buf_len) { + if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI) + return decoded_bytes; + + if (dec_level == 1) { + tlv_pointer = buf_src; + QMI_ENCDEC_DECODE_TLV(&tlv_type, + &tlv_len, tlv_pointer); + QMI_DECODE_LOG_TLV(tlv_type, tlv_len); + buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE); + decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE); + temp_ei = find_ei(ei_array, tlv_type); + if (!temp_ei && (tlv_type < OPTIONAL_TLV_TYPE_START)) { + pr_err("%s: Inval element info\n", __func__); + return -EINVAL; + } else if (!temp_ei) { + UPDATE_DECODE_VARIABLES(buf_src, + decoded_bytes, tlv_len); + continue; + } + } else { + /* + * No length information for elements in nested + * structures. So use remaining decodable buffer space. + */ + tlv_len = in_buf_len - decoded_bytes; + } + + buf_dst = out_c_struct + temp_ei->offset; + if (temp_ei->data_type == QMI_OPT_FLAG) { + memcpy(buf_dst, &opt_flag_value, sizeof(uint8_t)); + temp_ei = temp_ei + 1; + buf_dst = out_c_struct + temp_ei->offset; + } + + if (temp_ei->data_type == QMI_DATA_LEN) { + data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ? + sizeof(uint8_t) : sizeof(uint16_t); + rc = qmi_decode_basic_elem(&data_len_value, buf_src, + 1, data_len_sz); + memcpy(buf_dst, &data_len_value, sizeof(uint32_t)); + temp_ei = temp_ei + 1; + buf_dst = out_c_struct + temp_ei->offset; + tlv_len -= data_len_sz; + UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); + } + + if (temp_ei->is_array == NO_ARRAY) { + data_len_value = 1; + } else if (temp_ei->is_array == STATIC_ARRAY) { + data_len_value = temp_ei->elem_len; + } else if (data_len_value > temp_ei->elem_len) { + pr_err("%s: Data len %d > max spec %d\n", + __func__, data_len_value, temp_ei->elem_len); + return -ETOOSMALL; + } + + switch (temp_ei->data_type) { + case QMI_UNSIGNED_1_BYTE: + case QMI_UNSIGNED_2_BYTE: + case QMI_UNSIGNED_4_BYTE: + case QMI_UNSIGNED_8_BYTE: + case QMI_SIGNED_2_BYTE_ENUM: + case QMI_SIGNED_4_BYTE_ENUM: + rc = qmi_decode_basic_elem(buf_dst, buf_src, + data_len_value, temp_ei->elem_size); + QMI_DECODE_LOG_ELEM(dec_level, data_len_value, + temp_ei->elem_size, buf_dst); + UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); + break; + + case QMI_STRUCT: + rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src, + data_len_value, tlv_len, (dec_level + 1)); + if (rc < 0) + return rc; + UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); + break; + + case QMI_STRING: + rc = qmi_decode_string_elem(temp_ei, buf_dst, buf_src, + tlv_len, dec_level); + if (rc < 0) + return rc; + UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); + break; + + default: + pr_err("%s: Unrecognized data type\n", __func__); + return -EINVAL; + } + temp_ei = temp_ei + 1; + } + return decoded_bytes; +} +MODULE_DESCRIPTION("QMI kernel enc/dec"); +MODULE_LICENSE("GPL v2"); diff --git a/lib/qmi_encdec_priv.h b/lib/qmi_encdec_priv.h new file mode 100644 index 000000000000..97fe45b9d97a --- /dev/null +++ b/lib/qmi_encdec_priv.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _QMI_ENCDEC_PRIV_H_ +#define _QMI_ENCDEC_PRIV_H_ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/list.h> +#include <linux/socket.h> +#include <linux/gfp.h> +#include <linux/qmi_encdec.h> + +#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \ + *p_dst++ = type; \ + *p_dst++ = ((uint8_t)((length) & 0xFF)); \ + *p_dst++ = ((uint8_t)(((length) >> 8) & 0xFF)); \ +} while (0) + +#define QMI_ENCDEC_DECODE_TLV(p_type, p_length, p_src) do { \ + *p_type = (uint8_t)*p_src++; \ + *p_length = (uint8_t)*p_src++; \ + *p_length |= ((uint8_t)*p_src) << 8; \ +} while (0) + +#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \ +do { \ + memcpy(p_dst, p_src, size); \ + p_dst = (uint8_t *)p_dst + size; \ + p_src = (uint8_t *)p_src + size; \ +} while (0) + +#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \ +do { \ + memcpy(p_dst, p_src, size); \ + p_dst = (uint8_t *)p_dst + size; \ + p_src = (uint8_t *)p_src + size; \ +} while (0) + +#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \ + encoded_bytes, tlv_len, encode_tlv, rc) \ +do { \ + buf_dst = (uint8_t *)buf_dst + rc; \ + encoded_bytes += rc; \ + tlv_len += rc; \ + temp_si = temp_si + 1; \ + encode_tlv = 1; \ +} while (0) + +#define UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc) \ +do { \ + buf_src = (uint8_t *)buf_src + rc; \ + decoded_bytes += rc; \ +} while (0) + +#endif diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 6b79e9026e24..534878e6cf11 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -1035,6 +1035,49 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, EXPORT_SYMBOL(radix_tree_gang_lookup); /** + * radix_tree_gang_lookup_index - perform multiple lookup on a radix tree + * @root: radix tree root + * @results: where the results of the lookup are placed + * @indices: where their indices should be placed + * @first_index: start the lookup from this key + * @max_items: place up to this many items at *results + * + * Performs an index-ascending scan of the tree for present items. Places + * them at *@results and returns the number of items which were placed at + * *@results. The indices are placed in @indices. + * + * The implementation is naive. + * + * Just one difference from radix_tree_gang_lookup, the indices are also + * collected along with the results of lookup. + */ +unsigned int +radix_tree_gang_lookup_index(struct radix_tree_root *root, void **results, + unsigned long *indices, unsigned long first_index, + unsigned int max_items) +{ + struct radix_tree_iter iter; + void **slot; + unsigned int ret = 0; + + if (unlikely(!max_items)) + return 0; + + radix_tree_for_each_slot(slot, root, &iter, first_index) { + results[ret] = indirect_to_ptr(rcu_dereference_raw(*slot)); + if (!results[ret]) + continue; + if (indices) + indices[ret] = iter.index; + if (++ret == max_items) + break; + } + + return ret; +} +EXPORT_SYMBOL(radix_tree_gang_lookup_index); + +/** * radix_tree_gang_lookup_slot - perform multiple slot lookup on radix tree * @root: radix tree root * @results: where the results of the lookup are placed diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c index 5a003a2ebd96..06ebe4efa1f2 100644 --- a/lib/strncpy_from_user.c +++ b/lib/strncpy_from_user.c @@ -14,6 +14,8 @@ (((long) dst | (long) src) & (sizeof(long) - 1)) #endif +#define CHECK_ALIGN(v, a) ((((unsigned long)(v)) & ((a) - 1)) == 0) + /* * Do a strncpy, return length of string without final '\0'. * 'count' is the user-supplied count (return 'count' if we @@ -35,6 +37,21 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long if (IS_UNALIGNED(src, dst)) goto byte_at_a_time; + /* Copy a byte at a time until we align to 8 bytes */ + while (max && (!CHECK_ALIGN(src + res, 8))) { + char c; + int ret; + + ret = __get_user(c, src + res); + if (ret) + return -EFAULT; + dst[res] = c; + if (!c) + return res; + res++; + max--; + } + while (max >= sizeof(unsigned long)) { unsigned long c, data; diff --git a/lib/ubsan.c b/lib/ubsan.c new file mode 100644 index 000000000000..8799ae5e2e42 --- /dev/null +++ b/lib/ubsan.c @@ -0,0 +1,456 @@ +/* + * UBSAN error reporting functions + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Andrey Ryabinin <ryabinin.a.a@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 <linux/bitops.h> +#include <linux/bug.h> +#include <linux/ctype.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/sched.h> + +#include "ubsan.h" + +const char *type_check_kinds[] = { + "load of", + "store to", + "reference binding to", + "member access within", + "member call on", + "constructor call on", + "downcast of", + "downcast of" +}; + +#define REPORTED_BIT 31 + +#if (BITS_PER_LONG == 64) && defined(__BIG_ENDIAN) +#define COLUMN_MASK (~(1U << REPORTED_BIT)) +#define LINE_MASK (~0U) +#else +#define COLUMN_MASK (~0U) +#define LINE_MASK (~(1U << REPORTED_BIT)) +#endif + +#define VALUE_LENGTH 40 + +static bool was_reported(struct source_location *location) +{ + return test_and_set_bit(REPORTED_BIT, &location->reported); +} + +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); +} + +static bool suppress_report(struct source_location *loc) +{ + return current->in_ubsan || was_reported(loc); +} + +static bool type_is_int(struct type_descriptor *type) +{ + return type->type_kind == type_kind_int; +} + +static bool type_is_signed(struct type_descriptor *type) +{ + WARN_ON(!type_is_int(type)); + return type->type_info & 1; +} + +static unsigned type_bit_width(struct type_descriptor *type) +{ + return 1 << (type->type_info >> 1); +} + +static bool is_inline_int(struct type_descriptor *type) +{ + unsigned inline_bits = sizeof(unsigned long)*8; + unsigned bits = type_bit_width(type); + + WARN_ON(!type_is_int(type)); + + return bits <= inline_bits; +} + +static s_max get_signed_val(struct type_descriptor *type, unsigned long val) +{ + if (is_inline_int(type)) { + unsigned extra_bits = sizeof(s_max)*8 - type_bit_width(type); + return ((s_max)val) << extra_bits >> extra_bits; + } + + if (type_bit_width(type) == 64) + return *(s64 *)val; + + return *(s_max *)val; +} + +static bool val_is_negative(struct type_descriptor *type, unsigned long val) +{ + return type_is_signed(type) && get_signed_val(type, val) < 0; +} + +static u_max get_unsigned_val(struct type_descriptor *type, unsigned long val) +{ + if (is_inline_int(type)) + return val; + + if (type_bit_width(type) == 64) + return *(u64 *)val; + + return *(u_max *)val; +} + +static void val_to_string(char *str, size_t size, struct type_descriptor *type, + unsigned long value) +{ + if (type_is_int(type)) { + if (type_bit_width(type) == 128) { +#if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__) + u_max val = get_unsigned_val(type, value); + + scnprintf(str, size, "0x%08x%08x%08x%08x", + (u32)(val >> 96), + (u32)(val >> 64), + (u32)(val >> 32), + (u32)(val)); +#else + WARN_ON(1); +#endif + } else if (type_is_signed(type)) { + scnprintf(str, size, "%lld", + (s64)get_signed_val(type, value)); + } else { + scnprintf(str, size, "%llu", + (u64)get_unsigned_val(type, value)); + } + } +} + +static bool location_is_valid(struct source_location *loc) +{ + return loc->file_name != NULL; +} + +static DEFINE_SPINLOCK(report_lock); + +static void ubsan_prologue(struct source_location *location, + unsigned long *flags) +{ + current->in_ubsan++; + spin_lock_irqsave(&report_lock, *flags); + + pr_err("========================================" + "========================================\n"); + print_source_location("UBSAN: Undefined behaviour in", location); +} + +static void ubsan_epilogue(unsigned long *flags) +{ + dump_stack(); + pr_err("========================================" + "========================================\n"); + spin_unlock_irqrestore(&report_lock, *flags); + current->in_ubsan--; +} + +static void handle_overflow(struct overflow_data *data, unsigned long lhs, + unsigned long rhs, char op) +{ + + struct type_descriptor *type = data->type; + unsigned long flags; + char lhs_val_str[VALUE_LENGTH]; + char rhs_val_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + 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); + + ubsan_epilogue(&flags); +} + +void __ubsan_handle_add_overflow(struct overflow_data *data, + unsigned long lhs, + unsigned long rhs) +{ + + handle_overflow(data, lhs, rhs, '+'); +} +EXPORT_SYMBOL(__ubsan_handle_add_overflow); + +void __ubsan_handle_sub_overflow(struct overflow_data *data, + unsigned long lhs, + unsigned long rhs) +{ + handle_overflow(data, lhs, rhs, '-'); +} +EXPORT_SYMBOL(__ubsan_handle_sub_overflow); + +void __ubsan_handle_mul_overflow(struct overflow_data *data, + unsigned long lhs, + unsigned long rhs) +{ + handle_overflow(data, lhs, rhs, '*'); +} +EXPORT_SYMBOL(__ubsan_handle_mul_overflow); + +void __ubsan_handle_negate_overflow(struct overflow_data *data, + unsigned long old_val) +{ + unsigned long flags; + char old_val_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + 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); + + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_negate_overflow); + + +void __ubsan_handle_divrem_overflow(struct overflow_data *data, + unsigned long lhs, + unsigned long rhs) +{ + unsigned long flags; + char rhs_val_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + 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); + else + pr_err("division by zero\n"); + + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_divrem_overflow); + +static void handle_null_ptr_deref(struct type_mismatch_data *data) +{ + unsigned long flags; + + if (suppress_report(&data->location)) + return; + + 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); + + ubsan_epilogue(&flags); +} + +static void handle_missaligned_access(struct type_mismatch_data *data, + unsigned long ptr) +{ + unsigned long flags; + + if (suppress_report(&data->location)) + return; + + 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); + + ubsan_epilogue(&flags); +} + +static void handle_object_size_mismatch(struct type_mismatch_data *data, + unsigned long ptr) +{ + unsigned long flags; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + pr_err("%s address %pk 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); + ubsan_epilogue(&flags); +} + +void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, + unsigned long ptr) +{ + + if (!ptr) + handle_null_ptr_deref(data); + else if (data->alignment && !IS_ALIGNED(ptr, data->alignment)) + handle_missaligned_access(data, ptr); + else + handle_object_size_mismatch(data, ptr); +} +EXPORT_SYMBOL(__ubsan_handle_type_mismatch); + +void __ubsan_handle_nonnull_return(struct nonnull_return_data *data) +{ + unsigned long flags; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + pr_err("null pointer returned from function declared to never return null\n"); + + if (location_is_valid(&data->attr_location)) + print_source_location("returns_nonnull attribute specified in", + &data->attr_location); + + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_nonnull_return); + +void __ubsan_handle_vla_bound_not_positive(struct vla_bound_data *data, + unsigned long bound) +{ + unsigned long flags; + char bound_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + val_to_string(bound_str, sizeof(bound_str), data->type, bound); + pr_err("variable length array bound value %s <= 0\n", bound_str); + + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_vla_bound_not_positive); + +void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, + unsigned long index) +{ + unsigned long flags; + char index_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + 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); + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_out_of_bounds); + +void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data, + unsigned long lhs, unsigned long rhs) +{ + unsigned long flags; + struct type_descriptor *rhs_type = data->rhs_type; + struct type_descriptor *lhs_type = data->lhs_type; + char rhs_str[VALUE_LENGTH]; + char lhs_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs); + 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); + + 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); + else if (val_is_negative(lhs_type, lhs)) + pr_err("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); + + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds); + + +void __noreturn +__ubsan_handle_builtin_unreachable(struct unreachable_data *data) +{ + unsigned long flags; + + ubsan_prologue(&data->location, &flags); + pr_err("calling __builtin_unreachable()\n"); + ubsan_epilogue(&flags); + panic("can't return from __builtin_unreachable()"); +} +EXPORT_SYMBOL(__ubsan_handle_builtin_unreachable); + +void __ubsan_handle_load_invalid_value(struct invalid_value_data *data, + unsigned long val) +{ + unsigned long flags; + char val_str[VALUE_LENGTH]; + + if (suppress_report(&data->location)) + return; + + ubsan_prologue(&data->location, &flags); + + 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); + + ubsan_epilogue(&flags); +} +EXPORT_SYMBOL(__ubsan_handle_load_invalid_value); diff --git a/lib/ubsan.h b/lib/ubsan.h new file mode 100644 index 000000000000..b2d18d4a53f5 --- /dev/null +++ b/lib/ubsan.h @@ -0,0 +1,84 @@ +#ifndef _LIB_UBSAN_H +#define _LIB_UBSAN_H + +enum { + type_kind_int = 0, + type_kind_float = 1, + type_unknown = 0xffff +}; + +struct type_descriptor { + u16 type_kind; + u16 type_info; + char type_name[1]; +}; + +struct source_location { + const char *file_name; + union { + unsigned long reported; + struct { + u32 line; + u32 column; + }; + }; +}; + +struct overflow_data { + struct source_location location; + struct type_descriptor *type; +}; + +struct type_mismatch_data { + struct source_location location; + struct type_descriptor *type; + unsigned long alignment; + unsigned char type_check_kind; +}; + +struct nonnull_arg_data { + struct source_location location; + struct source_location attr_location; + int arg_index; +}; + +struct nonnull_return_data { + struct source_location location; + struct source_location attr_location; +}; + +struct vla_bound_data { + struct source_location location; + struct type_descriptor *type; +}; + +struct out_of_bounds_data { + struct source_location location; + struct type_descriptor *array_type; + struct type_descriptor *index_type; +}; + +struct shift_out_of_bounds_data { + struct source_location location; + struct type_descriptor *lhs_type; + struct type_descriptor *rhs_type; +}; + +struct unreachable_data { + struct source_location location; +}; + +struct invalid_value_data { + struct source_location location; + struct type_descriptor *type; +}; + +#if defined(CONFIG_ARCH_SUPPORTS_INT128) && defined(__SIZEOF_INT128__) +typedef __int128 s_max; +typedef unsigned __int128 u_max; +#else +typedef s64 s_max; +typedef u64 u_max; +#endif + +#endif |