Merge tag 'for-linus-20190524' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / arch / arm64 / kernel / module.c
index f0f27ae..f32359c 100644 (file)
@@ -56,7 +56,7 @@ void *module_alloc(unsigned long size)
                 * can simply omit this fallback in that case.
                 */
                p = __vmalloc_node_range(size, MODULE_ALIGN, module_alloc_base,
-                               module_alloc_base + SZ_4G, GFP_KERNEL,
+                               module_alloc_base + SZ_2G, GFP_KERNEL,
                                PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
                                __builtin_return_address(0));
 
@@ -96,15 +96,27 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
 {
        s64 sval = do_reloc(op, place, val);
 
+       /*
+        * The ELF psABI for AArch64 documents the 16-bit and 32-bit place
+        * relative relocations as having a range of [-2^15, 2^16) or
+        * [-2^31, 2^32), respectively. However, in order to be able to detect
+        * overflows reliably, we have to choose whether we interpret such
+        * quantities as signed or as unsigned, and stick with it.
+        * The way we organize our address space requires a signed
+        * interpretation of 32-bit relative references, so let's use that
+        * for all R_AARCH64_PRELxx relocations. This means our upper
+        * bound for overflow detection should be Sxx_MAX rather than Uxx_MAX.
+        */
+
        switch (len) {
        case 16:
                *(s16 *)place = sval;
-               if (sval < S16_MIN || sval > U16_MAX)
+               if (sval < S16_MIN || sval > S16_MAX)
                        return -ERANGE;
                break;
        case 32:
                *(s32 *)place = sval;
-               if (sval < S32_MIN || sval > U32_MAX)
+               if (sval < S32_MIN || sval > S32_MAX)
                        return -ERANGE;
                break;
        case 64:
@@ -198,13 +210,12 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, __le32 *place, u64 val,
        return 0;
 }
 
-static int reloc_insn_adrp(struct module *mod, __le32 *place, u64 val)
+static int reloc_insn_adrp(struct module *mod, Elf64_Shdr *sechdrs,
+                          __le32 *place, u64 val)
 {
        u32 insn;
 
-       if (!IS_ENABLED(CONFIG_ARM64_ERRATUM_843419) ||
-           !cpus_have_const_cap(ARM64_WORKAROUND_843419) ||
-           ((u64)place & 0xfff) < 0xff8)
+       if (!is_forbidden_offset_for_adrp(place))
                return reloc_insn_imm(RELOC_OP_PAGE, place, val, 12, 21,
                                      AARCH64_INSN_IMM_ADR);
 
@@ -215,7 +226,7 @@ static int reloc_insn_adrp(struct module *mod, __le32 *place, u64 val)
                insn &= ~BIT(31);
        } else {
                /* out of range for ADR -> emit a veneer */
-               val = module_emit_veneer_for_adrp(mod, place, val & ~0xfff);
+               val = module_emit_veneer_for_adrp(mod, sechdrs, place, val & ~0xfff);
                if (!val)
                        return -ENOEXEC;
                insn = aarch64_insn_gen_branch_imm((u64)place, val,
@@ -368,7 +379,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
                case R_AARCH64_ADR_PREL_PG_HI21_NC:
                        overflow_check = false;
                case R_AARCH64_ADR_PREL_PG_HI21:
-                       ovf = reloc_insn_adrp(me, loc, val);
+                       ovf = reloc_insn_adrp(me, sechdrs, loc, val);
                        if (ovf && ovf != -ERANGE)
                                return ovf;
                        break;
@@ -413,7 +424,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
 
                        if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
                            ovf == -ERANGE) {
-                               val = module_emit_plt_entry(me, loc, &rel[i], sym);
+                               val = module_emit_plt_entry(me, sechdrs, loc, &rel[i], sym);
                                if (!val)
                                        return -ENOEXEC;
                                ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,