0ddb73d446
Changes in 5.10.166 clk: generalize devm_clk_get() a bit clk: Provide new devm_clk helpers for prepared and enabled clocks memory: atmel-sdramc: Fix missing clk_disable_unprepare in atmel_ramc_probe() memory: mvebu-devbus: Fix missing clk_disable_unprepare in mvebu_devbus_probe() ARM: dts: imx6ul-pico-dwarf: Use 'clock-frequency' ARM: dts: imx7d-pico: Use 'clock-frequency' ARM: dts: imx6qdl-gw560x: Remove incorrect 'uart-has-rtscts' arm64: dts: imx8mm-beacon: Fix ecspi2 pinmux ARM: imx: add missing of_node_put() HID: intel_ish-hid: Add check for ishtp_dma_tx_map EDAC/highbank: Fix memory leak in highbank_mc_probe() firmware: arm_scmi: Harden shared memory access in fetch_response firmware: arm_scmi: Harden shared memory access in fetch_notification tomoyo: fix broken dependency on *.conf.default RDMA/core: Fix ib block iterator counter overflow IB/hfi1: Reject a zero-length user expected buffer IB/hfi1: Reserve user expected TIDs IB/hfi1: Fix expected receive setup error exit issues IB/hfi1: Immediately remove invalid memory from hardware IB/hfi1: Remove user expected buffer invalidate race affs: initialize fsdata in affs_truncate() PM: AVS: qcom-cpr: Fix an error handling path in cpr_probe() phy: ti: fix Kconfig warning and operator precedence ARM: dts: at91: sam9x60: fix the ddr clock for sam9x60 amd-xgbe: TX Flow Ctrl Registers are h/w ver dependent amd-xgbe: Delay AN timeout during KR training bpf: Fix pointer-leak due to insufficient speculative store bypass mitigation phy: rockchip-inno-usb2: Fix missing clk_disable_unprepare() in rockchip_usb2phy_power_on() net: nfc: Fix use-after-free in local_cleanup() net: wan: Add checks for NULL for utdm in undo_uhdlc_init and unmap_si_regs gpio: mxc: Always set GPIOs used as interrupt source to INPUT mode wifi: rndis_wlan: Prevent buffer overflow in rndis_query_oid net/sched: sch_taprio: fix possible use-after-free l2tp: Serialize access to sk_user_data with sk_callback_lock l2tp: Don't sleep and disable BH under writer-side sk_callback_lock l2tp: convert l2tp_tunnel_list to idr l2tp: close all race conditions in l2tp_tunnel_register() net: usb: sr9700: Handle negative len net: mdio: validate parameter addr in mdiobus_get_phy() HID: check empty report_list in hid_validate_values() HID: check empty report_list in bigben_probe() net: stmmac: fix invalid call to mdiobus_get_phy() HID: revert CHERRY_MOUSE_000C quirk usb: gadget: f_fs: Prevent race during ffs_ep0_queue_wait usb: gadget: f_fs: Ensure ep0req is dequeued before free_request net: mlx5: eliminate anonymous module_init & module_exit drm/panfrost: fix GENERIC_ATOMIC64 dependency dmaengine: Fix double increment of client_count in dma_chan_get() net: macb: fix PTP TX timestamp failure due to packet padding l2tp: prevent lockdep issue in l2tp_tunnel_register() HID: betop: check shape of output reports dmaengine: xilinx_dma: call of_node_put() when breaking out of for_each_child_of_node() nvme-pci: fix timeout request state check tcp: avoid the lookup process failing to get sk in ehash table w1: fix deadloop in __w1_remove_master_device() w1: fix WARNING after calling w1_process() driver core: Fix test_async_probe_init saves device in wrong array net: dsa: microchip: ksz9477: port map correction in ALU table entry register tcp: fix rate_app_limited to default to 1 scsi: iscsi: Fix multiple iSCSI session unbind events sent to userspace cpufreq: Add Tegra234 to cpufreq-dt-platdev blocklist kcsan: test: don't put the expect array on the stack ASoC: fsl_micfil: Correct the number of steps on SX controls drm: Add orientation quirk for Lenovo ideapad D330-10IGL s390/debug: add _ASM_S390_ prefix to header guard cpufreq: armada-37xx: stop using 0 as NULL pointer ASoC: fsl_ssi: Rename AC'97 streams to avoid collisions with AC'97 CODEC ASoC: fsl-asoc-card: Fix naming of AC'97 CODEC widgets spi: spidev: remove debug messages that access spidev->spi without locking KVM: s390: interrupt: use READ_ONCE() before cmpxchg() scsi: hisi_sas: Set a port invalid only if there are no devices attached when refreshing port id platform/x86: touchscreen_dmi: Add info for the CSL Panther Tab HD platform/x86: asus-nb-wmi: Add alternate mapping for KEY_SCREENLOCK lockref: stop doing cpu_relax in the cmpxchg loop Revert "selftests/bpf: check null propagation only neither reg is PTR_TO_BTF_ID" netfilter: conntrack: do not renew entry stuck in tcp SYN_SENT state x86: ACPI: cstate: Optimize C3 entry on AMD CPUs fs: reiserfs: remove useless new_opts in reiserfs_remount sysctl: add a new register_sysctl_init() interface kernel/panic: move panic sysctls to its own file panic: unset panic_on_warn inside panic() ubsan: no need to unset panic_on_warn in ubsan_epilogue() kasan: no need to unset panic_on_warn in end_report() exit: Add and use make_task_dead. objtool: Add a missing comma to avoid string concatenation hexagon: Fix function name in die() h8300: Fix build errors from do_exit() to make_task_dead() transition csky: Fix function name in csky_alignment() and die() ia64: make IA64_MCA_RECOVERY bool instead of tristate panic: Separate sysctl logic from CONFIG_SMP exit: Put an upper limit on how often we can oops exit: Expose "oops_count" to sysfs exit: Allow oops_limit to be disabled panic: Consolidate open-coded panic_on_warn checks panic: Introduce warn_limit panic: Expose "warn_count" to sysfs docs: Fix path paste-o for /sys/kernel/warn_count exit: Use READ_ONCE() for all oops/warn limit reads Bluetooth: hci_sync: cancel cmd_timer if hci_open failed xhci: Set HCD flag to defer primary roothub registration scsi: hpsa: Fix allocation size for scsi_host_alloc() module: Don't wait for GOING modules tracing: Make sure trace_printk() can output as soon as it can be used trace_events_hist: add check for return value of 'create_hist_field' ftrace/scripts: Update the instructions for ftrace-bisect.sh cifs: Fix oops due to uncleared server->smbd_conn in reconnect KVM: x86/vmx: Do not skip segment attributes if unusable bit is set thermal: intel: int340x: Protect trip temperature from concurrent updates ARM: 9280/1: mm: fix warning on phys_addr_t to void pointer assignment EDAC/device: Respect any driver-supplied workqueue polling value EDAC/qcom: Do not pass llcc_driv_data as edac_device_ctl_info's pvt_info units: Add Watt units units: Add SI metric prefix definitions i2c: designware: Use DIV_ROUND_CLOSEST() macro i2c: designware: use casting of u64 in clock multiplication to avoid overflow netlink: prevent potential spectre v1 gadgets net: fix UaF in netns ops registration error path netfilter: nft_set_rbtree: Switch to node list walk for overlap detection netfilter: nft_set_rbtree: skip elements in transaction from garbage collection netlink: annotate data races around nlk->portid netlink: annotate data races around dst_portid and dst_group netlink: annotate data races around sk_state ipv4: prevent potential spectre v1 gadget in ip_metrics_convert() ipv4: prevent potential spectre v1 gadget in fib_metrics_match() netfilter: conntrack: fix vtag checks for ABORT/SHUTDOWN_COMPLETE netrom: Fix use-after-free of a listening socket. net/sched: sch_taprio: do not schedule in taprio_reset() sctp: fail if no bound addresses can be used for a given scope net: ravb: Fix possible hang if RIS2_QFF1 happen thermal: intel: int340x: Add locking to int340x_thermal_get_trip_type() net/tg3: resolve deadlock in tg3_reset_task() during EEH net: mdio-mux-meson-g12a: force internal PHY off on mux switch tools: gpio: fix -c option of gpio-event-mon Revert "Input: synaptics - switch touchpad on HP Laptop 15-da3001TU to RMI mode" nouveau: explicitly wait on the fence in nouveau_bo_move_m2mf nfsd: Ensure knfsd shuts down when the "nfsd" pseudofs is unmounted Revert "selftests/ftrace: Update synthetic event syntax errors" block: fix and cleanup bio_check_ro x86/i8259: Mark legacy PIC interrupts with IRQ_LEVEL netfilter: conntrack: unify established states for SCTP paths perf/x86/amd: fix potential integer overflow on shift of a int clk: Fix pointer casting to prevent oops in devm_clk_release() Linux 5.10.166 Change-Id: Ibf582f7504221c6ee1648da95c49b45e3678708c Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
384 lines
9.6 KiB
C
384 lines
9.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* UBSAN error reporting functions
|
|
*
|
|
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
|
|
* Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
|
|
*/
|
|
|
|
#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 <linux/uaccess.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 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, void *val)
|
|
{
|
|
if (is_inline_int(type)) {
|
|
unsigned extra_bits = sizeof(s_max)*8 - type_bit_width(type);
|
|
unsigned long ulong_val = (unsigned long)val;
|
|
|
|
return ((s_max)ulong_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, void *val)
|
|
{
|
|
return type_is_signed(type) && get_signed_val(type, val) < 0;
|
|
}
|
|
|
|
static u_max get_unsigned_val(struct type_descriptor *type, void *val)
|
|
{
|
|
if (is_inline_int(type))
|
|
return (unsigned long)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,
|
|
void *value)
|
|
{
|
|
if (type_is_int(type)) {
|
|
if (type_bit_width(type) == 128) {
|
|
#if defined(CONFIG_ARCH_SUPPORTS_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 void ubsan_prologue(struct source_location *loc, const char *reason)
|
|
{
|
|
current->in_ubsan++;
|
|
|
|
pr_err("========================================"
|
|
"========================================\n");
|
|
pr_err("UBSAN: %s in %s:%d:%d\n", reason, loc->file_name,
|
|
loc->line & LINE_MASK, loc->column & COLUMN_MASK);
|
|
}
|
|
|
|
static void ubsan_epilogue(void)
|
|
{
|
|
dump_stack();
|
|
pr_err("========================================"
|
|
"========================================\n");
|
|
|
|
current->in_ubsan--;
|
|
|
|
check_panic_on_warn("UBSAN");
|
|
}
|
|
|
|
void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs)
|
|
{
|
|
struct overflow_data *data = _data;
|
|
char rhs_val_str[VALUE_LENGTH];
|
|
|
|
if (suppress_report(&data->location))
|
|
return;
|
|
|
|
ubsan_prologue(&data->location, "division-overflow");
|
|
|
|
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();
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_divrem_overflow);
|
|
|
|
static void handle_null_ptr_deref(struct type_mismatch_data_common *data)
|
|
{
|
|
if (suppress_report(data->location))
|
|
return;
|
|
|
|
ubsan_prologue(data->location, "null-ptr-deref");
|
|
|
|
pr_err("%s null pointer of type %s\n",
|
|
type_check_kinds[data->type_check_kind],
|
|
data->type->type_name);
|
|
|
|
ubsan_epilogue();
|
|
}
|
|
|
|
static void handle_misaligned_access(struct type_mismatch_data_common *data,
|
|
unsigned long ptr)
|
|
{
|
|
if (suppress_report(data->location))
|
|
return;
|
|
|
|
ubsan_prologue(data->location, "misaligned-access");
|
|
|
|
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();
|
|
}
|
|
|
|
static void handle_object_size_mismatch(struct type_mismatch_data_common *data,
|
|
unsigned long ptr)
|
|
{
|
|
if (suppress_report(data->location))
|
|
return;
|
|
|
|
ubsan_prologue(data->location, "object-size-mismatch");
|
|
pr_err("%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);
|
|
ubsan_epilogue();
|
|
}
|
|
|
|
static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data,
|
|
unsigned long ptr)
|
|
{
|
|
unsigned long flags = user_access_save();
|
|
|
|
if (!ptr)
|
|
handle_null_ptr_deref(data);
|
|
else if (data->alignment && !IS_ALIGNED(ptr, data->alignment))
|
|
handle_misaligned_access(data, ptr);
|
|
else
|
|
handle_object_size_mismatch(data, ptr);
|
|
|
|
user_access_restore(flags);
|
|
}
|
|
|
|
void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
|
|
void *ptr)
|
|
{
|
|
struct type_mismatch_data_common common_data = {
|
|
.location = &data->location,
|
|
.type = data->type,
|
|
.alignment = data->alignment,
|
|
.type_check_kind = data->type_check_kind
|
|
};
|
|
|
|
ubsan_type_mismatch_common(&common_data, (unsigned long)ptr);
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_type_mismatch);
|
|
|
|
void __ubsan_handle_type_mismatch_v1(void *_data, void *ptr)
|
|
{
|
|
struct type_mismatch_data_v1 *data = _data;
|
|
struct type_mismatch_data_common common_data = {
|
|
.location = &data->location,
|
|
.type = data->type,
|
|
.alignment = 1UL << data->log_alignment,
|
|
.type_check_kind = data->type_check_kind
|
|
};
|
|
|
|
ubsan_type_mismatch_common(&common_data, (unsigned long)ptr);
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_type_mismatch_v1);
|
|
|
|
void __ubsan_handle_out_of_bounds(void *_data, void *index)
|
|
{
|
|
struct out_of_bounds_data *data = _data;
|
|
char index_str[VALUE_LENGTH];
|
|
|
|
if (suppress_report(&data->location))
|
|
return;
|
|
|
|
ubsan_prologue(&data->location, "array-index-out-of-bounds");
|
|
|
|
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();
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_out_of_bounds);
|
|
|
|
void __ubsan_handle_shift_out_of_bounds(void *_data, void *lhs, void *rhs)
|
|
{
|
|
struct shift_out_of_bounds_data *data = _data;
|
|
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];
|
|
unsigned long ua_flags = user_access_save();
|
|
|
|
if (suppress_report(&data->location))
|
|
goto out;
|
|
|
|
ubsan_prologue(&data->location, "shift-out-of-bounds");
|
|
|
|
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();
|
|
out:
|
|
user_access_restore(ua_flags);
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds);
|
|
|
|
|
|
void __ubsan_handle_builtin_unreachable(void *_data)
|
|
{
|
|
struct unreachable_data *data = _data;
|
|
ubsan_prologue(&data->location, "unreachable");
|
|
pr_err("calling __builtin_unreachable()\n");
|
|
ubsan_epilogue();
|
|
panic("can't return from __builtin_unreachable()");
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_builtin_unreachable);
|
|
|
|
void __ubsan_handle_load_invalid_value(void *_data, void *val)
|
|
{
|
|
struct invalid_value_data *data = _data;
|
|
char val_str[VALUE_LENGTH];
|
|
|
|
if (suppress_report(&data->location))
|
|
return;
|
|
|
|
ubsan_prologue(&data->location, "invalid-load");
|
|
|
|
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();
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_load_invalid_value);
|
|
|
|
void __ubsan_handle_alignment_assumption(void *_data, unsigned long ptr,
|
|
unsigned long align,
|
|
unsigned long offset);
|
|
void __ubsan_handle_alignment_assumption(void *_data, unsigned long ptr,
|
|
unsigned long align,
|
|
unsigned long offset)
|
|
{
|
|
struct alignment_assumption_data *data = _data;
|
|
unsigned long real_ptr;
|
|
|
|
if (suppress_report(&data->location))
|
|
return;
|
|
|
|
ubsan_prologue(&data->location, "alignment-assumption");
|
|
|
|
if (offset)
|
|
pr_err("assumption of %lu byte alignment (with offset of %lu byte) for pointer of type %s failed",
|
|
align, offset, data->type->type_name);
|
|
else
|
|
pr_err("assumption of %lu byte alignment for pointer of type %s failed",
|
|
align, data->type->type_name);
|
|
|
|
real_ptr = ptr - offset;
|
|
pr_err("%saddress is %lu aligned, misalignment offset is %lu bytes",
|
|
offset ? "offset " : "", BIT(real_ptr ? __ffs(real_ptr) : 0),
|
|
real_ptr & (align - 1));
|
|
|
|
ubsan_epilogue();
|
|
}
|
|
EXPORT_SYMBOL(__ubsan_handle_alignment_assumption);
|