diff options
57 files changed, 925 insertions, 357 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 1311a48a7367..951b7eedc44e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2884,6 +2884,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. spia_pedr= spia_peddr= + stack_guard_gap= [MM] + override the default stack gap protection. The value + is in page units and it defines how many pages prior + to (for stacks growing down) resp. after (for stacks + growing up) the main stack are reserved for no other + mapping. Default value is 256 pages. + stacktrace [FTRACE] Enabled the stack tracer on boot up. diff --git a/Documentation/speculation.txt b/Documentation/speculation.txt new file mode 100644 index 000000000000..e9e6cbae2841 --- /dev/null +++ b/Documentation/speculation.txt @@ -0,0 +1,90 @@ +This document explains potential effects of speculation, and how undesirable +effects can be mitigated portably using common APIs. + +=========== +Speculation +=========== + +To improve performance and minimize average latencies, many contemporary CPUs +employ speculative execution techniques such as branch prediction, performing +work which may be discarded at a later stage. + +Typically speculative execution cannot be observed from architectural state, +such as the contents of registers. However, in some cases it is possible to +observe its impact on microarchitectural state, such as the presence or +absence of data in caches. Such state may form side-channels which can be +observed to extract secret information. + +For example, in the presence of branch prediction, it is possible for bounds +checks to be ignored by code which is speculatively executed. Consider the +following code: + + int load_array(int *array, unsigned int index) + { + if (index >= MAX_ARRAY_ELEMS) + return 0; + else + return array[index]; + } + +Which, on arm64, may be compiled to an assembly sequence such as: + + CMP <index>, #MAX_ARRAY_ELEMS + B.LT less + MOV <returnval>, #0 + RET + less: + LDR <returnval>, [<array>, <index>] + RET + +It is possible that a CPU mis-predicts the conditional branch, and +speculatively loads array[index], even if index >= MAX_ARRAY_ELEMS. This +value will subsequently be discarded, but the speculated load may affect +microarchitectural state which can be subsequently measured. + +More complex sequences involving multiple dependent memory accesses may +result in sensitive information being leaked. Consider the following +code, building on the prior example: + + int load_dependent_arrays(int *arr1, int *arr2, int index) + { + int val1, val2, + + val1 = load_array(arr1, index); + val2 = load_array(arr2, val1); + + return val2; + } + +Under speculation, the first call to load_array() may return the value +of an out-of-bounds address, while the second call will influence +microarchitectural state dependent on this value. This may provide an +arbitrary read primitive. + +==================================== +Mitigating speculation side-channels +==================================== + +The kernel provides a generic API to ensure that bounds checks are +respected even under speculation. Architectures which are affected by +speculation-based side-channels are expected to implement these +primitives. + +The array_index_nospec() helper in <linux/nospec.h> can be used to +prevent information from being leaked via side-channels. + +A call to array_index_nospec(index, size) returns a sanitized index +value that is bounded to [0, size) even under cpu speculation +conditions. + +This can be used to protect the earlier load_array() example: + + int load_array(int *array, unsigned int index) + { + if (index >= MAX_ARRAY_ELEMS) + return 0; + else { + index = array_index_nospec(index, MAX_ARRAY_ELEMS); + return array[index]; + } + } diff --git a/arch/arm/configs/tegra12_defconfig b/arch/arm/configs/tegra12_defconfig index 5e16975801fe..a0cb43fa35b6 100644 --- a/arch/arm/configs/tegra12_defconfig +++ b/arch/arm/configs/tegra12_defconfig @@ -195,7 +195,6 @@ CONFIG_AD525X_DPOT=y CONFIG_AD525X_DPOT_I2C=y CONFIG_APDS9802ALS=y CONFIG_SENSORS_NCT1008=y -CONFIG_TEGRA_CRYPTO_DEV=y CONFIG_THERM_EST=y CONFIG_FAN_THERM_EST=y CONFIG_EEPROM_AT24=y diff --git a/arch/arm/include/asm/barrier.h b/arch/arm/include/asm/barrier.h index b00ef075bc2e..6af46bfa9837 100644 --- a/arch/arm/include/asm/barrier.h +++ b/arch/arm/include/asm/barrier.h @@ -79,5 +79,9 @@ do { \ #define set_mb(var, value) do { var = value; smp_mb(); } while (0) +#define speculation_barrier() \ + asm volatile( "dsb sy\n" \ + "isb\n" : : : "memory") + #endif /* !__ASSEMBLY__ */ #endif /* __ASM_BARRIER_H */ diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h index 1f3262e99d81..d820fc5f043a 100644 --- a/arch/arm/include/asm/cp15.h +++ b/arch/arm/include/asm/cp15.h @@ -42,8 +42,32 @@ #define vectors_high() (0) #endif +#define __ACCESS_CP15(CRn, Op1, CRm, Op2) \ + "mrc", "mcr", __stringify(p15, Op1, %0, CRn, CRm, Op2), u32 +#define __ACCESS_CP15_64(Op1, CRm) \ + "mrrc", "mcrr", __stringify(p15, Op1, %Q0, %R0, CRm), u64 + +#define __read_sysreg(r, w, c, t) ({ \ + t __val; \ + asm volatile(r " " c : "=r" (__val)); \ + __val; \ +}) +#define read_sysreg(...) __read_sysreg(__VA_ARGS__) + +#define __write_sysreg(v, r, w, c, t) asm volatile(w " " c : : "r" ((t)(v))) +#define write_sysreg(v, ...) __write_sysreg(v, __VA_ARGS__) + #ifdef CONFIG_CPU_CP15 +#define __ACCESS_CP15(CRn, Op1, CRm, Op2) \ + "mrc", "mcr", __stringify(p15, Op1, %0, CRn, CRm, Op2), u32 +#define __ACCESS_CP15_64(Op1, CRm) \ + "mrrc", "mcrr", __stringify(p15, Op1, %Q0, %R0, CRm), u64 + + +#define BPIALL __ACCESS_CP15(c7, 0, c5, 6) +#define ICIALLU __ACCESS_CP15(c7, 0, c5, 0) + extern unsigned long cr_no_alignment; /* defined in entry-armv.S */ extern unsigned long cr_alignment; /* defined in entry-armv.S */ diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 2c7e308f8cab..07c3309b6288 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -947,3 +947,20 @@ config ARM_SAVE_DEBUG_CONTEXT_NO_LOCK the ARM debug registers across CPU powerdown. This option should not be selected unless you are actively debugging the context save/restore code. If unsure, say N. + +config HARDEN_BRANCH_PREDICTOR + bool "Harden the branch predictor against aliasing attacks" if EXPERT + default y + help + Speculation attacks against some high-performance processors rely on + being able to manipulate the branch predictor for a victim context by + executing aliasing branches in the attacker context. Such attacks + can be partially mitigated against by clearing internal branch + predictor state and limiting the prediction logic in some situations. + + This config option will take CPU-specific actions to harden the + branch predictor against aliasing attacks and may rely on specific + instruction sequences or control bits being set by the system + firmware. + + If unsure, say Y. diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 9820ad4b80c0..e8f42821a206 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -20,11 +20,13 @@ #include <linux/highmem.h> #include <linux/perf_event.h> +#include <asm/cp15.h> #include <asm/exception.h> #include <asm/pgtable.h> #include <asm/system_misc.h> #include <asm/system_info.h> #include <asm/tlbflush.h> +#include <asm/cputype.h> #include "fault.h" @@ -393,12 +395,33 @@ no_context: __do_kernel_fault(mm, addr, fsr, regs); return 0; } + +static int __maybe_unused +do_pabt_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + if (addr > TASK_SIZE) { + switch (read_cpuid_part_number()) { + case ARM_CPU_PART_CORTEX_A15: + write_sysreg(0, ICIALLU); + break; + } + } +#endif + return do_page_fault(addr, fsr, regs); +} #else /* CONFIG_MMU */ static int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { return 0; } + +static int +do_pabt_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ + return 0; +} #endif /* CONFIG_MMU */ /* diff --git a/arch/arm/mm/fsr-2level.c b/arch/arm/mm/fsr-2level.c index 18ca74c0f341..4cede9bc7722 100644 --- a/arch/arm/mm/fsr-2level.c +++ b/arch/arm/mm/fsr-2level.c @@ -50,7 +50,7 @@ static struct fsr_info ifsr_info[] = { { do_bad, SIGBUS, 0, "unknown 4" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "section translation fault" }, { do_bad, SIGSEGV, SEGV_ACCERR, "page access flag fault" }, - { do_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_MAPERR, "page translation fault" }, { do_bad, SIGBUS, 0, "external abort on non-linefetch" }, { do_bad, SIGSEGV, SEGV_ACCERR, "section domain fault" }, { do_bad, SIGBUS, 0, "unknown 10" }, @@ -58,7 +58,7 @@ static struct fsr_info ifsr_info[] = { { do_bad, SIGBUS, 0, "external abort on translation" }, { do_sect_fault, SIGSEGV, SEGV_ACCERR, "section permission fault" }, { do_bad, SIGBUS, 0, "external abort on translation" }, - { do_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_ACCERR, "page permission fault" }, { do_bad, SIGBUS, 0, "unknown 16" }, { do_bad, SIGBUS, 0, "unknown 17" }, { do_bad, SIGBUS, 0, "unknown 18" }, diff --git a/arch/arm/mm/fsr-3level.c b/arch/arm/mm/fsr-3level.c index 47f4c6fb25ba..791ef0c66741 100644 --- a/arch/arm/mm/fsr-3level.c +++ b/arch/arm/mm/fsr-3level.c @@ -65,4 +65,73 @@ static struct fsr_info fsr_info[] = { { do_bad, SIGBUS, 0, "unknown 63" }, }; +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +static struct fsr_info ifsr_info[] = { + { do_bad, SIGBUS, 0, "unknown 0" }, + { do_bad, SIGBUS, 0, "unknown 1" }, + { do_bad, SIGBUS, 0, "unknown 2" }, + { do_bad, SIGBUS, 0, "unknown 3" }, + { do_bad, SIGBUS, 0, "reserved translation fault" }, + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" }, + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, + { do_bad, SIGBUS, 0, "reserved access flag fault" }, + { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, + { do_bad, SIGBUS, 0, "reserved permission fault" }, + { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, + { do_pabt_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, + { do_bad, SIGBUS, 0, "synchronous external abort" }, + { do_bad, SIGBUS, 0, "asynchronous external abort" }, + { do_bad, SIGBUS, 0, "unknown 18" }, + { do_bad, SIGBUS, 0, "unknown 19" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" }, + { do_bad, SIGBUS, 0, "synchronous parity error" }, + { do_bad, SIGBUS, 0, "asynchronous parity error" }, + { do_bad, SIGBUS, 0, "unknown 26" }, + { do_bad, SIGBUS, 0, "unknown 27" }, + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, + { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" }, + { do_bad, SIGBUS, 0, "unknown 32" }, + { do_bad, SIGBUS, BUS_ADRALN, "alignment fault" }, + { do_bad, SIGBUS, 0, "debug event" }, + { do_bad, SIGBUS, 0, "unknown 35" }, + { do_bad, SIGBUS, 0, "unknown 36" }, + { do_bad, SIGBUS, 0, "unknown 37" }, + { do_bad, SIGBUS, 0, "unknown 38" }, + { do_bad, SIGBUS, 0, "unknown 39" }, + { do_bad, SIGBUS, 0, "unknown 40" }, + { do_bad, SIGBUS, 0, "unknown 41" }, + { do_bad, SIGBUS, 0, "unknown 42" }, + { do_bad, SIGBUS, 0, "unknown 43" }, + { do_bad, SIGBUS, 0, "unknown 44" }, + { do_bad, SIGBUS, 0, "unknown 45" }, + { do_bad, SIGBUS, 0, "unknown 46" }, + { do_bad, SIGBUS, 0, "unknown 47" }, + { do_bad, SIGBUS, 0, "unknown 48" }, + { do_bad, SIGBUS, 0, "unknown 49" }, + { do_bad, SIGBUS, 0, "unknown 50" }, + { do_bad, SIGBUS, 0, "unknown 51" }, + { do_bad, SIGBUS, 0, "implementation fault (lockdown abort)" }, + { do_bad, SIGBUS, 0, "unknown 53" }, + { do_bad, SIGBUS, 0, "unknown 54" }, + { do_bad, SIGBUS, 0, "unknown 55" }, + { do_bad, SIGBUS, 0, "unknown 56" }, + { do_bad, SIGBUS, 0, "unknown 57" }, + { do_bad, SIGBUS, 0, "implementation fault (coprocessor abort)" }, + { do_bad, SIGBUS, 0, "unknown 59" }, + { do_bad, SIGBUS, 0, "unknown 60" }, + { do_bad, SIGBUS, 0, "unknown 61" }, + { do_bad, SIGBUS, 0, "unknown 62" }, + { do_bad, SIGBUS, 0, "unknown 63" }, +}; +#else #define ifsr_info fsr_info ++#endif diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c index a5b1934e711b..86cda2e8ec19 100644 --- a/arch/arm/mm/mmap.c +++ b/arch/arm/mm/mmap.c @@ -90,7 +90,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && - (!vma || addr + len <= vma->vm_start)) + (!vma || addr + len <= vm_start_gap(vma))) return addr; } @@ -155,7 +155,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, addr = PAGE_ALIGN(addr); vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && - (!vma || addr + len <= vma->vm_start)) + (!vma || addr + len <= vm_start_gap(vma))) return addr; } diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S index a0cf0dc9f0d7..48db62324291 100644 --- a/arch/arm/mm/proc-v7-2level.S +++ b/arch/arm/mm/proc-v7-2level.S @@ -36,7 +36,16 @@ * * It is assumed that: * - we are not using split page tables - */ + * + * Cortex-A15 requires ACTLR[0] to be set from secure in order + * for the icache invalidation to also invalidate the BTB. + */ +ENTRY(cpu_v7_icinv_switch_mm) +#ifdef CONFIG_MMU + mcr p15, 0, r0, c7, c5, 0 @ ICIALLU + /* Fall through to switch_mm... */ +#endif + ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_MMU mov r2, #0 @@ -61,6 +70,7 @@ ENTRY(cpu_v7_switch_mm) #endif mov pc, lr ENDPROC(cpu_v7_switch_mm) +ENDPROC(cpu_v7_icinv_switch_mm) /* * cpu_v7_set_pte_ext(ptep, pte) diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S index e377cc4031b0..e2bb1ae55047 100644 --- a/arch/arm/mm/proc-v7-3level.S +++ b/arch/arm/mm/proc-v7-3level.S @@ -53,6 +53,19 @@ * Set the translation table base pointer to be pgd_phys (physical address of * the new TTB). */ +ENTRY(cpu_v7_icinv_switch_mm) +#ifdef CONFIG_MMU + /* + * Cortex-A15 requires ACTLR[0] to be set from secure in order + * for the icache invalidation to also invalidate the BTB. + */ + mcr p15, 0, r0, c7, c5, 0 @ ICIALLU + /* Fall through to switch_mm... */ +#endif +ENTRY(cpu_v7_btbinv_switch_mm) +#ifdef CONFIG_MMU + mcr p15, 0, r0, c7, c5, 6 @ flush BTAC/BTB +#endif ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_MMU mmid r2, r2 @ get mm->context.id @@ -64,6 +77,8 @@ ENTRY(cpu_v7_switch_mm) #endif mov pc, lr ENDPROC(cpu_v7_switch_mm) +ENDPROC(cpu_v7_btbinv_switch_mm) +ENDPROC(cpu_v7_icinv_switch_mm) /* * cpu_v7_set_pte_ext(ptep, pte) diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index d1dea91517e0..322ecf52c2a7 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -415,6 +415,46 @@ start_restore_wpt: ENDPROC(cpu_v7_do_resume) #endif +/* + * Cortex-A15 + */ + globl_equ cpu_ca15_proc_init, cpu_v7_proc_init + globl_equ cpu_ca15_proc_fin, cpu_v7_proc_fin + globl_equ cpu_ca15_reset, cpu_v7_reset + globl_equ cpu_ca15_do_idle, cpu_v7_do_idle + globl_equ cpu_ca15_dcache_clean_area, cpu_v7_dcache_clean_area + globl_equ cpu_ca15_set_pte_ext, cpu_v7_set_pte_ext + globl_equ cpu_ca15_suspend_size, cpu_v7_suspend_size +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + globl_equ cpu_ca15_switch_mm, cpu_v7_icinv_switch_mm +#else + globl_equ cpu_ca15_switch_mm, cpu_v7_switch_mm +#endif +#ifdef CONFIG_ARM_CPU_SUSPEND + globl_equ cpu_ca15_do_suspend, cpu_v7_do_suspend + globl_equ cpu_ca15_do_resume, cpu_v7_do_resume +#endif + +/* + * Cortex-A12/A17 + */ + globl_equ cpu_ca17_proc_init, cpu_v7_proc_init + globl_equ cpu_ca17_proc_fin, cpu_v7_proc_fin + globl_equ cpu_ca17_reset, cpu_v7_reset + globl_equ cpu_ca17_do_idle, cpu_v7_do_idle + globl_equ cpu_ca17_dcache_clean_area, cpu_v7_dcache_clean_area + globl_equ cpu_ca17_set_pte_ext, cpu_v7_set_pte_ext + globl_equ cpu_ca17_suspend_size, cpu_v7_suspend_size +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + globl_equ cpu_ca17_switch_mm, cpu_v7_btbinv_switch_mm +#else + globl_equ cpu_ca17_switch_mm, cpu_v7_switch_mm +#endif +#ifdef CONFIG_ARM_CPU_SUSPEND + globl_equ cpu_ca17_do_suspend, cpu_v7_do_suspend + globl_equ cpu_ca17_do_resume, cpu_v7_do_resume +#endif + #ifdef CONFIG_CPU_PJ4B globl_equ cpu_pj4b_switch_mm, cpu_v7_switch_mm globl_equ cpu_pj4b_set_pte_ext, cpu_v7_set_pte_ext @@ -722,6 +762,7 @@ __v7_setup_stack: @ define struct processor (see <asm/proc-fns.h> and proc-macros.S) define_processor_functions v7, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 + define_processor_functions ca15, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 #ifdef CONFIG_CPU_PJ4B define_processor_functions pj4b, dabort=v7_early_abort, pabort=v7_pabort, suspend=1 #endif @@ -818,7 +859,7 @@ __v7_ca15mp_r3_proc_info: __v7_ca15mp_proc_info: .long 0x410fc0f0 .long 0xff0ffff0 - __v7_proc __v7_ca15mp_setup + __v7_proc __v7_ca15mp_setup, proc_fns = ca15_processor_functions .size __v7_ca15mp_proc_info, . - __v7_ca15mp_proc_info /* diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h index 9269edba2c90..8059ead5f2d6 100644 --- a/arch/arm64/include/asm/barrier.h +++ b/arch/arm64/include/asm/barrier.h @@ -99,6 +99,10 @@ do { \ #define set_mb(var, value) do { var = value; smp_mb(); } while (0) #define nop() asm volatile("nop"); +#define speculation_barrier() \ + asm volatile( "dsb sy\n" \ + "isb\n" : : : "memory") + #endif /* __ASSEMBLY__ */ #endif /* __ASM_BARRIER_H */ diff --git a/block/genhd.c b/block/genhd.c index 6f612a747810..50d8e7ac4d69 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -828,6 +828,7 @@ static void disk_seqf_stop(struct seq_file *seqf, void *v) if (iter) { class_dev_iter_exit(iter); kfree(iter); + seqf->private = NULL; } } diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c index 3065e8403559..0a48f6a551ae 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.c @@ -3,7 +3,7 @@ * * GK20A Graphics channel * - * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2015, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -669,7 +669,7 @@ void gk20a_free_channel(struct channel_gk20a *ch, bool finish) memset(&ch->ramfc, 0, sizeof(struct mem_desc_sub)); /* free gpfifo */ - if (ch->gpfifo.gpu_va) + if (ch->vm && ch->gpfifo.gpu_va) gk20a_gmmu_unmap(ch_vm, ch->gpfifo.gpu_va, ch->gpfifo.size, gk20a_mem_flag_none); if (ch->gpfifo.cpu_va) @@ -698,8 +698,9 @@ unbind: channel_gk20a_unbind(ch); channel_gk20a_free_inst(g, ch); - ch->vpr = false; + gk20a_vm_put(ch->vm); /* Don't use VM after this. */ ch->vm = NULL; + ch->vpr = false; WARN_ON(ch->sync); /* unlink all debug sessions */ @@ -2066,6 +2067,18 @@ long gk20a_channel_ioctl(struct file *filp, (struct nvhost_alloc_obj_ctx_args *)buf); gk20a_idle(dev); break; + case NVHOST_IOCTL_CHANNEL_FREE_OBJ_CTX: + err = gk20a_busy(dev); + if (err) { + dev_err(&dev->dev, + "%s: failed to host gk20a for ioctl cmd: 0x%x", + __func__, cmd); + return err; + } + err = gk20a_free_obj_ctx(ch, + (struct nvhost_free_obj_ctx_args *)buf); + gk20a_idle(dev); + break; case NVHOST_IOCTL_CHANNEL_ALLOC_GPFIFO: err = gk20a_busy(dev); if (err) { diff --git a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h index 831db0f4986a..547bb064fd63 100644 --- a/drivers/gpu/nvgpu/gk20a/channel_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/channel_gk20a.h @@ -3,7 +3,7 @@ * * GK20A graphics channel * - * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -98,6 +98,7 @@ struct channel_gk20a { u64 userd_iova; u64 userd_gpu_va; + s32 num_objects; u32 obj_class; /* we support only one obj per channel */ struct priv_cmd_queue priv_cmd_q; diff --git a/drivers/gpu/nvgpu/gk20a/gr_gk20a.c b/drivers/gpu/nvgpu/gk20a/gr_gk20a.c index d5a3bbd34a78..db34cc0e85e9 100644 --- a/drivers/gpu/nvgpu/gk20a/gr_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/gr_gk20a.c @@ -1,7 +1,7 @@ /* * GK20A Graphics * - * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2015, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -26,6 +26,7 @@ #include <linux/dma-mapping.h> #include <linux/firmware.h> #include <linux/nvhost.h> +#include <asm/barrier.h> #include "gk20a.h" #include "kind_gk20a.h" @@ -2697,6 +2698,7 @@ void gk20a_free_channel_ctx(struct channel_gk20a *c) memset(&c->ch_ctx, 0, sizeof(struct channel_ctx_gk20a)); + c->num_objects = 0; c->first_init = false; } @@ -2847,6 +2849,8 @@ int gk20a_alloc_obj_ctx(struct channel_gk20a *c, c->first_init = true; } + c->num_objects++; + gk20a_dbg_fn("done"); return 0; out: @@ -2858,6 +2862,29 @@ out: return err; } +int gk20a_free_obj_ctx(struct channel_gk20a *c, + struct nvhost_free_obj_ctx_args *args) +{ + unsigned long timeout = gk20a_get_gr_idle_timeout(c->g); + + gk20a_dbg_fn(""); + + if (c->num_objects == 0) + return 0; + + c->num_objects--; + + if (c->num_objects == 0) { + c->first_init = false; + gk20a_disable_channel(c, + !c->has_timedout, + timeout); + gr_gk20a_unmap_channel_patch_ctx(c); + } + + return 0; +} + static void gk20a_remove_gr_support(struct gr_gk20a *gr) { struct gk20a *g = gr->g; @@ -3568,6 +3595,7 @@ int gr_gk20a_add_zbc(struct gk20a *g, struct gr_gk20a *gr, mutex_lock(&gr->zbc_lock); switch (zbc_val->type) { case GK20A_ZBC_TYPE_COLOR: + speculation_barrier(); /* search existing tables */ for (i = 0; i < gr->max_used_color_index; i++) { @@ -3606,6 +3634,7 @@ int gr_gk20a_add_zbc(struct gk20a *g, struct gr_gk20a *gr, } break; case GK20A_ZBC_TYPE_DEPTH: + speculation_barrier(); /* search existing tables */ for (i = 0; i < gr->max_used_depth_index; i++) { diff --git a/drivers/gpu/nvgpu/gk20a/gr_gk20a.h b/drivers/gpu/nvgpu/gk20a/gr_gk20a.h index 526eefb46b6f..2a31aa0b830f 100644 --- a/drivers/gpu/nvgpu/gk20a/gr_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/gr_gk20a.h @@ -1,7 +1,7 @@ /* * GK20A Graphics Engine * - * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -324,9 +324,12 @@ int gk20a_init_gr_channel(struct channel_gk20a *ch_gk20a); int gr_gk20a_init_ctx_vars(struct gk20a *g, struct gr_gk20a *gr); struct nvhost_alloc_obj_ctx_args; +struct nvhost_free_obj_ctx_args; int gk20a_alloc_obj_ctx(struct channel_gk20a *c, struct nvhost_alloc_obj_ctx_args *args); +int gk20a_free_obj_ctx(struct channel_gk20a *c, + struct nvhost_free_obj_ctx_args *args); void gk20a_free_channel_ctx(struct channel_gk20a *c); int gk20a_gr_isr(struct gk20a *g); diff --git a/drivers/gpu/nvgpu/gk20a/mm_gk20a.c b/drivers/gpu/nvgpu/gk20a/mm_gk20a.c index 2bbd973ad0b1..c5a5791f9489 100644 --- a/drivers/gpu/nvgpu/gk20a/mm_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/mm_gk20a.c @@ -1329,6 +1329,12 @@ u64 gk20a_vm_map(struct vm_gk20a *vm, bfr.pgsz_idx = -1; mapping_size = mapping_size ? mapping_size : bfr.size; + if ((mapping_size > bfr.size) || + (buffer_offset > (bfr.size - mapping_size))) { + err = -EINVAL; + goto clean_up; + } + /* If FIX_OFFSET is set, pgsz is determined. Otherwise, select * page size according to memory alignment */ if (flags & NVHOST_AS_MAP_BUFFER_FLAGS_FIXED_OFFSET) { @@ -2420,6 +2426,7 @@ int gk20a_vm_bind_channel(struct gk20a_as_share *as_share, gk20a_dbg_fn(""); + gk20a_vm_get(vm); ch->vm = vm; err = channel_gk20a_commit_va(ch); if (err) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 46b7b12376f9..dcea21ed9cd6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1138,6 +1138,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, /* Ignore report if ErrorRollOver */ if (!(field->flags & HID_MAIN_ITEM_VARIABLE) && value[n] >= min && value[n] <= max && + value[n] - min < field->maxusage && field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) goto exit; } @@ -1150,11 +1151,13 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, } if (field->value[n] >= min && field->value[n] <= max + && field->value[n] - min < field->maxusage && field->usage[field->value[n] - min].hid && search(value, field->value[n], count)) hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt); if (value[n] >= min && value[n] <= max + && value[n] - min < field->maxusage && field->usage[value[n] - min].hid && search(field->value, value[n], count)) hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt); diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 58344b6c3a55..436b9fd4775e 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -36,6 +36,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/ad9389b.h> +#include <asm/barrier.h> static int debug; module_param(debug, int, 0644); @@ -627,6 +628,9 @@ static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi } if (edid->start_block >= state->edid.segments * 2) return -E2BIG; + + speculation_barrier(); + if (edid->blocks + edid->start_block >= state->edid.segments * 2) edid->blocks = state->edid.segments * 2 - edid->start_block; memcpy(edid->edid, &state->edid.data[edid->start_block * 128], diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 31a63c9324fe..84202010d7d8 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -40,6 +40,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-chip-ident.h> #include <media/adv7604.h> +#include <asm/barrier.h> static int debug; module_param(debug, int, 0644); @@ -1593,6 +1594,9 @@ static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edi return -EINVAL; if (edid->start_block >= state->edid_blocks) return -EINVAL; + + speculation_barrier(); + if (edid->start_block + edid->blocks > state->edid_blocks) edid->blocks = state->edid_blocks - edid->start_block; if (!edid->edid) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 617ad3fff4aa..7124145a210b 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -21,6 +21,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-mediabus.h> #include <media/ov7670.h> +#include <asm/barrier.h> MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); @@ -1087,6 +1088,9 @@ static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, { if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) return -EINVAL; + + speculation_barrier(); + interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; interval->discrete.numerator = 1; interval->discrete.denominator = ov7670_frame_rates[interval->index]; diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 1dbb8118a285..47902efae8d4 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -30,6 +30,7 @@ #include <media/v4l2-subdev.h> #include <media/v4l2-mediabus.h> #include <media/ov9650.h> +#include <asm/barrier.h> static int debug; module_param(debug, int, 0644); @@ -1086,6 +1087,8 @@ static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, if (fse->index > ARRAY_SIZE(ov965x_framesizes)) return -EINVAL; + speculation_barrier(); + while (--i) if (fse->code == ov965x_formats[i].code) break; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 9eac5310942f..a7078441e1e1 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -33,6 +33,7 @@ #include <media/v4l2-subdev.h> #include <media/v4l2-mediabus.h> #include <media/s5c73m3.h> +#include <asm/barrier.h> #include "s5c73m3.h" @@ -959,6 +960,8 @@ static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd, if (fie->index >= ARRAY_SIZE(s5c73m3_intervals)) return -EINVAL; + speculation_barrier(); + mutex_lock(&state->lock); fi = &s5c73m3_intervals[fie->index]; if (fie->width > fi->size.width || fie->height > fi->size.height) @@ -1228,6 +1231,8 @@ static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= s5c73m3_resolutions_len[idx]) return -EINVAL; + speculation_barrier(); + fse->min_width = s5c73m3_resolutions[idx][fse->index].width; fse->max_width = fse->min_width; fse->max_height = s5c73m3_resolutions[idx][fse->index].height; @@ -1272,6 +1277,8 @@ static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= s5c73m3_resolutions_len[idx]) return -EINVAL; + speculation_barrier(); + fse->min_width = s5c73m3_resolutions[idx][fse->index].width; fse->max_width = fse->min_width; fse->max_height = s5c73m3_resolutions[idx][fse->index].height; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index bdf5e3db31d1..aff91c3bcfde 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -29,6 +29,7 @@ #include <media/v4l2-subdev.h> #include <media/v4l2-mediabus.h> #include <media/s5k6aa.h> +#include <asm/barrier.h> static int debug; module_param(debug, int, 0644); @@ -1006,6 +1007,8 @@ static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) return -EINVAL; + speculation_barrier(); + v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, S5K6AA_WIN_WIDTH_MAX, 1, &fie->height, S5K6AA_WIN_HEIGHT_MIN, diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 9771cd83c06e..38afc54ef349 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -1385,11 +1385,12 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) * in order to avoid troubles during device release. */ kfree(priv->ctrl.fname); + priv->ctrl.fname = NULL; memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); if (p->fname) { priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); if (priv->ctrl.fname == NULL) - rc = -ENOMEM; + return -ENOMEM; } /* diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 6f52e699178b..99b3162ab5b4 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -28,6 +28,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/videobuf2-core.h> +#include <asm/barrier.h> /* Zero out the end of the struct pointed to by p. Everything after, but * not including, the specified field is cleared. */ @@ -2121,6 +2122,7 @@ bool v4l2_is_known_ioctl(unsigned int cmd) { if (_IOC_NR(cmd) >= V4L2_IOCTLS) return false; + speculation_barrier(); return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; } @@ -2130,6 +2132,7 @@ struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd) return vdev->lock; if (test_bit(_IOC_NR(cmd), vdev->disable_locking)) return NULL; + speculation_barrier(); if (vdev->queue && vdev->queue->lock && (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) return vdev->queue->lock; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index e3bdc3be91e1..60ba606afc56 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -23,6 +23,7 @@ #include <media/v4l2-fh.h> #include <media/v4l2-event.h> #include <media/videobuf2-core.h> +#include <asm/barrier.h> static int debug; module_param(debug, int, 0644); @@ -1800,6 +1801,8 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb) return -EINVAL; } + speculation_barrier(); + vb = q->bufs[eb->index]; if (eb->plane >= vb->num_planes) { diff --git a/drivers/misc/tegra-cryptodev.c b/drivers/misc/tegra-cryptodev.c index 88c9cb217880..2c0d3918100d 100644 --- a/drivers/misc/tegra-cryptodev.c +++ b/drivers/misc/tegra-cryptodev.c @@ -33,6 +33,7 @@ #include <linux/tegra-soc.h> #include <crypto/rng.h> #include <crypto/hash.h> +#include <asm/barrier.h> #include "tegra-cryptodev.h" @@ -282,6 +283,8 @@ static int process_crypt_req(struct tegra_crypto_ctx *ctx, struct tegra_crypt_re const u8 *key = NULL; struct tegra_crypto_completion tcrypt_complete; + speculation_barrier(); + if (crypt_req->op & TEGRA_CRYPTO_ECB) { req = ablkcipher_request_alloc(ctx->ecb_tfm, GFP_KERNEL); tfm = ctx->ecb_tfm; @@ -425,6 +428,33 @@ static int tegra_crypt_rsa(struct tegra_crypto_ctx *ctx, int ret = 0; unsigned long *xbuf[XBUFSIZE]; struct tegra_crypto_completion rsa_complete; + unsigned int total_key_len; + char *key_mem; + + if ((((rsa_req->keylen >> 16) & 0xFFFF) > + MAX_RSA_MSG_LEN) || + ((rsa_req->keylen & 0xFFFF) > + MAX_RSA_MSG_LEN)) { + pr_err("Invalid rsa key length\n"); + return -EINVAL; + } + + total_key_len = (((rsa_req->keylen >> 16) & 0xFFFF) + + (rsa_req->keylen & 0xFFFF)); + + key_mem = kzalloc(total_key_len, GFP_KERNEL); + if (!key_mem) + return -ENOMEM; + + ret = copy_from_user(key_mem, (void __user *)rsa_req->key, + total_key_len); + if (ret) { + pr_err("%s: copy_from_user fail(%d)\n", __func__, ret); + kfree(key_mem); + return -EINVAL; + } + + rsa_req->key = key_mem; switch (rsa_req->algo) { case TEGRA_RSA512: @@ -475,10 +505,8 @@ static int tegra_crypt_rsa(struct tegra_crypto_ctx *ctx, init_completion(&rsa_complete.restart); result = kzalloc(rsa_req->keylen >> 16, GFP_KERNEL); - if (!result) { - pr_err("\nresult alloc fail\n"); + if (!result) goto result_fail; - } hash_buff = xbuf[0]; @@ -528,6 +556,7 @@ result_fail: buf_fail: ahash_request_free(req); req_fail: + kfree(key_mem); return ret; } @@ -537,6 +566,7 @@ static int tegra_crypto_sha(struct tegra_sha_req *sha_req) struct crypto_ahash *tfm; struct scatterlist sg[1]; char result[64]; + char algo[64]; struct ahash_request *req; struct tegra_crypto_completion sha_complete; void *hash_buff; @@ -548,17 +578,23 @@ static int tegra_crypto_sha(struct tegra_sha_req *sha_req) return -EINVAL; } - tfm = crypto_alloc_ahash(sha_req->algo, 0, 0); + if (strncpy_from_user(algo, sha_req->algo, sizeof(algo)) < 0) { + ret = -EFAULT; + goto out_alloc; + } + algo[sizeof(algo) - 1] = '\0'; + + tfm = crypto_alloc_ahash(algo, 0, 0); if (IS_ERR(tfm)) { pr_err("alg:hash:Failed to load transform for %s:%ld\n", - sha_req->algo, PTR_ERR(tfm)); + algo, PTR_ERR(tfm)); goto out_alloc; } req = ahash_request_alloc(tfm, GFP_KERNEL); if (!req) { pr_err("alg:hash:Failed to allocate request for %s\n", - sha_req->algo); + algo); goto out_noreq; } @@ -574,7 +610,14 @@ static int tegra_crypto_sha(struct tegra_sha_req *sha_req) hash_buff = xbuf[0]; - memcpy(hash_buff, sha_req->plaintext, sha_req->plaintext_sz); + ret = copy_from_user((void *)hash_buff, + (void __user *)sha_req->plaintext, + sha_req->plaintext_sz); + if (ret) { + ret = -EFAULT; + pr_err("%s: copy_from_user failed (%d)\n", __func__, ret); + goto out; + } sg_init_one(&sg[0], hash_buff, sha_req->plaintext_sz); if (sha_req->keylen) { @@ -583,7 +626,7 @@ static int tegra_crypto_sha(struct tegra_sha_req *sha_req) sha_req->keylen); if (ret) { pr_err("alg:hash:setkey failed on %s:ret=%d\n", - sha_req->algo, ret); + algo, ret); goto out; } @@ -594,21 +637,21 @@ static int tegra_crypto_sha(struct tegra_sha_req *sha_req) ret = sha_async_hash_op(req, &sha_complete, crypto_ahash_init(req)); if (ret) { pr_err("alg: hash: init failed for %s: ret=%d\n", - sha_req->algo, ret); + algo, ret); goto out; } ret = sha_async_hash_op(req, &sha_complete, crypto_ahash_update(req)); if (ret) { pr_err("alg: hash: update failed for %s: ret=%d\n", - sha_req->algo, ret); + algo, ret); goto out; } ret = sha_async_hash_op(req, &sha_complete, crypto_ahash_final(req)); if (ret) { pr_err("alg: hash: final failed for %s: ret=%d\n", - sha_req->algo, ret); + algo, ret); goto out; } @@ -617,7 +660,7 @@ static int tegra_crypto_sha(struct tegra_sha_req *sha_req) if (ret) { ret = -EFAULT; pr_err("alg: hash: copy_to_user failed (%d) for %s\n", - ret, sha_req->algo); + ret, algo); } out: @@ -890,6 +933,9 @@ rng_out: rsa_req.algo); return -EINVAL; } + + speculation_barrier(); + ret = tegra_crypt_rsa(ctx, &rsa_req); break; diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index bd521b77bc5b..7d7d9feae144 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -182,6 +182,7 @@ #define RP_VEND_XP 0x00000F00 #define RP_VEND_XP_DL_UP (1 << 30) +#define RP_VEND_XP_UPDATE_FC_THRESHOLD (0xFF << 18) #define RP_LINK_CONTROL_STATUS 0x00000090 @@ -195,6 +196,13 @@ #define NV_PCIE2_RP_INTR_BCR 0x0000003C #define NV_PCIE2_RP_INTR_BCR_INTR_LINE (0xFF << 0) +#define NV_PCIE2_RP_PRIV_XP_DL 0x00000494 +#define PCIE2_RP_PRIV_XP_DL_GEN2_UPD_FC_TSHOLD (0x1FF << 1) + +#define NV_PCIE2_RP_RX_HDR_LIMIT 0x00000E00 +#define PCIE2_RP_RX_HDR_LIMIT_PW_MASK (0xFF00) +#define PCIE2_RP_RX_HDR_LIMIT_PW (0x0E << 8) + #define NV_PCIE2_RP_PRIV_MISC 0x00000FE0 #define PCIE2_RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) #define PCIE2_RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) @@ -1609,6 +1617,19 @@ static void tegra_pcie_apply_sw_war(int index, bool enum_done) data = rp_readl(NV_PCIE2_RP_INTR_BCR, index); data |= NV_PCIE2_RP_INTR_BCR_INTR_LINE; rp_writel(data, NV_PCIE2_RP_INTR_BCR, index); + /* WAR for RAW violation on T124/T132 platforms */ + data = rp_readl(NV_PCIE2_RP_RX_HDR_LIMIT, index); + data &= ~PCIE2_RP_RX_HDR_LIMIT_PW_MASK; + data |= PCIE2_RP_RX_HDR_LIMIT_PW; + rp_writel(data, NV_PCIE2_RP_RX_HDR_LIMIT, index); + + data = rp_readl(NV_PCIE2_RP_PRIV_XP_DL, index); + data |= PCIE2_RP_PRIV_XP_DL_GEN2_UPD_FC_TSHOLD; + rp_writel(data, NV_PCIE2_RP_PRIV_XP_DL, index); + + data = rp_readl(RP_VEND_XP, index); + data |= RP_VEND_XP_UPDATE_FC_THRESHOLD; + rp_writel(data, RP_VEND_XP, index); } } diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index df5e961484e1..47eafb87e038 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -765,8 +765,14 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, return k; /* probably out of space --> ENOMEM */ } if (sdp->detached) { - if (srp->bio) + if (srp->bio) { + if (srp->rq->cmd != srp->rq->__cmd) + kfree(srp->rq->cmd); + blk_end_request_all(srp->rq, -EIO); + srp->rq = NULL; + } + sg_finish_rem_req(srp); return -ENODEV; } diff --git a/drivers/staging/android/ion/ion_heap.c b/drivers/staging/android/ion/ion_heap.c index 551fe2e0bc2d..ec1fb7913f4c 100644 --- a/drivers/staging/android/ion/ion_heap.c +++ b/drivers/staging/android/ion/ion_heap.c @@ -269,6 +269,8 @@ static int ion_heap_shrink(struct shrinker *shrinker, struct shrink_control *sc) { struct ion_heap *heap = container_of(shrinker, struct ion_heap, shrinker); + if (IS_ERR_OR_NULL(heap)) + return -EINVAL; int total = 0; int freed = 0; int to_scan = sc->nr_to_scan; @@ -309,8 +311,9 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) switch (heap_data->type) { case ION_HEAP_TYPE_SYSTEM_CONTIG: - heap = ion_system_contig_heap_create(heap_data); - break; + pr_err("%s: Heap type is disabled: %d\n", __func__, + heap_data->type); + return ERR_PTR(-EINVAL); case ION_HEAP_TYPE_SYSTEM: heap = ion_system_heap_create(heap_data); break; @@ -343,12 +346,13 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) void ion_heap_destroy(struct ion_heap *heap) { - if (!heap) + if (IS_ERR_OR_NULL(heap)) return; switch (heap->type) { case ION_HEAP_TYPE_SYSTEM_CONTIG: - ion_system_contig_heap_destroy(heap); + pr_err("%s: Heap type is disabled: %d\n", __func__, + heap->type); break; case ION_HEAP_TYPE_SYSTEM: ion_system_heap_destroy(heap); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index ffb4b9c41a40..68e6e09fdb37 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -944,8 +944,8 @@ thermal_cooling_device_cur_state_store(struct device *dev, const char *buf, size_t count) { struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int result; + unsigned long state, max_state; + int result, ret; if (!sscanf(buf, "%ld\n", &state)) return -EINVAL; @@ -953,6 +953,13 @@ thermal_cooling_device_cur_state_store(struct device *dev, if ((long)state < 0) return -EINVAL; + ret = cdev->ops->get_max_state(cdev, &max_state); + if (ret) + return ret; + + if (state > max_state) + return -EINVAL; + result = cdev->ops->set_cur_state(cdev, state); if (result) return result; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 1afe192bef6a..b5cbe12e2815 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -400,6 +400,10 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); * they are not on hot paths so a little discipline won't do * any harm. * + * The line discipline-related tty_struct fields are reset to + * prevent the ldisc driver from re-using stale information for + * the new ldisc instance. + * * Locking: takes termios_mutex */ @@ -408,6 +412,9 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) mutex_lock(&tty->termios_mutex); tty->termios.c_line = num; mutex_unlock(&tty->termios_mutex); + + tty->disc_data = NULL; + tty->receive_room = 0; } /** diff --git a/drivers/video/tegra/dc/sor.c b/drivers/video/tegra/dc/sor.c index 1326155cd183..5fee008d122a 100644 --- a/drivers/video/tegra/dc/sor.c +++ b/drivers/video/tegra/dc/sor.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/sor.c * - * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -1287,7 +1287,7 @@ void tegra_dc_sor_enable_lvds(struct tegra_dc_sor_data *sor, 6 << NV_SOR_LVDS_ROTDAT_SHIFT: 0 << NV_SOR_LVDS_ROTDAT_SHIFT); tegra_sor_writel(sor, NV_SOR_LANE4_DRIVE_CURRENT(sor->portnum), - 0x40); + 0x40); } #if 0 diff --git a/drivers/video/tegra/dc/sor_regs.h b/drivers/video/tegra/dc/sor_regs.h index 8e1cc1c3231c..8080e2925d82 100644 --- a/drivers/video/tegra/dc/sor_regs.h +++ b/drivers/video/tegra/dc/sor_regs.h @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/sor_regs.h * - * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2011-2017, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and diff --git a/drivers/video/tegra/host/host1x/host1x.c b/drivers/video/tegra/host/host1x/host1x.c index 522219484286..6af16bab059c 100644 --- a/drivers/video/tegra/host/host1x/host1x.c +++ b/drivers/video/tegra/host/host1x/host1x.c @@ -33,6 +33,8 @@ #include <linux/tegra-soc.h> #include <linux/tegra_pm_domains.h> +#include <linux/version.h> +#include <asm/barrier.h> #include "dev.h" #include <trace/events/nvhost.h> @@ -267,6 +269,8 @@ static int nvhost_ioctl_ctrl_module_mutex(struct nvhost_ctrl_userctx *ctx, args->lock > 1) return -EINVAL; + speculation_barrier(); + trace_nvhost_ioctl_ctrl_module_mutex(args->lock, args->id); if (args->lock && !ctx->mod_locks[args->id]) { if (args->id == 0) @@ -379,6 +383,7 @@ static int nvhost_ioctl_ctrl_syncpt_read_max(struct nvhost_ctrl_userctx *ctx, { if (args->id >= nvhost_syncpt_nb_pts(&ctx->dev->syncpt)) return -EINVAL; + speculation_barrier(); args->value = nvhost_syncpt_read_max(&ctx->dev->syncpt, args->id); return 0; } diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index b0af8a143bd2..4d431cc14890 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -3,7 +3,7 @@ * * Tegra Graphics Host Syncpoints * - * Copyright (c) 2010-2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2010-2018, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -650,13 +650,18 @@ static ssize_t syncpt_name_show(struct kobject *kobj, { struct nvhost_syncpt_attr *syncpt_attr = container_of(attr, struct nvhost_syncpt_attr, attr); + ssize_t count = 0; if (syncpt_attr->id < 0) return snprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%s\n", + mutex_lock(&syncpt_attr->host->syncpt.syncpt_mutex); + count = snprintf(buf, PAGE_SIZE, "%s\n", nvhost_syncpt_get_name(syncpt_attr->host->dev, syncpt_attr->id)); + mutex_unlock(&syncpt_attr->host->syncpt.syncpt_mutex); + + return count; } static ssize_t syncpt_min_show(struct kobject *kobj, diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 1be3996b5942..3517d5af2574 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -75,8 +75,14 @@ int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) ext4_put_nojournal(handle); return 0; } - sb = handle->h_transaction->t_journal->j_private; + err = handle->h_err; + if (!handle->h_transaction) { + rc = jbd2_journal_stop(handle); + return err ? err : rc; + } + + sb = handle->h_transaction->t_journal->j_private; rc = jbd2_journal_stop(handle); if (!err) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 4e5f332f15d9..db7d89cea2ce 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -169,7 +169,7 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, addr = ALIGN(addr, huge_page_size(h)); vma = find_vma(mm, addr); if (TASK_SIZE - len >= addr && - (!vma || addr + len <= vma->vm_start)) + (!vma || addr + len <= vm_start_gap(vma))) return addr; } diff --git a/fs/ioprio.c b/fs/ioprio.c index e50170ca7c33..46f0e24f43e9 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -149,8 +149,10 @@ static int get_task_ioprio(struct task_struct *p) if (ret) goto out; ret = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, IOPRIO_NORM); + task_lock(p); if (p->io_context) ret = p->io_context->ioprio; + task_unlock(p); out: return ret; } diff --git a/fs/proc/base.c b/fs/proc/base.c index 4823113f2584..464df13a1c19 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -46,7 +46,7 @@ * Paul Mundt <paul.mundt@nokia.com>: * Overall revision about smaps. * - * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. */ #include <asm/uaccess.h> @@ -857,7 +857,8 @@ static ssize_t environ_read(struct file *file, char __user *buf, int ret = 0; struct mm_struct *mm = file->private_data; - if (!mm) + /* Ensure the process spawned far enough to have an environment. */ + if (!mm || !mm->env_end) return 0; page = (char *)__get_free_page(GFP_TEMPORARY); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index db17f98bc564..fb705f672960 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -329,11 +329,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) /* We don't show the stack guard page in /proc/maps */ start = vma->vm_start; - if (stack_guard_page_start(vma, start)) - start += PAGE_SIZE; end = vma->vm_end; - if (stack_guard_page_end(vma, end)) - end -= PAGE_SIZE; seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n", start, diff --git a/include/linux/mm.h b/include/linux/mm.h index 03a60a38ec45..9d572d445e3e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1066,34 +1066,6 @@ int set_page_dirty(struct page *page); int set_page_dirty_lock(struct page *page); int clear_page_dirty_for_io(struct page *page); -/* Is the vma a continuation of the stack vma above it? */ -static inline int vma_growsdown(struct vm_area_struct *vma, unsigned long addr) -{ - return vma && (vma->vm_end == addr) && (vma->vm_flags & VM_GROWSDOWN); -} - -static inline int stack_guard_page_start(struct vm_area_struct *vma, - unsigned long addr) -{ - return (vma->vm_flags & VM_GROWSDOWN) && - (vma->vm_start == addr) && - !vma_growsdown(vma->vm_prev, addr); -} - -/* Is the vma a continuation of the stack vma below it? */ -static inline int vma_growsup(struct vm_area_struct *vma, unsigned long addr) -{ - return vma && (vma->vm_start == addr) && (vma->vm_flags & VM_GROWSUP); -} - -static inline int stack_guard_page_end(struct vm_area_struct *vma, - unsigned long addr) -{ - return (vma->vm_flags & VM_GROWSUP) && - (vma->vm_end == addr) && - !vma_growsup(vma->vm_next, addr); -} - extern pid_t vm_is_stack(struct task_struct *task, struct vm_area_struct *vma, int in_group); @@ -1620,6 +1592,7 @@ unsigned long ra_submit(struct file_ra_state *ra, struct address_space *mapping, struct file *filp); +extern unsigned long stack_guard_gap; /* Generic expand stack which grows the stack according to GROWS{UP,DOWN} */ extern int expand_stack(struct vm_area_struct *vma, unsigned long address); @@ -1648,6 +1621,30 @@ static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * m return vma; } +static inline unsigned long vm_start_gap(struct vm_area_struct *vma) +{ + unsigned long vm_start = vma->vm_start; + + if (vma->vm_flags & VM_GROWSDOWN) { + vm_start -= stack_guard_gap; + if (vm_start > vma->vm_start) + vm_start = 0; + } + return vm_start; +} + +static inline unsigned long vm_end_gap(struct vm_area_struct *vma) +{ + unsigned long vm_end = vma->vm_end; + + if (vma->vm_flags & VM_GROWSUP) { + vm_end += stack_guard_gap; + if (vm_end < vma->vm_end) + vm_end = -PAGE_SIZE; + } + return vm_end; +} + static inline unsigned long vma_pages(struct vm_area_struct *vma) { return (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; diff --git a/include/linux/nospec.h b/include/linux/nospec.h new file mode 100644 index 000000000000..b99bced39ac2 --- /dev/null +++ b/include/linux/nospec.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Linus Torvalds. All rights reserved. +// Copyright(c) 2018 Alexei Starovoitov. All rights reserved. +// Copyright(c) 2018 Intel Corporation. All rights reserved. + +#ifndef _LINUX_NOSPEC_H +#define _LINUX_NOSPEC_H + +/** + * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise + * @index: array element index + * @size: number of elements in array + * + * When @index is out of bounds (@index >= @size), the sign bit will be + * set. Extend the sign bit to all bits and invert, giving a result of + * zero for an out of bounds index, or ~0 if within bounds [0, @size). + */ +#ifndef array_index_mask_nospec +static inline unsigned long array_index_mask_nospec(unsigned long index, + unsigned long size) +{ + /* + * Warn developers about inappropriate array_index_nospec() usage. + * + * Even if the CPU speculates past the WARN_ONCE branch, the + * sign bit of @index is taken into account when generating the + * mask. + * + * This warning is compiled out when the compiler can infer that + * @index and @size are less than LONG_MAX. + */ + if (WARN_ONCE(index > LONG_MAX || size > LONG_MAX, + "array_index_nospec() limited to range of [0, LONG_MAX]\n")) + return 0; + + /* + * Always calculate and emit the mask even if the compiler + * thinks the mask is not needed. The compiler does not take + * into account the value of @index under speculation. + */ + OPTIMIZER_HIDE_VAR(index); + return ~(long)(index | (size - 1UL - index)) >> (BITS_PER_LONG - 1); +} +#endif + +/* + * array_index_nospec - sanitize an array index after a bounds check + * + * For a code sequence like: + * + * if (index < size) { + * index = array_index_nospec(index, size); + * val = array[index]; + * } + * + * ...if the CPU speculates past the bounds check then + * array_index_nospec() will clamp the index within the range of [0, + * size). + */ +#define array_index_nospec(index, size) \ +({ \ + typeof(index) _i = (index); \ + typeof(size) _s = (size); \ + unsigned long _mask = array_index_mask_nospec(_i, _s); \ + \ + BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \ + BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \ + \ + _i &= _mask; \ + _i; \ +}) +#endif /* _LINUX_NOSPEC_H */ diff --git a/include/linux/nvhost_ioctl.h b/include/linux/nvhost_ioctl.h index a1011e5a1daf..b060864ff1d1 100644 --- a/include/linux/nvhost_ioctl.h +++ b/include/linux/nvhost_ioctl.h @@ -3,7 +3,7 @@ * * Tegra graphics host driver * - * Copyright (c) 2009-2017, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2009-2014, NVIDIA Corporation. 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 as published by @@ -118,6 +118,10 @@ struct nvhost_alloc_obj_ctx_args { __u64 obj_id; /* output, used to free later */ }; +struct nvhost_free_obj_ctx_args { + __u64 obj_id; /* obj ctx to free */ +}; + struct nvhost_alloc_gpfifo_args { __u32 num_entries; #define NVHOST_ALLOC_GPFIFO_FLAGS_VPR_ENABLED (1 << 0) /* set owner channel of this gpfifo as a vpr channel */ @@ -386,6 +390,8 @@ struct nvhost_set_ctxswitch_args { _IOWR(NVHOST_IOCTL_MAGIC, 107, struct nvhost_submit_gpfifo_args) #define NVHOST_IOCTL_CHANNEL_ALLOC_OBJ_CTX \ _IOWR(NVHOST_IOCTL_MAGIC, 108, struct nvhost_alloc_obj_ctx_args) +#define NVHOST_IOCTL_CHANNEL_FREE_OBJ_CTX \ + _IOR(NVHOST_IOCTL_MAGIC, 109, struct nvhost_free_obj_ctx_args) #define NVHOST_IOCTL_CHANNEL_ZCULL_BIND \ _IOWR(NVHOST_IOCTL_MAGIC, 110, struct nvhost_zcull_bind_args) #define NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER \ diff --git a/include/net/tcp.h b/include/net/tcp.h index 13f12c10f03b..013daf1494ba 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1392,6 +1392,8 @@ static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unli { if (sk->sk_send_head == skb_unlinked) sk->sk_send_head = NULL; + if (tcp_sk(sk)->highest_sack == skb_unlinked) + tcp_sk(sk)->highest_sack = NULL; } static inline void tcp_init_send_head(struct sock *sk) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9845cb32b60a..04a257b4a2da 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -68,6 +68,7 @@ #include <linux/capability.h> #include <linux/fs_struct.h> #include <linux/compat.h> +#include <linux/uaccess.h> #include "audit.h" @@ -76,7 +77,8 @@ #define AUDITSC_SUCCESS 1 #define AUDITSC_FAILURE 2 -/* no execve audit message should be longer than this (userspace limits) */ +/* no execve audit message should be longer than this (userspace limits), + * see the note near the top of audit_log_execve_info() about this value */ #define MAX_EXECVE_AUDIT_LEN 7500 /* number of audit rules */ @@ -1001,189 +1003,184 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid, return rc; } -/* - * to_send and len_sent accounting are very loose estimates. We aren't - * really worried about a hard cap to MAX_EXECVE_AUDIT_LEN so much as being - * within about 500 bytes (next page boundary) - * - * why snprintf? an int is up to 12 digits long. if we just assumed when - * logging that a[%d]= was going to be 16 characters long we would be wasting - * space in every audit message. In one 7500 byte message we can log up to - * about 1000 min size arguments. That comes down to about 50% waste of space - * if we didn't do the snprintf to find out how long arg_num_len was. - */ -static int audit_log_single_execve_arg(struct audit_context *context, - struct audit_buffer **ab, - int arg_num, - size_t *len_sent, - const char __user *p, - char *buf) +static void audit_log_execve_info(struct audit_context *context, + struct audit_buffer **ab, + struct audit_aux_data_execve *axi) { - char arg_num_len_buf[12]; - const char __user *tmp_p = p; - /* how many digits are in arg_num? 5 is the length of ' a=""' */ - size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 5; - size_t len, len_left, to_send; - size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN; - unsigned int i, has_cntl = 0, too_long = 0; - int ret; - - /* strnlen_user includes the null we don't want to send */ - len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1; + long len_max; + long len_rem; + long len_full; + long len_buf; + long len_abuf; + long len_tmp; + bool require_data; + bool encode; + unsigned int iter; + unsigned int arg; + char *buf_head; + char *buf; + const char __user *p; - /* - * We just created this mm, if we can't find the strings - * we just copied into it something is _very_ wrong. Similar - * for strings that are too long, we should not have created - * any. - */ - if (unlikely((len == -1) || len > MAX_ARG_STRLEN - 1)) { - WARN_ON(1); - send_sig(SIGKILL, current, 0); - return -1; + /* NOTE: this buffer needs to be large enough to hold all the non-arg + * data we put in the audit record for this argument (see the + * code below) ... at this point in time 96 is plenty */ + char abuf[96]; + + if (axi->mm != current->mm) + return; /* execve failed, no additional info */ + + p = (const char __user *)axi->mm->arg_start; + + /* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the + * current value of 7500 is not as important as the fact that it + * is less than 8k, a setting of 7500 gives us plenty of wiggle + * room if we go over a little bit in the logging below */ + WARN_ON_ONCE(MAX_EXECVE_AUDIT_LEN > 7500); + len_max = MAX_EXECVE_AUDIT_LEN; + + /* scratch buffer to hold the userspace args */ + buf_head = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL); + if (!buf_head) { + audit_panic("out of memory for argv string"); + return; } + buf = buf_head; + + audit_log_format(*ab, "argc=%d", axi->argc); - /* walk the whole argument looking for non-ascii chars */ + len_rem = len_max; + len_buf = 0; + len_full = 0; + require_data = true; + encode = false; + iter = 0; + arg = 0; do { - if (len_left > MAX_EXECVE_AUDIT_LEN) - to_send = MAX_EXECVE_AUDIT_LEN; - else - to_send = len_left; - ret = copy_from_user(buf, tmp_p, to_send); - /* - * There is no reason for this copy to be short. We just - * copied them here, and the mm hasn't been exposed to user- - * space yet. - */ - if (ret) { - WARN_ON(1); - send_sig(SIGKILL, current, 0); - return -1; - } - buf[to_send] = '\0'; - has_cntl = audit_string_contains_control(buf, to_send); - if (has_cntl) { - /* - * hex messages get logged as 2 bytes, so we can only - * send half as much in each message - */ - max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2; - break; - } - len_left -= to_send; - tmp_p += to_send; - } while (len_left > 0); - - len_left = len; - - if (len > max_execve_audit_len) - too_long = 1; - - /* rewalk the argument actually logging the message */ - for (i = 0; len_left > 0; i++) { - int room_left; - - if (len_left > max_execve_audit_len) - to_send = max_execve_audit_len; - else - to_send = len_left; - - /* do we have space left to send this argument in this ab? */ - room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent; - if (has_cntl) - room_left -= (to_send * 2); - else - room_left -= to_send; - if (room_left < 0) { - *len_sent = 0; - audit_log_end(*ab); - *ab = audit_log_start(context, GFP_KERNEL, AUDIT_EXECVE); - if (!*ab) - return 0; - } + /* NOTE: we don't ever want to trust this value for anything + * serious, but the audit record format insists we + * provide an argument length for really long arguments, + * e.g. > MAX_EXECVE_AUDIT_LEN, so we have no choice but + * to use strncpy_from_user() to obtain this value for + * recording in the log, although we don't use it + * anywhere here to avoid a double-fetch problem */ + if (len_full == 0) + len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1; + + /* read more data from userspace */ + if (require_data) { + /* can we make more room in the buffer? */ + if (buf != buf_head) { + memmove(buf_head, buf, len_buf); + buf = buf_head; + } - /* - * first record needs to say how long the original string was - * so we can be sure nothing was lost. - */ - if ((i == 0) && (too_long)) - audit_log_format(*ab, " a%d_len=%zu", arg_num, - has_cntl ? 2*len : len); - - /* - * normally arguments are small enough to fit and we already - * filled buf above when we checked for control characters - * so don't bother with another copy_from_user - */ - if (len >= max_execve_audit_len) - ret = copy_from_user(buf, p, to_send); - else - ret = 0; - if (ret) { - WARN_ON(1); - send_sig(SIGKILL, current, 0); - return -1; - } - buf[to_send] = '\0'; - - /* actually log it */ - audit_log_format(*ab, " a%d", arg_num); - if (too_long) - audit_log_format(*ab, "[%d]", i); - audit_log_format(*ab, "="); - if (has_cntl) - audit_log_n_hex(*ab, buf, to_send); - else - audit_log_string(*ab, buf); - - p += to_send; - len_left -= to_send; - *len_sent += arg_num_len; - if (has_cntl) - *len_sent += to_send * 2; - else - *len_sent += to_send; - } - /* include the null we didn't log */ - return len + 1; -} + /* fetch as much as we can of the argument */ + len_tmp = strncpy_from_user(&buf_head[len_buf], p, + len_max - len_buf); + if (len_tmp == -EFAULT) { + /* unable to copy from userspace */ + send_sig(SIGKILL, current, 0); + goto out; + } else if (len_tmp == (len_max - len_buf)) { + /* buffer is not large enough */ + require_data = true; + /* NOTE: if we are going to span multiple + * buffers force the encoding so we stand + * a chance at a sane len_full value and + * consistent record encoding */ + encode = true; + len_full = len_full * 2; + p += len_tmp; + } else { + require_data = false; + if (!encode) + encode = audit_string_contains_control( + buf, len_tmp); + /* try to use a trusted value for len_full */ + if (len_full < len_max) + len_full = (encode ? + len_tmp * 2 : len_tmp); + p += len_tmp + 1; + } + len_buf += len_tmp; + buf_head[len_buf] = '\0'; -static void audit_log_execve_info(struct audit_context *context, - struct audit_buffer **ab, - struct audit_aux_data_execve *axi) -{ - int i, len; - size_t len_sent = 0; - const char __user *p; - char *buf; + /* length of the buffer in the audit record? */ + len_abuf = (encode ? len_buf * 2 : len_buf + 2); + } - if (axi->mm != current->mm) - return; /* execve failed, no additional info */ + /* write as much as we can to the audit log */ + if (len_buf > 0) { + /* NOTE: some magic numbers here - basically if we + * can't fit a reasonable amount of data into the + * existing audit buffer, flush it and start with + * a new buffer */ + if ((sizeof(abuf) + 8) > len_rem) { + len_rem = len_max; + audit_log_end(*ab); + *ab = audit_log_start(context, + GFP_KERNEL, AUDIT_EXECVE); + if (!*ab) + goto out; + } - p = (const char __user *)axi->mm->arg_start; + /* create the non-arg portion of the arg record */ + len_tmp = 0; + if (require_data || (iter > 0) || + ((len_abuf + sizeof(abuf)) > len_rem)) { + if (iter == 0) { + len_tmp += snprintf(&abuf[len_tmp], + sizeof(abuf) - len_tmp, + " a%d_len=%lu", + arg, len_full); + } + len_tmp += snprintf(&abuf[len_tmp], + sizeof(abuf) - len_tmp, + " a%d[%d]=", arg, iter++); + } else + len_tmp += snprintf(&abuf[len_tmp], + sizeof(abuf) - len_tmp, + " a%d=", arg); + WARN_ON(len_tmp >= sizeof(abuf)); + abuf[sizeof(abuf) - 1] = '\0'; + + /* log the arg in the audit record */ + audit_log_format(*ab, "%s", abuf); + len_rem -= len_tmp; + len_tmp = len_buf; + if (encode) { + if (len_abuf > len_rem) + len_tmp = len_rem / 2; /* encoding */ + audit_log_n_hex(*ab, buf, len_tmp); + len_rem -= len_tmp * 2; + len_abuf -= len_tmp * 2; + } else { + if (len_abuf > len_rem) + len_tmp = len_rem - 2; /* quotes */ + audit_log_n_string(*ab, buf, len_tmp); + len_rem -= len_tmp + 2; + /* don't subtract the "2" because we still need + * to add quotes to the remaining string */ + len_abuf -= len_tmp; + } + len_buf -= len_tmp; + buf += len_tmp; + } - audit_log_format(*ab, "argc=%d", axi->argc); + /* ready to move to the next argument? */ + if ((len_buf == 0) && !require_data) { + arg++; + iter = 0; + len_full = 0; + require_data = true; + encode = false; + } + } while (arg < axi->argc); - /* - * we need some kernel buffer to hold the userspace args. Just - * allocate one big one rather than allocating one of the right size - * for every single argument inside audit_log_single_execve_arg() - * should be <8k allocation so should be pretty safe. - */ - buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL); - if (!buf) { - audit_panic("out of memory for argv string\n"); - return; - } + /* NOTE: the caller handles the final audit_log_end() call */ - for (i = 0; i < axi->argc; i++) { - len = audit_log_single_execve_arg(context, ab, i, - &len_sent, p, buf); - if (len <= 0) - break; - p += len; - } - kfree(buf); +out: + kfree(buf_head); } static void show_special(struct audit_context *context, int *call_panic) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 05b36e7b9e6b..1e2c3588b990 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5449,7 +5449,7 @@ static int cgroup_css_links_read(struct cgroup *cont, struct css_set *cg = link->cg; struct task_struct *task; int count = 0; - seq_printf(seq, "css_set %p\n", cg); + seq_printf(seq, "css_set %pK\n", cg); list_for_each_entry(task, &cg->tasks, cg_list) { if (count++ > MAX_TASKS_SHOWN_PER_CSS) { seq_puts(seq, " ...\n"); diff --git a/kernel/events/core.c b/kernel/events/core.c index f8eb2b154bdb..be3b4cc87eeb 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7450,13 +7450,7 @@ static void perf_event_exit_cpu_context(int cpu) static void perf_event_exit_cpu(int cpu) { - struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu); - perf_event_exit_cpu_context(cpu); - - mutex_lock(&swhash->hlist_mutex); - swevent_hlist_release(swhash); - mutex_unlock(&swhash->hlist_mutex); } #else static inline void perf_event_exit_cpu(int cpu) { } diff --git a/mm/memory.c b/mm/memory.c index 6be9914ddc0d..17c9186c819a 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1659,12 +1659,6 @@ no_page_table: return page; } -static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) -{ - return stack_guard_page_start(vma, addr) || - stack_guard_page_end(vma, addr+PAGE_SIZE); -} - /** * replace_cma_page() - migrate page out of CMA page blocks * @page: source page to be migrated @@ -1875,10 +1869,6 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, fault_flags = FAULT_FLAG_NO_CMA; /* For mlock, just skip the stack guard page. */ - if (foll_flags & FOLL_MLOCK) { - if (stack_guard_page(vma, start)) - goto next_page; - } if (foll_flags & FOLL_WRITE) fault_flags |= FAULT_FLAG_WRITE; if (nonblocking) @@ -3171,40 +3161,6 @@ out_release: return ret; } -/* - * This is like a special single-page "expand_{down|up}wards()", - * except we must first make sure that 'address{-|+}PAGE_SIZE' - * doesn't hit another vma. - */ -static inline int check_stack_guard_page(struct vm_area_struct *vma, unsigned long address) -{ - address &= PAGE_MASK; - if ((vma->vm_flags & VM_GROWSDOWN) && address == vma->vm_start) { - struct vm_area_struct *prev = vma->vm_prev; - - /* - * Is there a mapping abutting this one below? - * - * That's only ok if it's the same stack mapping - * that has gotten split.. - */ - if (prev && prev->vm_end == address) - return prev->vm_flags & VM_GROWSDOWN ? 0 : -ENOMEM; - - expand_downwards(vma, address - PAGE_SIZE); - } - if ((vma->vm_flags & VM_GROWSUP) && address + PAGE_SIZE == vma->vm_end) { - struct vm_area_struct *next = vma->vm_next; - - /* As VM_GROWSDOWN but s/below/above/ */ - if (next && next->vm_start == address + PAGE_SIZE) - return next->vm_flags & VM_GROWSUP ? 0 : -ENOMEM; - - expand_upwards(vma, address + PAGE_SIZE); - } - return 0; -} - bool is_vma_temporary_stack(struct vm_area_struct *vma); /* * We enter with non-exclusive mmap_sem (to exclude vma changes, @@ -3221,10 +3177,6 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, pte_unmap(page_table); - /* Check if we need to add a guard page to the stack */ - if (check_stack_guard_page(vma, address) < 0) - return VM_FAULT_SIGBUS; - /* Use the zero-page for reads */ if (!(flags & FAULT_FLAG_WRITE)) { entry = pte_mkspecial(pfn_pte(my_zero_pfn(address), diff --git a/mm/mmap.c b/mm/mmap.c index e0a152e2dc10..a323e12f8a6d 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -264,6 +264,7 @@ SYSCALL_DEFINE1(brk, unsigned long, brk) unsigned long rlim, retval; unsigned long newbrk, oldbrk; struct mm_struct *mm = current->mm; + struct vm_area_struct *next; unsigned long min_brk; bool populate; @@ -309,7 +310,8 @@ SYSCALL_DEFINE1(brk, unsigned long, brk) } /* Check against existing mmap mappings. */ - if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) + next = find_vma(mm, oldbrk); + if (next && newbrk + PAGE_SIZE > vm_start_gap(next)) goto out; /* Ok, looks good - let it rip. */ @@ -332,10 +334,22 @@ out: static long vma_compute_subtree_gap(struct vm_area_struct *vma) { - unsigned long max, subtree_gap; - max = vma->vm_start; - if (vma->vm_prev) - max -= vma->vm_prev->vm_end; + unsigned long max, prev_end, subtree_gap; + + /* + * Note: in the rare case of a VM_GROWSDOWN above a VM_GROWSUP, we + * allow two stack_guard_gaps between them here, and when choosing + * an unmapped area; whereas when expanding we only require one. + * That's a little inconsistent, but keeps the code here simpler. + */ + max = vm_start_gap(vma); + if (vma->vm_prev) { + prev_end = vm_end_gap(vma->vm_prev); + if (max > prev_end) + max -= prev_end; + else + max = 0; + } if (vma->vm_rb.rb_left) { subtree_gap = rb_entry(vma->vm_rb.rb_left, struct vm_area_struct, vm_rb)->rb_subtree_gap; @@ -419,7 +433,7 @@ void validate_mm(struct mm_struct *mm) list_for_each_entry(avc, &vma->anon_vma_chain, same_vma) anon_vma_interval_tree_verify(avc); vma_unlock_anon_vma(vma); - highest_address = vma->vm_end; + highest_address = vm_end_gap(vma); vma = vma->vm_next; i++; } @@ -587,7 +601,7 @@ void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, if (vma->vm_next) vma_gap_update(vma->vm_next); else - mm->highest_vm_end = vma->vm_end; + mm->highest_vm_end = vm_end_gap(vma); /* * vma->vm_prev wasn't known when we followed the rbtree to find the @@ -836,7 +850,7 @@ again: remove_next = 1 + (end > next->vm_end); vma_gap_update(vma); if (end_changed) { if (!next) - mm->highest_vm_end = end; + mm->highest_vm_end = vm_end_gap(vma); else if (!adjust_next) vma_gap_update(next); } @@ -879,7 +893,7 @@ again: remove_next = 1 + (end > next->vm_end); else if (next) vma_gap_update(next); else - mm->highest_vm_end = end; + mm->highest_vm_end = vm_end_gap(vma); } if (insert && file) uprobe_mmap(insert); @@ -1679,7 +1693,7 @@ unsigned long unmapped_area(struct vm_unmapped_area_info *info) while (true) { /* Visit left subtree if it looks promising */ - gap_end = vma->vm_start; + gap_end = vm_start_gap(vma); if (gap_end >= low_limit && vma->vm_rb.rb_left) { struct vm_area_struct *left = rb_entry(vma->vm_rb.rb_left, @@ -1690,7 +1704,7 @@ unsigned long unmapped_area(struct vm_unmapped_area_info *info) } } - gap_start = vma->vm_prev ? vma->vm_prev->vm_end : 0; + gap_start = vma->vm_prev ? vm_end_gap(vma->vm_prev) : 0; check_current: /* Check if current node has a suitable gap */ if (gap_start > high_limit) @@ -1717,8 +1731,8 @@ check_current: vma = rb_entry(rb_parent(prev), struct vm_area_struct, vm_rb); if (prev == vma->vm_rb.rb_left) { - gap_start = vma->vm_prev->vm_end; - gap_end = vma->vm_start; + gap_start = vm_end_gap(vma->vm_prev); + gap_end = vm_start_gap(vma); goto check_current; } } @@ -1782,7 +1796,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) while (true) { /* Visit right subtree if it looks promising */ - gap_start = vma->vm_prev ? vma->vm_prev->vm_end : 0; + gap_start = vma->vm_prev ? vm_end_gap(vma->vm_prev) : 0; if (gap_start <= high_limit && vma->vm_rb.rb_right) { struct vm_area_struct *right = rb_entry(vma->vm_rb.rb_right, @@ -1795,7 +1809,7 @@ unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info) check_current: /* Check if current node has a suitable gap */ - gap_end = vma->vm_start; + gap_end = vm_start_gap(vma); if (gap_end < low_limit) return -ENOMEM; if (gap_start <= high_limit && gap_end - gap_start >= length) @@ -1821,7 +1835,7 @@ check_current: struct vm_area_struct, vm_rb); if (prev == vma->vm_rb.rb_right) { gap_start = vma->vm_prev ? - vma->vm_prev->vm_end : 0; + vm_end_gap(vma->vm_prev) : 0; goto check_current; } } @@ -1859,7 +1873,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; + struct vm_area_struct *vma, *prev; struct vm_unmapped_area_info info; if (len > TASK_SIZE - mmap_min_addr) @@ -1870,9 +1884,10 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, if (addr) { addr = PAGE_ALIGN(addr); - vma = find_vma(mm, addr); + vma = find_vma_prev(mm, addr, &prev); if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && - (!vma || addr + len <= vma->vm_start)) + (!vma || addr + len <= vm_start_gap(vma)) && + (!prev || addr >= vm_end_gap(prev))) return addr; } @@ -1904,7 +1919,7 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, const unsigned long len, const unsigned long pgoff, const unsigned long flags) { - struct vm_area_struct *vma; + struct vm_area_struct *vma, *prev; struct mm_struct *mm = current->mm; unsigned long addr = addr0; struct vm_unmapped_area_info info; @@ -1919,9 +1934,10 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, /* requesting a specific address */ if (addr) { addr = PAGE_ALIGN(addr); - vma = find_vma(mm, addr); + vma = find_vma_prev(mm, addr, &prev); if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && - (!vma || addr + len <= vma->vm_start)) + (!vma || addr + len <= vm_start_gap(vma)) && + (!prev || addr >= vm_end_gap(prev))) return addr; } @@ -2061,7 +2077,8 @@ find_vma_prev(struct mm_struct *mm, unsigned long addr, * update accounting. This is shared with both the * grow-up and grow-down cases. */ -static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, unsigned long grow) +static int acct_stack_growth(struct vm_area_struct *vma, + unsigned long size, unsigned long grow) { struct mm_struct *mm = vma->vm_mm; struct rlimit *rlim = current->signal->rlim; @@ -2114,6 +2131,8 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns int expand_upwards(struct vm_area_struct *vma, unsigned long address) { int error; + struct vm_area_struct *next; + unsigned long gap_addr; if (!(vma->vm_flags & VM_GROWSUP)) return -EFAULT; @@ -2124,8 +2143,19 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) */ if (unlikely(anon_vma_prepare(vma))) return -ENOMEM; - vma_lock_anon_vma(vma); + /* Enforce stack_guard_gap */ + gap_addr = address + stack_guard_gap; + if (gap_addr < address) + return -ENOMEM; + next = vma->vm_next; + if (next && next->vm_start < gap_addr) { + if (!(next->vm_flags & VM_GROWSUP)) + return -ENOMEM; + /* Check that both stack segments have the same anon_vma? */ + } + + vma_lock_anon_vma(vma); /* * vma->vm_start/vm_end cannot change under us because the caller * is required to hold the mmap_sem in read mode. We need the @@ -2169,7 +2199,7 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) if (vma->vm_next) vma_gap_update(vma->vm_next); else - vma->vm_mm->highest_vm_end = address; + vma->vm_mm->highest_vm_end = vm_end_gap(vma); spin_unlock(&vma->vm_mm->page_table_lock); perf_event_mmap(vma); @@ -2190,6 +2220,8 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) int expand_downwards(struct vm_area_struct *vma, unsigned long address) { + struct vm_area_struct *prev; + unsigned long gap_addr; int error; /* @@ -2204,6 +2236,17 @@ int expand_downwards(struct vm_area_struct *vma, if (error) return error; + /* Enforce stack_guard_gap */ + gap_addr = address - stack_guard_gap; + if (gap_addr > address) + return -ENOMEM; + prev = vma->vm_prev; + if (prev && prev->vm_end > gap_addr) { + if (!(prev->vm_flags & VM_GROWSDOWN)) + return -ENOMEM; + /* Check that both stack segments have the same anon_vma? */ + } + vma_lock_anon_vma(vma); /* @@ -2253,28 +2296,25 @@ int expand_downwards(struct vm_area_struct *vma, return error; } -/* - * Note how expand_stack() refuses to expand the stack all the way to - * abut the next virtual mapping, *unless* that mapping itself is also - * a stack mapping. We want to leave room for a guard page, after all - * (the guard page itself is not added here, that is done by the - * actual page faulting logic) - * - * This matches the behavior of the guard page logic (see mm/memory.c: - * check_stack_guard_page()), which only allows the guard page to be - * removed under these circumstances. - */ +/* enforced gap between the expanding stack and other mappings. */ +unsigned long stack_guard_gap = 256UL<<PAGE_SHIFT; + +static int __init cmdline_parse_stack_guard_gap(char *p) +{ + unsigned long val; + char *endptr; + + val = simple_strtoul(p, &endptr, 10); + if (!*endptr) + stack_guard_gap = val << PAGE_SHIFT; + + return 0; +} +__setup("stack_guard_gap=", cmdline_parse_stack_guard_gap); + #ifdef CONFIG_STACK_GROWSUP int expand_stack(struct vm_area_struct *vma, unsigned long address) { - struct vm_area_struct *next; - - address &= PAGE_MASK; - next = vma->vm_next; - if (next && next->vm_start == address + PAGE_SIZE) { - if (!(next->vm_flags & VM_GROWSUP)) - return -ENOMEM; - } return expand_upwards(vma, address); } @@ -2400,7 +2440,7 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma, vma->vm_prev = prev; vma_gap_update(vma); } else - mm->highest_vm_end = prev ? prev->vm_end : 0; + mm->highest_vm_end = prev ? vm_end_gap(prev) : 0; tail_vma->vm_next = NULL; if (mm->unmap_area == arch_unmap_area) addr = prev ? prev->vm_end : mm->mmap_base; diff --git a/sound/soc/tegra/tegra30_avp.c b/sound/soc/tegra/tegra30_avp.c index 44d954561f96..a074e25614d1 100644 --- a/sound/soc/tegra/tegra30_avp.c +++ b/sound/soc/tegra/tegra30_avp.c @@ -256,7 +256,7 @@ static int tegra30_avp_load_ucode(void) struct audio_engine_data *audio_engine; const struct firmware *ucode_fw; const struct tegra30_avp_ucode_desc *ucode_desc; - int ucode_size = 0, ucode_offset = 0, total_ucode_size = 0; + ssize_t ucode_size = 0, ucode_offset = 0, total_ucode_size = 0; int i, ret = 0; dev_vdbg(audio_avp->dev, "%s", __func__); @@ -296,13 +296,14 @@ static int tegra30_avp_load_ucode(void) } ucode_size = ucode_fw->size; - if (ucode_size <= 0) { + if (ucode_size <= 0 || + ucode_size > avp_ucode_desc[i].max_mem_size) { dev_err(audio_avp->dev, "Invalid ucode size."); ret = -EINVAL; release_firmware(ucode_fw); goto err_param_mem_free; } - dev_vdbg(audio_avp->dev, "%s ucode size = %d bytes", + dev_vdbg(audio_avp->dev, "%s ucode size = %zd bytes", ucode_desc->bin_name, ucode_size); /* Read ucode */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 3879eae7e874..a72bfeee6de2 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -136,6 +136,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, snd_printk(KERN_ERR "cannot memdup\n"); return -ENOMEM; } + INIT_LIST_HEAD(&fp->list); if (fp->nr_rates > MAX_NR_RATES) { kfree(fp); return -EINVAL; @@ -154,15 +155,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { - kfree(fp); - kfree(rate_table); - return err; + goto error; } if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber || fp->altset_idx >= iface->num_altsetting) { - kfree(fp); - kfree(rate_table); - return -EINVAL; + err = -EINVAL; + goto error; } alts = &iface->altsetting[fp->altset_idx]; if (fp->datainterval == 0) @@ -173,6 +171,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, snd_usb_init_pitch(chip, fp->iface, alts, fp); snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); return 0; +error: + list_del(&fp->list); /* unlink for avoiding double-free */ + kfree(fp); + kfree(rate_table); + return err; } /* @@ -239,6 +242,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = 0; fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + INIT_LIST_HEAD(&fp->list); switch (fp->maxpacksize) { case 0x120: @@ -262,6 +266,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp); return err; } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 5b6f7a027c6a..7ddf4a437e4a 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -307,7 +307,9 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, /* * add this endpoint to the chip instance. * if a stream with the same endpoint already exists, append to it. - * if not, create a new pcm stream. + * if not, create a new pcm stream. note, fp is added to the substream + * fmt_list and will be freed on the chip instance release. do not free + * fp or do remove it from the substream fmt_list to avoid double-free. */ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, int stream, @@ -755,6 +757,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; fp->chmap = convert_chmap(num_channels, chconfig, protocol); + INIT_LIST_HEAD(&fp->list); /* some quirks for attributes here */ @@ -799,6 +802,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint); err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { + list_del(&fp->list); /* unlink for avoiding double-free */ kfree(fp->rate_table); kfree(fp->chmap); kfree(fp); |