* [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel
@ 2018-04-14 20:19 Bhupesh Sharma
2018-04-16 2:30 ` AKASHI Takahiro
0 siblings, 1 reply; 5+ messages in thread
From: Bhupesh Sharma @ 2018-04-14 20:19 UTC (permalink / raw)
To: kexec; +Cc: takahiro.akashi, bhsharma, bhupesh.linux, dyoung, horms
This patch adds the support to supply 'kaslr-seed' to secondary kernel,
when we do a 'kexec warm reboot to another kernel' (although the
behaviour remains the same for the 'kdump' case as well) on arm64
platforms using the 'kexec_load' invocation method.
Lets consider the case where the primary kernel working on the arm64
platform supports kaslr (i.e 'CONFIG_RANDOMIZE_BASE' was set to y and
we have a compliant EFI firmware which supports EFI_RNG_PROTOCOL and
hence can pass a non-zero (valid) seed to the primary kernel).
Now the primary kernel reads the 'kaslr-seed' and wipes it to 0 and
uses the seed value to randomize for e.g. the module base address
offset.
In the case of 'kexec_load' (or even kdump for brevity),
we rely on the user-space kexec-tools to pass an appropriate dtb to the
secondary kernel and since 'kaslr-seed' is wiped to 0 by the primary
kernel, the secondary will essentially work with *nokaslr* as
'kaslr-seed' is set to 0 when it is passed to the secondary kernel.
This can be true even in case the secondary kernel had
'CONFIG_RANDOMIZE_BASE' and 'CONFIG_RANDOMIZE_MODULE_REGION_FULL' set to
y.
This patch addresses this issue by first checking if the device tree
provided by the firmware to the kernel supports the 'kaslr-seed'
property and verifies that it is really wiped to 0. If this condition is
met, it fixes up the 'kaslr-seed' property by using the getrandom()
syscall to get a suitable random number.
I verified this patch on my Qualcomm arm64 board and here are some test
results:
1. Ensure that the primary kernel is boot'ed with 'kaslr-seed'
dts property and it is really wiped to 0:
[root@qualcomm-amberwing]# dtc -I dtb -O dts /sys/firmware/fdt | grep -A 10 -i chosen
chosen {
kaslr-seed = <0x0 0x0>;
...
}
2. Now issue 'kexec_load' to load the secondary kernel (let's assume
that we are using the same kernel as the secondary kernel):
# kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname
-r`.img --reuse-cmdline -d
3. Issue 'kexec -e' to warm boot to the secondary:
# kexec -e
4. Now after the secondary boots, confirm that the load address of the
modules is randomized in every successive boot:
[root@qualcomm-amberwing]# cat /proc/modules
sunrpc 524288 1 - Live 0xffff0307db190000
vfat 262144 1 - Live 0xffff0307db110000
fat 262144 1 vfat, Live 0xffff0307db090000
crc32_ce 262144 0 - Live 0xffff0307d8c70000
...
Signed-off-by: Bhupesh Sharma <bhsharma@redhat.com>
---
kexec/arch/arm64/kexec-arm64.c | 135 +++++++++++++++++++++++++++++------------
1 file changed, 97 insertions(+), 38 deletions(-)
diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c
index 62f37585b788..2ab11227447a 100644
--- a/kexec/arch/arm64/kexec-arm64.c
+++ b/kexec/arch/arm64/kexec-arm64.c
@@ -15,6 +15,11 @@
#include <linux/elf-em.h>
#include <elf.h>
+#include <unistd.h>
+#include <syscall.h>
+#include <errno.h>
+#include <linux/random.h>
+
#include "kexec.h"
#include "kexec-arm64.h"
#include "crashdump.h"
@@ -392,11 +397,13 @@ static int fdt_setprop_range(void *fdt, int nodeoffset,
static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
{
uint32_t address_cells, size_cells;
- int range_len;
- int nodeoffset;
+ uint64_t fdt_val64;
+ uint64_t *prop;
char *new_buf = NULL;
+ int len, range_len;
+ int nodeoffset;
int new_size;
- int result;
+ int result, kaslr_seed;
result = fdt_check_header(dtb->buf);
@@ -407,47 +414,99 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
result = set_bootargs(dtb, command_line);
- if (on_crash) {
- /* determine #address-cells and #size-cells */
- result = get_cells_size(dtb->buf, &address_cells, &size_cells);
- if (result) {
- fprintf(stderr,
- "kexec: cannot determine cells-size.\n");
- result = -EINVAL;
- goto on_error;
- }
+ /* determine #address-cells and #size-cells */
+ result = get_cells_size(dtb->buf, &address_cells, &size_cells);
+ if (result) {
+ fprintf(stderr, "kexec: cannot determine cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
- if (!cells_size_fitted(address_cells, size_cells,
- &elfcorehdr_mem)) {
- fprintf(stderr,
- "kexec: elfcorehdr doesn't fit cells-size.\n");
+ if (!cells_size_fitted(address_cells, size_cells,
+ &elfcorehdr_mem)) {
+ fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ if (!cells_size_fitted(address_cells, size_cells,
+ &crash_reserved_mem)) {
+ fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
+
+ /* duplicate dt blob */
+ range_len = sizeof(uint32_t) * (address_cells + size_cells);
+ new_size = fdt_totalsize(dtb->buf)
+ + fdt_prop_len(PROP_ELFCOREHDR, range_len)
+ + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len);
+
+ new_buf = xmalloc(new_size);
+ result = fdt_open_into(dtb->buf, new_buf, new_size);
+ if (result) {
+ dbgprintf("%s: fdt_open_into failed: %s\n", __func__,
+ fdt_strerror(result));
+ result = -ENOSPC;
+ goto on_error;
+ }
+
+ /* fixup 'kaslr-seed' with a random value, if supported */
+ nodeoffset = fdt_path_offset(new_buf, "/chosen");
+ prop = fdt_getprop_w(new_buf, nodeoffset,
+ "kaslr-seed", &len);
+ if (!prop || len != sizeof(uint64_t)) {
+ dbgprintf("%s: no kaslr-seed found: %s\n",
+ __func__, fdt_strerror(result));
+ /* for kexec warm reboot case, we don't need to fixup
+ * other dtb properties
+ */
+ if (!on_crash)
+ goto free_new_buf;
+
+ } else {
+ kaslr_seed = fdt64_to_cpu(*prop);
+
+ /* kaslr_seed must be wiped clean by primary
+ * kernel during boot
+ */
+ if (kaslr_seed != 0) {
+ dbgprintf("%s: kaslr-seed is not wiped to 0.\n",
+ __func__);
result = -EINVAL;
goto on_error;
}
- if (!cells_size_fitted(address_cells, size_cells,
- &crash_reserved_mem)) {
- fprintf(stderr,
- "kexec: usable memory range doesn't fit cells-size.\n");
+ /*
+ * Invoke the getrandom system call with
+ * GRND_NONBLOCK, to make sure we
+ * have a valid random seed to pass to the
+ * secondary kernel.
+ */
+ result = syscall(SYS_getrandom, &fdt_val64,
+ sizeof(fdt_val64),
+ GRND_NONBLOCK);
+
+ if(result == -1) {
+ dbgprintf("%s: Reading random bytes failed.\n",
+ __func__);
result = -EINVAL;
goto on_error;
}
- /* duplicate dt blob */
- range_len = sizeof(uint32_t) * (address_cells + size_cells);
- new_size = fdt_totalsize(dtb->buf)
- + fdt_prop_len(PROP_ELFCOREHDR, range_len)
- + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len);
-
- new_buf = xmalloc(new_size);
- result = fdt_open_into(dtb->buf, new_buf, new_size);
+ nodeoffset = fdt_path_offset(new_buf, "/chosen");
+ result = fdt_setprop_inplace(new_buf,
+ nodeoffset, "kaslr-seed",
+ &fdt_val64, sizeof(fdt_val64));
if (result) {
- dbgprintf("%s: fdt_open_into failed: %s\n", __func__,
- fdt_strerror(result));
- result = -ENOSPC;
+ dbgprintf("%s: fdt_setprop failed: %s\n",
+ __func__, fdt_strerror(result));
+ result = -EINVAL;
goto on_error;
}
+ }
+ if (on_crash) {
/* add linux,elfcorehdr */
nodeoffset = fdt_path_offset(new_buf, "/chosen");
result = fdt_setprop_range(new_buf, nodeoffset,
@@ -455,7 +514,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
address_cells, size_cells);
if (result) {
dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
- fdt_strerror(result));
+ fdt_strerror(result));
result = -EINVAL;
goto on_error;
}
@@ -467,23 +526,23 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
address_cells, size_cells);
if (result) {
dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
- fdt_strerror(result));
+ fdt_strerror(result));
result = -EINVAL;
goto on_error;
}
-
- fdt_pack(new_buf);
- dtb->buf = new_buf;
- dtb->size = fdt_totalsize(new_buf);
}
- dump_reservemap(dtb);
+ fdt_pack(new_buf);
+ dtb->buf = new_buf;
+ dtb->size = fdt_totalsize(new_buf);
+ dump_reservemap(dtb);
return result;
on_error:
fprintf(stderr, "kexec: %s failed.\n", __func__);
+free_new_buf:
if (new_buf)
free(new_buf);
--
2.7.4
_______________________________________________
kexec mailing list
kexec@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kexec
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel 2018-04-14 20:19 [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel Bhupesh Sharma @ 2018-04-16 2:30 ` AKASHI Takahiro 2018-04-16 19:05 ` Bhupesh Sharma 0 siblings, 1 reply; 5+ messages in thread From: AKASHI Takahiro @ 2018-04-16 2:30 UTC (permalink / raw) To: Bhupesh Sharma; +Cc: dyoung, bhupesh.linux, kexec, horms Bhupesh, On Sun, Apr 15, 2018 at 01:49:40AM +0530, Bhupesh Sharma wrote: > This patch adds the support to supply 'kaslr-seed' to secondary kernel, > when we do a 'kexec warm reboot to another kernel' (although the > behaviour remains the same for the 'kdump' case as well) on arm64 > platforms using the 'kexec_load' invocation method. > > Lets consider the case where the primary kernel working on the arm64 > platform supports kaslr (i.e 'CONFIG_RANDOMIZE_BASE' was set to y and > we have a compliant EFI firmware which supports EFI_RNG_PROTOCOL and > hence can pass a non-zero (valid) seed to the primary kernel). > > Now the primary kernel reads the 'kaslr-seed' and wipes it to 0 and > uses the seed value to randomize for e.g. the module base address > offset. > > In the case of 'kexec_load' (or even kdump for brevity), > we rely on the user-space kexec-tools to pass an appropriate dtb to the > secondary kernel and since 'kaslr-seed' is wiped to 0 by the primary > kernel, the secondary will essentially work with *nokaslr* as > 'kaslr-seed' is set to 0 when it is passed to the secondary kernel. > > This can be true even in case the secondary kernel had > 'CONFIG_RANDOMIZE_BASE' and 'CONFIG_RANDOMIZE_MODULE_REGION_FULL' set to > y. > > This patch addresses this issue by first checking if the device tree > provided by the firmware to the kernel supports the 'kaslr-seed' > property and verifies that it is really wiped to 0. If this condition is > met, it fixes up the 'kaslr-seed' property by using the getrandom() > syscall to get a suitable random number. > > I verified this patch on my Qualcomm arm64 board and here are some test > results: > > 1. Ensure that the primary kernel is boot'ed with 'kaslr-seed' > dts property and it is really wiped to 0: > > [root@qualcomm-amberwing]# dtc -I dtb -O dts /sys/firmware/fdt | grep -A 10 -i chosen > chosen { > kaslr-seed = <0x0 0x0>; > ... > } > > 2. Now issue 'kexec_load' to load the secondary kernel (let's assume > that we are using the same kernel as the secondary kernel): > # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname > -r`.img --reuse-cmdline -d > > 3. Issue 'kexec -e' to warm boot to the secondary: > # kexec -e > > 4. Now after the secondary boots, confirm that the load address of the > modules is randomized in every successive boot: > > [root@qualcomm-amberwing]# cat /proc/modules > sunrpc 524288 1 - Live 0xffff0307db190000 > vfat 262144 1 - Live 0xffff0307db110000 > fat 262144 1 vfat, Live 0xffff0307db090000 > crc32_ce 262144 0 - Live 0xffff0307d8c70000 > ... > > Signed-off-by: Bhupesh Sharma <bhsharma@redhat.com> > --- > kexec/arch/arm64/kexec-arm64.c | 135 +++++++++++++++++++++++++++++------------ > 1 file changed, 97 insertions(+), 38 deletions(-) > > diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c > index 62f37585b788..2ab11227447a 100644 > --- a/kexec/arch/arm64/kexec-arm64.c > +++ b/kexec/arch/arm64/kexec-arm64.c > @@ -15,6 +15,11 @@ > #include <linux/elf-em.h> > #include <elf.h> > > +#include <unistd.h> > +#include <syscall.h> > +#include <errno.h> > +#include <linux/random.h> > + > #include "kexec.h" > #include "kexec-arm64.h" > #include "crashdump.h" > @@ -392,11 +397,13 @@ static int fdt_setprop_range(void *fdt, int nodeoffset, > static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) > { > uint32_t address_cells, size_cells; > - int range_len; > - int nodeoffset; > + uint64_t fdt_val64; > + uint64_t *prop; > char *new_buf = NULL; > + int len, range_len; > + int nodeoffset; > int new_size; > - int result; > + int result, kaslr_seed; > > result = fdt_check_header(dtb->buf); > > @@ -407,47 +414,99 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) > > result = set_bootargs(dtb, command_line); > > - if (on_crash) { > - /* determine #address-cells and #size-cells */ > - result = get_cells_size(dtb->buf, &address_cells, &size_cells); > - if (result) { > - fprintf(stderr, > - "kexec: cannot determine cells-size.\n"); > - result = -EINVAL; > - goto on_error; > - } > + /* determine #address-cells and #size-cells */ > + result = get_cells_size(dtb->buf, &address_cells, &size_cells); > + if (result) { > + fprintf(stderr, "kexec: cannot determine cells-size.\n"); > + result = -EINVAL; > + goto on_error; > + } > > - if (!cells_size_fitted(address_cells, size_cells, > - &elfcorehdr_mem)) { > - fprintf(stderr, > - "kexec: elfcorehdr doesn't fit cells-size.\n"); > + if (!cells_size_fitted(address_cells, size_cells, > + &elfcorehdr_mem)) { > + fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n"); > + result = -EINVAL; > + goto on_error; > + } > + > + if (!cells_size_fitted(address_cells, size_cells, > + &crash_reserved_mem)) { > + fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); > + result = -EINVAL; > + goto on_error; > + } > + > + /* duplicate dt blob */ > + range_len = sizeof(uint32_t) * (address_cells + size_cells); > + new_size = fdt_totalsize(dtb->buf) > + + fdt_prop_len(PROP_ELFCOREHDR, range_len) > + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); > + > + new_buf = xmalloc(new_size); > + result = fdt_open_into(dtb->buf, new_buf, new_size); > + if (result) { > + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, > + fdt_strerror(result)); > + result = -ENOSPC; > + goto on_error; > + } > + > + /* fixup 'kaslr-seed' with a random value, if supported */ > + nodeoffset = fdt_path_offset(new_buf, "/chosen"); > + prop = fdt_getprop_w(new_buf, nodeoffset, > + "kaslr-seed", &len); > + if (!prop || len != sizeof(uint64_t)) { Do we need this check? Please note that people are allowed to provide a dtb explicitly at command line and may want to use kexec as bootloader on no-uefi platform. > + dbgprintf("%s: no kaslr-seed found: %s\n", > + __func__, fdt_strerror(result)); > + /* for kexec warm reboot case, we don't need to fixup > + * other dtb properties > + */ > + if (!on_crash) > + goto free_new_buf; > + > + } else { > + kaslr_seed = fdt64_to_cpu(*prop); > + > + /* kaslr_seed must be wiped clean by primary > + * kernel during boot > + */ > + if (kaslr_seed != 0) { > + dbgprintf("%s: kaslr-seed is not wiped to 0.\n", > + __func__); Ditto If this is a user-provided dtb, there is no reason to reject it. I think all what is needed here is to feed a *sane* dtb to kexec. So along with the comment above, it may be useful to add a command line option for turning on or off "kaslr-seed". > result = -EINVAL; > goto on_error; > } > > - if (!cells_size_fitted(address_cells, size_cells, > - &crash_reserved_mem)) { > - fprintf(stderr, > - "kexec: usable memory range doesn't fit cells-size.\n"); > + /* > + * Invoke the getrandom system call with > + * GRND_NONBLOCK, to make sure we > + * have a valid random seed to pass to the > + * secondary kernel. > + */ > + result = syscall(SYS_getrandom, &fdt_val64, > + sizeof(fdt_val64), > + GRND_NONBLOCK); Why do you use syscall() here? > + > + if(result == -1) { > + dbgprintf("%s: Reading random bytes failed.\n", > + __func__); > result = -EINVAL; > goto on_error; > } > > - /* duplicate dt blob */ > - range_len = sizeof(uint32_t) * (address_cells + size_cells); > - new_size = fdt_totalsize(dtb->buf) > - + fdt_prop_len(PROP_ELFCOREHDR, range_len) > - + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); > - > - new_buf = xmalloc(new_size); > - result = fdt_open_into(dtb->buf, new_buf, new_size); > + nodeoffset = fdt_path_offset(new_buf, "/chosen"); > + result = fdt_setprop_inplace(new_buf, > + nodeoffset, "kaslr-seed", > + &fdt_val64, sizeof(fdt_val64)); > if (result) { > - dbgprintf("%s: fdt_open_into failed: %s\n", __func__, > - fdt_strerror(result)); > - result = -ENOSPC; > + dbgprintf("%s: fdt_setprop failed: %s\n", > + __func__, fdt_strerror(result)); > + result = -EINVAL; > goto on_error; > } > + } > > + if (on_crash) { > /* add linux,elfcorehdr */ > nodeoffset = fdt_path_offset(new_buf, "/chosen"); > result = fdt_setprop_range(new_buf, nodeoffset, > @@ -455,7 +514,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) > address_cells, size_cells); > if (result) { > dbgprintf("%s: fdt_setprop failed: %s\n", __func__, > - fdt_strerror(result)); > + fdt_strerror(result)); > result = -EINVAL; > goto on_error; > } > @@ -467,23 +526,23 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) > address_cells, size_cells); > if (result) { > dbgprintf("%s: fdt_setprop failed: %s\n", __func__, > - fdt_strerror(result)); > + fdt_strerror(result)); > result = -EINVAL; > goto on_error; > } > - > - fdt_pack(new_buf); > - dtb->buf = new_buf; > - dtb->size = fdt_totalsize(new_buf); > } > > - dump_reservemap(dtb); > + fdt_pack(new_buf); > + dtb->buf = new_buf; > + dtb->size = fdt_totalsize(new_buf); > > + dump_reservemap(dtb); > > return result; > > on_error: > fprintf(stderr, "kexec: %s failed.\n", __func__); > +free_new_buf: Well, technically correct, but it looks odd as it is placed on *error* return path. You also miss dump_reservemap(). Thanks, -Takahiro AKASHI > if (new_buf) > free(new_buf); > > -- > 2.7.4 > _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel 2018-04-16 2:30 ` AKASHI Takahiro @ 2018-04-16 19:05 ` Bhupesh Sharma 2018-04-23 11:05 ` Bhupesh Sharma 0 siblings, 1 reply; 5+ messages in thread From: Bhupesh Sharma @ 2018-04-16 19:05 UTC (permalink / raw) To: AKASHI Takahiro, Bhupesh Sharma, kexec, Bhupesh SHARMA, Dave Young, Simon Horman Hello Akashi, Thanks for the review comments. On Mon, Apr 16, 2018 at 8:00 AM, AKASHI Takahiro <takahiro.akashi@linaro.org> wrote: > Bhupesh, > > On Sun, Apr 15, 2018 at 01:49:40AM +0530, Bhupesh Sharma wrote: >> This patch adds the support to supply 'kaslr-seed' to secondary kernel, >> when we do a 'kexec warm reboot to another kernel' (although the >> behaviour remains the same for the 'kdump' case as well) on arm64 >> platforms using the 'kexec_load' invocation method. >> >> Lets consider the case where the primary kernel working on the arm64 >> platform supports kaslr (i.e 'CONFIG_RANDOMIZE_BASE' was set to y and >> we have a compliant EFI firmware which supports EFI_RNG_PROTOCOL and >> hence can pass a non-zero (valid) seed to the primary kernel). >> >> Now the primary kernel reads the 'kaslr-seed' and wipes it to 0 and >> uses the seed value to randomize for e.g. the module base address >> offset. >> >> In the case of 'kexec_load' (or even kdump for brevity), >> we rely on the user-space kexec-tools to pass an appropriate dtb to the >> secondary kernel and since 'kaslr-seed' is wiped to 0 by the primary >> kernel, the secondary will essentially work with *nokaslr* as >> 'kaslr-seed' is set to 0 when it is passed to the secondary kernel. >> >> This can be true even in case the secondary kernel had >> 'CONFIG_RANDOMIZE_BASE' and 'CONFIG_RANDOMIZE_MODULE_REGION_FULL' set to >> y. >> >> This patch addresses this issue by first checking if the device tree >> provided by the firmware to the kernel supports the 'kaslr-seed' >> property and verifies that it is really wiped to 0. If this condition is >> met, it fixes up the 'kaslr-seed' property by using the getrandom() >> syscall to get a suitable random number. >> >> I verified this patch on my Qualcomm arm64 board and here are some test >> results: >> >> 1. Ensure that the primary kernel is boot'ed with 'kaslr-seed' >> dts property and it is really wiped to 0: >> >> [root@qualcomm-amberwing]# dtc -I dtb -O dts /sys/firmware/fdt | grep -A 10 -i chosen >> chosen { >> kaslr-seed = <0x0 0x0>; >> ... >> } >> >> 2. Now issue 'kexec_load' to load the secondary kernel (let's assume >> that we are using the same kernel as the secondary kernel): >> # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname >> -r`.img --reuse-cmdline -d >> >> 3. Issue 'kexec -e' to warm boot to the secondary: >> # kexec -e >> >> 4. Now after the secondary boots, confirm that the load address of the >> modules is randomized in every successive boot: >> >> [root@qualcomm-amberwing]# cat /proc/modules >> sunrpc 524288 1 - Live 0xffff0307db190000 >> vfat 262144 1 - Live 0xffff0307db110000 >> fat 262144 1 vfat, Live 0xffff0307db090000 >> crc32_ce 262144 0 - Live 0xffff0307d8c70000 >> ... >> >> Signed-off-by: Bhupesh Sharma <bhsharma@redhat.com> >> --- >> kexec/arch/arm64/kexec-arm64.c | 135 +++++++++++++++++++++++++++++------------ >> 1 file changed, 97 insertions(+), 38 deletions(-) >> >> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c >> index 62f37585b788..2ab11227447a 100644 >> --- a/kexec/arch/arm64/kexec-arm64.c >> +++ b/kexec/arch/arm64/kexec-arm64.c >> @@ -15,6 +15,11 @@ >> #include <linux/elf-em.h> >> #include <elf.h> >> >> +#include <unistd.h> >> +#include <syscall.h> >> +#include <errno.h> >> +#include <linux/random.h> >> + >> #include "kexec.h" >> #include "kexec-arm64.h" >> #include "crashdump.h" >> @@ -392,11 +397,13 @@ static int fdt_setprop_range(void *fdt, int nodeoffset, >> static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >> { >> uint32_t address_cells, size_cells; >> - int range_len; >> - int nodeoffset; >> + uint64_t fdt_val64; >> + uint64_t *prop; >> char *new_buf = NULL; >> + int len, range_len; >> + int nodeoffset; >> int new_size; >> - int result; >> + int result, kaslr_seed; >> >> result = fdt_check_header(dtb->buf); >> >> @@ -407,47 +414,99 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >> >> result = set_bootargs(dtb, command_line); >> >> - if (on_crash) { >> - /* determine #address-cells and #size-cells */ >> - result = get_cells_size(dtb->buf, &address_cells, &size_cells); >> - if (result) { >> - fprintf(stderr, >> - "kexec: cannot determine cells-size.\n"); >> - result = -EINVAL; >> - goto on_error; >> - } >> + /* determine #address-cells and #size-cells */ >> + result = get_cells_size(dtb->buf, &address_cells, &size_cells); >> + if (result) { >> + fprintf(stderr, "kexec: cannot determine cells-size.\n"); >> + result = -EINVAL; >> + goto on_error; >> + } >> >> - if (!cells_size_fitted(address_cells, size_cells, >> - &elfcorehdr_mem)) { >> - fprintf(stderr, >> - "kexec: elfcorehdr doesn't fit cells-size.\n"); >> + if (!cells_size_fitted(address_cells, size_cells, >> + &elfcorehdr_mem)) { >> + fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n"); >> + result = -EINVAL; >> + goto on_error; >> + } >> + >> + if (!cells_size_fitted(address_cells, size_cells, >> + &crash_reserved_mem)) { >> + fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); >> + result = -EINVAL; >> + goto on_error; >> + } >> + >> + /* duplicate dt blob */ >> + range_len = sizeof(uint32_t) * (address_cells + size_cells); >> + new_size = fdt_totalsize(dtb->buf) >> + + fdt_prop_len(PROP_ELFCOREHDR, range_len) >> + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); >> + >> + new_buf = xmalloc(new_size); >> + result = fdt_open_into(dtb->buf, new_buf, new_size); >> + if (result) { >> + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, >> + fdt_strerror(result)); >> + result = -ENOSPC; >> + goto on_error; >> + } >> + >> + /* fixup 'kaslr-seed' with a random value, if supported */ >> + nodeoffset = fdt_path_offset(new_buf, "/chosen"); >> + prop = fdt_getprop_w(new_buf, nodeoffset, >> + "kaslr-seed", &len); >> + if (!prop || len != sizeof(uint64_t)) { > > Do we need this check? > Please note that people are allowed to provide a dtb explicitly > at command line and may want to use kexec as bootloader on > no-uefi platform. I agree. Lets look at the original behaviour (before this patch). We used to unpack and fixup dtb properties and then pack it back when 'on_crash' was true (i.e only for the kdump case). In case of 'kexec' we do not fixup the dtb (as per my understanding, please correct me if I am wrong here). With this patch I wanted the dtb's kaslr-seed property to be fixed-up (if its supported and is wiped to 0 by the primary kernel). But this check is harmless in case we don't find the 'kaslr-seed' property in the dtb (for e.g. on non-uefi/u-boot based arm64 platforms). In case the property is not seen in the dtb, we just print a debug message (if '-d' flag was used to launch kexec) and proceed to perform fixup of other dtb properties (like 'linux, usable-memory-range) in case 'on_crash' is true (i.e. 'kexec -p' use case). In the 'kexec -l' case since we don't do any other fixups in the original approach so we retain the same behavior here. >> + dbgprintf("%s: no kaslr-seed found: %s\n", >> + __func__, fdt_strerror(result)); >> + /* for kexec warm reboot case, we don't need to fixup >> + * other dtb properties >> + */ >> + if (!on_crash) >> + goto free_new_buf; >> + >> + } else { >> + kaslr_seed = fdt64_to_cpu(*prop); >> + >> + /* kaslr_seed must be wiped clean by primary >> + * kernel during boot >> + */ >> + if (kaslr_seed != 0) { >> + dbgprintf("%s: kaslr-seed is not wiped to 0.\n", >> + __func__); > > Ditto > If this is a user-provided dtb, there is no reason to reject it. > I think all what is needed here is to feed a *sane* dtb to kexec. > > So along with the comment above, it may be useful to add a command line > option for turning on or off "kaslr-seed". Please see my comments above. Since the 'kaslr-seed' property just needs to be read from the dtb, we probably don't need a separate command line option for the same as we already have nokaslr available. If we want the secondary kernel to boot with *nokaslr*, we can pass the same to the secondary via the command line arguments. BTW, I also tried the behaviour with --dtb being passed while invoking the 'kexec -l' with the patch in question and the resulting behaviour is correct, i.e. we see that if the secondary kernel supports CONFIG_RANDOMIZE_BASE=y, we get the resulting randomization in module load address (for e.g.): # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname -r`.img --command-line="$(cat /proc/cmdline)" --dtb /sys/firmware/fdt -d # kexec -e On successive kexec warm reboots I see that '/proc/kallsyms' and '/proc/modules' have randomized addresses. >> result = -EINVAL; >> goto on_error; >> } >> >> - if (!cells_size_fitted(address_cells, size_cells, >> - &crash_reserved_mem)) { >> - fprintf(stderr, >> - "kexec: usable memory range doesn't fit cells-size.\n"); >> + /* >> + * Invoke the getrandom system call with >> + * GRND_NONBLOCK, to make sure we >> + * have a valid random seed to pass to the >> + * secondary kernel. >> + */ >> + result = syscall(SYS_getrandom, &fdt_val64, >> + sizeof(fdt_val64), >> + GRND_NONBLOCK); > > Why do you use syscall() here? I found that the standard way to invokde a getrandom() call is via a SYSCALL (please see <https://nikmav.blogspot.in/2016/10/random-generator-linux.html>). >> + >> + if(result == -1) { >> + dbgprintf("%s: Reading random bytes failed.\n", >> + __func__); >> result = -EINVAL; >> goto on_error; >> } >> >> - /* duplicate dt blob */ >> - range_len = sizeof(uint32_t) * (address_cells + size_cells); >> - new_size = fdt_totalsize(dtb->buf) >> - + fdt_prop_len(PROP_ELFCOREHDR, range_len) >> - + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); >> - >> - new_buf = xmalloc(new_size); >> - result = fdt_open_into(dtb->buf, new_buf, new_size); >> + nodeoffset = fdt_path_offset(new_buf, "/chosen"); >> + result = fdt_setprop_inplace(new_buf, >> + nodeoffset, "kaslr-seed", >> + &fdt_val64, sizeof(fdt_val64)); >> if (result) { >> - dbgprintf("%s: fdt_open_into failed: %s\n", __func__, >> - fdt_strerror(result)); >> - result = -ENOSPC; >> + dbgprintf("%s: fdt_setprop failed: %s\n", >> + __func__, fdt_strerror(result)); >> + result = -EINVAL; >> goto on_error; >> } >> + } >> >> + if (on_crash) { >> /* add linux,elfcorehdr */ >> nodeoffset = fdt_path_offset(new_buf, "/chosen"); >> result = fdt_setprop_range(new_buf, nodeoffset, >> @@ -455,7 +514,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >> address_cells, size_cells); >> if (result) { >> dbgprintf("%s: fdt_setprop failed: %s\n", __func__, >> - fdt_strerror(result)); >> + fdt_strerror(result)); >> result = -EINVAL; >> goto on_error; >> } >> @@ -467,23 +526,23 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >> address_cells, size_cells); >> if (result) { >> dbgprintf("%s: fdt_setprop failed: %s\n", __func__, >> - fdt_strerror(result)); >> + fdt_strerror(result)); >> result = -EINVAL; >> goto on_error; >> } >> - >> - fdt_pack(new_buf); >> - dtb->buf = new_buf; >> - dtb->size = fdt_totalsize(new_buf); >> } >> >> - dump_reservemap(dtb); >> + fdt_pack(new_buf); >> + dtb->buf = new_buf; >> + dtb->size = fdt_totalsize(new_buf); >> >> + dump_reservemap(dtb); >> >> return result; >> >> on_error: >> fprintf(stderr, "kexec: %s failed.\n", __func__); >> +free_new_buf: > > Well, technically correct, but it looks odd as it is placed > on *error* return path. I agree. I was not too comfortable with placing this label here. I will try to find a better approach in v2. > You also miss dump_reservemap(). Oops. Sure will fix this in v2. Regards, Bhupesh > Thanks, > -Takahiro AKASHI > >> if (new_buf) >> free(new_buf); >> >> -- >> 2.7.4 >> _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel 2018-04-16 19:05 ` Bhupesh Sharma @ 2018-04-23 11:05 ` Bhupesh Sharma 2018-04-24 9:40 ` AKASHI, Takahiro 0 siblings, 1 reply; 5+ messages in thread From: Bhupesh Sharma @ 2018-04-23 11:05 UTC (permalink / raw) To: AKASHI Takahiro, Bhupesh Sharma, kexec, Bhupesh SHARMA, Dave Young, Simon Horman Hello Akashi, On Tue, Apr 17, 2018 at 12:35 AM, Bhupesh Sharma <bhsharma@redhat.com> wrote: > Hello Akashi, > > Thanks for the review comments. > > On Mon, Apr 16, 2018 at 8:00 AM, AKASHI Takahiro > <takahiro.akashi@linaro.org> wrote: >> Bhupesh, >> >> On Sun, Apr 15, 2018 at 01:49:40AM +0530, Bhupesh Sharma wrote: >>> This patch adds the support to supply 'kaslr-seed' to secondary kernel, >>> when we do a 'kexec warm reboot to another kernel' (although the >>> behaviour remains the same for the 'kdump' case as well) on arm64 >>> platforms using the 'kexec_load' invocation method. >>> >>> Lets consider the case where the primary kernel working on the arm64 >>> platform supports kaslr (i.e 'CONFIG_RANDOMIZE_BASE' was set to y and >>> we have a compliant EFI firmware which supports EFI_RNG_PROTOCOL and >>> hence can pass a non-zero (valid) seed to the primary kernel). >>> >>> Now the primary kernel reads the 'kaslr-seed' and wipes it to 0 and >>> uses the seed value to randomize for e.g. the module base address >>> offset. >>> >>> In the case of 'kexec_load' (or even kdump for brevity), >>> we rely on the user-space kexec-tools to pass an appropriate dtb to the >>> secondary kernel and since 'kaslr-seed' is wiped to 0 by the primary >>> kernel, the secondary will essentially work with *nokaslr* as >>> 'kaslr-seed' is set to 0 when it is passed to the secondary kernel. >>> >>> This can be true even in case the secondary kernel had >>> 'CONFIG_RANDOMIZE_BASE' and 'CONFIG_RANDOMIZE_MODULE_REGION_FULL' set to >>> y. >>> >>> This patch addresses this issue by first checking if the device tree >>> provided by the firmware to the kernel supports the 'kaslr-seed' >>> property and verifies that it is really wiped to 0. If this condition is >>> met, it fixes up the 'kaslr-seed' property by using the getrandom() >>> syscall to get a suitable random number. >>> >>> I verified this patch on my Qualcomm arm64 board and here are some test >>> results: >>> >>> 1. Ensure that the primary kernel is boot'ed with 'kaslr-seed' >>> dts property and it is really wiped to 0: >>> >>> [root@qualcomm-amberwing]# dtc -I dtb -O dts /sys/firmware/fdt | grep -A 10 -i chosen >>> chosen { >>> kaslr-seed = <0x0 0x0>; >>> ... >>> } >>> >>> 2. Now issue 'kexec_load' to load the secondary kernel (let's assume >>> that we are using the same kernel as the secondary kernel): >>> # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname >>> -r`.img --reuse-cmdline -d >>> >>> 3. Issue 'kexec -e' to warm boot to the secondary: >>> # kexec -e >>> >>> 4. Now after the secondary boots, confirm that the load address of the >>> modules is randomized in every successive boot: >>> >>> [root@qualcomm-amberwing]# cat /proc/modules >>> sunrpc 524288 1 - Live 0xffff0307db190000 >>> vfat 262144 1 - Live 0xffff0307db110000 >>> fat 262144 1 vfat, Live 0xffff0307db090000 >>> crc32_ce 262144 0 - Live 0xffff0307d8c70000 >>> ... >>> >>> Signed-off-by: Bhupesh Sharma <bhsharma@redhat.com> >>> --- >>> kexec/arch/arm64/kexec-arm64.c | 135 +++++++++++++++++++++++++++++------------ >>> 1 file changed, 97 insertions(+), 38 deletions(-) >>> >>> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c >>> index 62f37585b788..2ab11227447a 100644 >>> --- a/kexec/arch/arm64/kexec-arm64.c >>> +++ b/kexec/arch/arm64/kexec-arm64.c >>> @@ -15,6 +15,11 @@ >>> #include <linux/elf-em.h> >>> #include <elf.h> >>> >>> +#include <unistd.h> >>> +#include <syscall.h> >>> +#include <errno.h> >>> +#include <linux/random.h> >>> + >>> #include "kexec.h" >>> #include "kexec-arm64.h" >>> #include "crashdump.h" >>> @@ -392,11 +397,13 @@ static int fdt_setprop_range(void *fdt, int nodeoffset, >>> static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>> { >>> uint32_t address_cells, size_cells; >>> - int range_len; >>> - int nodeoffset; >>> + uint64_t fdt_val64; >>> + uint64_t *prop; >>> char *new_buf = NULL; >>> + int len, range_len; >>> + int nodeoffset; >>> int new_size; >>> - int result; >>> + int result, kaslr_seed; >>> >>> result = fdt_check_header(dtb->buf); >>> >>> @@ -407,47 +414,99 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>> >>> result = set_bootargs(dtb, command_line); >>> >>> - if (on_crash) { >>> - /* determine #address-cells and #size-cells */ >>> - result = get_cells_size(dtb->buf, &address_cells, &size_cells); >>> - if (result) { >>> - fprintf(stderr, >>> - "kexec: cannot determine cells-size.\n"); >>> - result = -EINVAL; >>> - goto on_error; >>> - } >>> + /* determine #address-cells and #size-cells */ >>> + result = get_cells_size(dtb->buf, &address_cells, &size_cells); >>> + if (result) { >>> + fprintf(stderr, "kexec: cannot determine cells-size.\n"); >>> + result = -EINVAL; >>> + goto on_error; >>> + } >>> >>> - if (!cells_size_fitted(address_cells, size_cells, >>> - &elfcorehdr_mem)) { >>> - fprintf(stderr, >>> - "kexec: elfcorehdr doesn't fit cells-size.\n"); >>> + if (!cells_size_fitted(address_cells, size_cells, >>> + &elfcorehdr_mem)) { >>> + fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n"); >>> + result = -EINVAL; >>> + goto on_error; >>> + } >>> + >>> + if (!cells_size_fitted(address_cells, size_cells, >>> + &crash_reserved_mem)) { >>> + fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); >>> + result = -EINVAL; >>> + goto on_error; >>> + } >>> + >>> + /* duplicate dt blob */ >>> + range_len = sizeof(uint32_t) * (address_cells + size_cells); >>> + new_size = fdt_totalsize(dtb->buf) >>> + + fdt_prop_len(PROP_ELFCOREHDR, range_len) >>> + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); >>> + >>> + new_buf = xmalloc(new_size); >>> + result = fdt_open_into(dtb->buf, new_buf, new_size); >>> + if (result) { >>> + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, >>> + fdt_strerror(result)); >>> + result = -ENOSPC; >>> + goto on_error; >>> + } >>> + >>> + /* fixup 'kaslr-seed' with a random value, if supported */ >>> + nodeoffset = fdt_path_offset(new_buf, "/chosen"); >>> + prop = fdt_getprop_w(new_buf, nodeoffset, >>> + "kaslr-seed", &len); >>> + if (!prop || len != sizeof(uint64_t)) { >> >> Do we need this check? >> Please note that people are allowed to provide a dtb explicitly >> at command line and may want to use kexec as bootloader on >> no-uefi platform. > > I agree. Lets look at the original behaviour (before this patch). We > used to unpack and fixup dtb properties and then pack it back when > 'on_crash' was true (i.e only for the kdump case). In case of 'kexec' > we do not fixup the dtb (as per my understanding, please correct me if > I am wrong here). > > With this patch I wanted the dtb's kaslr-seed property to be fixed-up > (if its supported and is wiped to 0 by the primary kernel). But this > check is harmless in case we don't find the 'kaslr-seed' property in > the dtb (for e.g. on non-uefi/u-boot based arm64 platforms). > > In case the property is not seen in the dtb, we just print a debug > message (if '-d' flag was used to launch kexec) and proceed to perform > fixup of other dtb properties (like 'linux, usable-memory-range) in > case 'on_crash' is true (i.e. 'kexec -p' use case). In the 'kexec -l' > case since we don't do any other fixups in the original approach so we > retain the same behavior here. > >>> + dbgprintf("%s: no kaslr-seed found: %s\n", >>> + __func__, fdt_strerror(result)); >>> + /* for kexec warm reboot case, we don't need to fixup >>> + * other dtb properties >>> + */ >>> + if (!on_crash) >>> + goto free_new_buf; >>> + >>> + } else { >>> + kaslr_seed = fdt64_to_cpu(*prop); >>> + >>> + /* kaslr_seed must be wiped clean by primary >>> + * kernel during boot >>> + */ >>> + if (kaslr_seed != 0) { >>> + dbgprintf("%s: kaslr-seed is not wiped to 0.\n", >>> + __func__); >> >> Ditto >> If this is a user-provided dtb, there is no reason to reject it. >> I think all what is needed here is to feed a *sane* dtb to kexec. >> >> So along with the comment above, it may be useful to add a command line >> option for turning on or off "kaslr-seed". > > Please see my comments above. Since the 'kaslr-seed' property just > needs to be read from the dtb, we probably don't need a separate > command line option for the same as we already have nokaslr available. > If we want the secondary kernel to boot with *nokaslr*, we can pass > the same to the secondary via the command line arguments. > > BTW, I also tried the behaviour with --dtb being passed while invoking > the 'kexec -l' with the patch in question and the resulting behaviour > is correct, i.e. we see that if the secondary kernel supports > CONFIG_RANDOMIZE_BASE=y, we get the resulting randomization in module > load address (for e.g.): > > # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname > -r`.img --command-line="$(cat /proc/cmdline)" --dtb /sys/firmware/fdt > -d > > # kexec -e > > On successive kexec warm reboots I see that '/proc/kallsyms' and > '/proc/modules' have randomized addresses. > >>> result = -EINVAL; >>> goto on_error; >>> } >>> >>> - if (!cells_size_fitted(address_cells, size_cells, >>> - &crash_reserved_mem)) { >>> - fprintf(stderr, >>> - "kexec: usable memory range doesn't fit cells-size.\n"); >>> + /* >>> + * Invoke the getrandom system call with >>> + * GRND_NONBLOCK, to make sure we >>> + * have a valid random seed to pass to the >>> + * secondary kernel. >>> + */ >>> + result = syscall(SYS_getrandom, &fdt_val64, >>> + sizeof(fdt_val64), >>> + GRND_NONBLOCK); >> >> Why do you use syscall() here? > > I found that the standard way to invokde a getrandom() call is via a > SYSCALL (please see > <https://nikmav.blogspot.in/2016/10/random-generator-linux.html>). > >>> + >>> + if(result == -1) { >>> + dbgprintf("%s: Reading random bytes failed.\n", >>> + __func__); >>> result = -EINVAL; >>> goto on_error; >>> } >>> >>> - /* duplicate dt blob */ >>> - range_len = sizeof(uint32_t) * (address_cells + size_cells); >>> - new_size = fdt_totalsize(dtb->buf) >>> - + fdt_prop_len(PROP_ELFCOREHDR, range_len) >>> - + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); >>> - >>> - new_buf = xmalloc(new_size); >>> - result = fdt_open_into(dtb->buf, new_buf, new_size); >>> + nodeoffset = fdt_path_offset(new_buf, "/chosen"); >>> + result = fdt_setprop_inplace(new_buf, >>> + nodeoffset, "kaslr-seed", >>> + &fdt_val64, sizeof(fdt_val64)); >>> if (result) { >>> - dbgprintf("%s: fdt_open_into failed: %s\n", __func__, >>> - fdt_strerror(result)); >>> - result = -ENOSPC; >>> + dbgprintf("%s: fdt_setprop failed: %s\n", >>> + __func__, fdt_strerror(result)); >>> + result = -EINVAL; >>> goto on_error; >>> } >>> + } >>> >>> + if (on_crash) { >>> /* add linux,elfcorehdr */ >>> nodeoffset = fdt_path_offset(new_buf, "/chosen"); >>> result = fdt_setprop_range(new_buf, nodeoffset, >>> @@ -455,7 +514,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>> address_cells, size_cells); >>> if (result) { >>> dbgprintf("%s: fdt_setprop failed: %s\n", __func__, >>> - fdt_strerror(result)); >>> + fdt_strerror(result)); >>> result = -EINVAL; >>> goto on_error; >>> } >>> @@ -467,23 +526,23 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>> address_cells, size_cells); >>> if (result) { >>> dbgprintf("%s: fdt_setprop failed: %s\n", __func__, >>> - fdt_strerror(result)); >>> + fdt_strerror(result)); >>> result = -EINVAL; >>> goto on_error; >>> } >>> - >>> - fdt_pack(new_buf); >>> - dtb->buf = new_buf; >>> - dtb->size = fdt_totalsize(new_buf); >>> } >>> >>> - dump_reservemap(dtb); >>> + fdt_pack(new_buf); >>> + dtb->buf = new_buf; >>> + dtb->size = fdt_totalsize(new_buf); >>> >>> + dump_reservemap(dtb); >>> >>> return result; >>> >>> on_error: >>> fprintf(stderr, "kexec: %s failed.\n", __func__); >>> +free_new_buf: >> >> Well, technically correct, but it looks odd as it is placed >> on *error* return path. > > I agree. I was not too comfortable with placing this label here. > I will try to find a better approach in v2. > >> You also miss dump_reservemap(). > > Oops. Sure will fix this in v2. > > Regards, > Bhupesh I was about to send a v2 for this feature and was wondering if you have any further comments on the comments I shared last on the review comments you had for the v1. If yes, I can try and include them in the v2. Please let me know. Thanks, Bhupesh _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel 2018-04-23 11:05 ` Bhupesh Sharma @ 2018-04-24 9:40 ` AKASHI, Takahiro 0 siblings, 0 replies; 5+ messages in thread From: AKASHI, Takahiro @ 2018-04-24 9:40 UTC (permalink / raw) To: Bhupesh Sharma; +Cc: Dave Young, Bhupesh SHARMA, kexec, Simon Horman Bhupesh, On 23 April 2018 at 20:05, Bhupesh Sharma <bhsharma@redhat.com> wrote: > Hello Akashi, > > On Tue, Apr 17, 2018 at 12:35 AM, Bhupesh Sharma <bhsharma@redhat.com> wrote: >> Hello Akashi, >> >> Thanks for the review comments. >> >> On Mon, Apr 16, 2018 at 8:00 AM, AKASHI Takahiro >> <takahiro.akashi@linaro.org> wrote: >>> Bhupesh, >>> >>> On Sun, Apr 15, 2018 at 01:49:40AM +0530, Bhupesh Sharma wrote: >>>> This patch adds the support to supply 'kaslr-seed' to secondary kernel, >>>> when we do a 'kexec warm reboot to another kernel' (although the >>>> behaviour remains the same for the 'kdump' case as well) on arm64 >>>> platforms using the 'kexec_load' invocation method. >>>> >>>> Lets consider the case where the primary kernel working on the arm64 >>>> platform supports kaslr (i.e 'CONFIG_RANDOMIZE_BASE' was set to y and >>>> we have a compliant EFI firmware which supports EFI_RNG_PROTOCOL and >>>> hence can pass a non-zero (valid) seed to the primary kernel). >>>> >>>> Now the primary kernel reads the 'kaslr-seed' and wipes it to 0 and >>>> uses the seed value to randomize for e.g. the module base address >>>> offset. >>>> >>>> In the case of 'kexec_load' (or even kdump for brevity), >>>> we rely on the user-space kexec-tools to pass an appropriate dtb to the >>>> secondary kernel and since 'kaslr-seed' is wiped to 0 by the primary >>>> kernel, the secondary will essentially work with *nokaslr* as >>>> 'kaslr-seed' is set to 0 when it is passed to the secondary kernel. >>>> >>>> This can be true even in case the secondary kernel had >>>> 'CONFIG_RANDOMIZE_BASE' and 'CONFIG_RANDOMIZE_MODULE_REGION_FULL' set to >>>> y. >>>> >>>> This patch addresses this issue by first checking if the device tree >>>> provided by the firmware to the kernel supports the 'kaslr-seed' >>>> property and verifies that it is really wiped to 0. If this condition is >>>> met, it fixes up the 'kaslr-seed' property by using the getrandom() >>>> syscall to get a suitable random number. >>>> >>>> I verified this patch on my Qualcomm arm64 board and here are some test >>>> results: >>>> >>>> 1. Ensure that the primary kernel is boot'ed with 'kaslr-seed' >>>> dts property and it is really wiped to 0: >>>> >>>> [root@qualcomm-amberwing]# dtc -I dtb -O dts /sys/firmware/fdt | grep -A 10 -i chosen >>>> chosen { >>>> kaslr-seed = <0x0 0x0>; >>>> ... >>>> } >>>> >>>> 2. Now issue 'kexec_load' to load the secondary kernel (let's assume >>>> that we are using the same kernel as the secondary kernel): >>>> # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname >>>> -r`.img --reuse-cmdline -d >>>> >>>> 3. Issue 'kexec -e' to warm boot to the secondary: >>>> # kexec -e >>>> >>>> 4. Now after the secondary boots, confirm that the load address of the >>>> modules is randomized in every successive boot: >>>> >>>> [root@qualcomm-amberwing]# cat /proc/modules >>>> sunrpc 524288 1 - Live 0xffff0307db190000 >>>> vfat 262144 1 - Live 0xffff0307db110000 >>>> fat 262144 1 vfat, Live 0xffff0307db090000 >>>> crc32_ce 262144 0 - Live 0xffff0307d8c70000 >>>> ... >>>> >>>> Signed-off-by: Bhupesh Sharma <bhsharma@redhat.com> >>>> --- >>>> kexec/arch/arm64/kexec-arm64.c | 135 +++++++++++++++++++++++++++++------------ >>>> 1 file changed, 97 insertions(+), 38 deletions(-) >>>> >>>> diff --git a/kexec/arch/arm64/kexec-arm64.c b/kexec/arch/arm64/kexec-arm64.c >>>> index 62f37585b788..2ab11227447a 100644 >>>> --- a/kexec/arch/arm64/kexec-arm64.c >>>> +++ b/kexec/arch/arm64/kexec-arm64.c >>>> @@ -15,6 +15,11 @@ >>>> #include <linux/elf-em.h> >>>> #include <elf.h> >>>> >>>> +#include <unistd.h> >>>> +#include <syscall.h> >>>> +#include <errno.h> >>>> +#include <linux/random.h> >>>> + >>>> #include "kexec.h" >>>> #include "kexec-arm64.h" >>>> #include "crashdump.h" >>>> @@ -392,11 +397,13 @@ static int fdt_setprop_range(void *fdt, int nodeoffset, >>>> static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>>> { >>>> uint32_t address_cells, size_cells; >>>> - int range_len; >>>> - int nodeoffset; >>>> + uint64_t fdt_val64; >>>> + uint64_t *prop; >>>> char *new_buf = NULL; >>>> + int len, range_len; >>>> + int nodeoffset; >>>> int new_size; >>>> - int result; >>>> + int result, kaslr_seed; >>>> >>>> result = fdt_check_header(dtb->buf); >>>> >>>> @@ -407,47 +414,99 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>>> >>>> result = set_bootargs(dtb, command_line); >>>> >>>> - if (on_crash) { >>>> - /* determine #address-cells and #size-cells */ >>>> - result = get_cells_size(dtb->buf, &address_cells, &size_cells); >>>> - if (result) { >>>> - fprintf(stderr, >>>> - "kexec: cannot determine cells-size.\n"); >>>> - result = -EINVAL; >>>> - goto on_error; >>>> - } >>>> + /* determine #address-cells and #size-cells */ >>>> + result = get_cells_size(dtb->buf, &address_cells, &size_cells); >>>> + if (result) { >>>> + fprintf(stderr, "kexec: cannot determine cells-size.\n"); >>>> + result = -EINVAL; >>>> + goto on_error; >>>> + } >>>> >>>> - if (!cells_size_fitted(address_cells, size_cells, >>>> - &elfcorehdr_mem)) { >>>> - fprintf(stderr, >>>> - "kexec: elfcorehdr doesn't fit cells-size.\n"); >>>> + if (!cells_size_fitted(address_cells, size_cells, >>>> + &elfcorehdr_mem)) { >>>> + fprintf(stderr, "kexec: elfcorehdr doesn't fit cells-size.\n"); >>>> + result = -EINVAL; >>>> + goto on_error; >>>> + } >>>> + >>>> + if (!cells_size_fitted(address_cells, size_cells, >>>> + &crash_reserved_mem)) { >>>> + fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n"); >>>> + result = -EINVAL; >>>> + goto on_error; >>>> + } >>>> + >>>> + /* duplicate dt blob */ >>>> + range_len = sizeof(uint32_t) * (address_cells + size_cells); >>>> + new_size = fdt_totalsize(dtb->buf) >>>> + + fdt_prop_len(PROP_ELFCOREHDR, range_len) >>>> + + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); >>>> + >>>> + new_buf = xmalloc(new_size); >>>> + result = fdt_open_into(dtb->buf, new_buf, new_size); >>>> + if (result) { >>>> + dbgprintf("%s: fdt_open_into failed: %s\n", __func__, >>>> + fdt_strerror(result)); >>>> + result = -ENOSPC; >>>> + goto on_error; >>>> + } >>>> + >>>> + /* fixup 'kaslr-seed' with a random value, if supported */ >>>> + nodeoffset = fdt_path_offset(new_buf, "/chosen"); >>>> + prop = fdt_getprop_w(new_buf, nodeoffset, >>>> + "kaslr-seed", &len); >>>> + if (!prop || len != sizeof(uint64_t)) { >>> >>> Do we need this check? >>> Please note that people are allowed to provide a dtb explicitly >>> at command line and may want to use kexec as bootloader on >>> no-uefi platform. >> >> I agree. Lets look at the original behaviour (before this patch). We >> used to unpack and fixup dtb properties and then pack it back when >> 'on_crash' was true (i.e only for the kdump case). In case of 'kexec' >> we do not fixup the dtb (as per my understanding, please correct me if >> I am wrong here). >> >> With this patch I wanted the dtb's kaslr-seed property to be fixed-up >> (if its supported and is wiped to 0 by the primary kernel). But this >> check is harmless in case we don't find the 'kaslr-seed' property in >> the dtb (for e.g. on non-uefi/u-boot based arm64 platforms). >> >> In case the property is not seen in the dtb, we just print a debug >> message (if '-d' flag was used to launch kexec) and proceed to perform >> fixup of other dtb properties (like 'linux, usable-memory-range) in >> case 'on_crash' is true (i.e. 'kexec -p' use case). In the 'kexec -l' >> case since we don't do any other fixups in the original approach so we >> retain the same behavior here. >> >>>> + dbgprintf("%s: no kaslr-seed found: %s\n", >>>> + __func__, fdt_strerror(result)); >>>> + /* for kexec warm reboot case, we don't need to fixup >>>> + * other dtb properties >>>> + */ >>>> + if (!on_crash) >>>> + goto free_new_buf; >>>> + >>>> + } else { >>>> + kaslr_seed = fdt64_to_cpu(*prop); >>>> + >>>> + /* kaslr_seed must be wiped clean by primary >>>> + * kernel during boot >>>> + */ >>>> + if (kaslr_seed != 0) { >>>> + dbgprintf("%s: kaslr-seed is not wiped to 0.\n", >>>> + __func__); >>> >>> Ditto >>> If this is a user-provided dtb, there is no reason to reject it. >>> I think all what is needed here is to feed a *sane* dtb to kexec. >>> >>> So along with the comment above, it may be useful to add a command line >>> option for turning on or off "kaslr-seed". >> >> Please see my comments above. Since the 'kaslr-seed' property just >> needs to be read from the dtb, we probably don't need a separate >> command line option for the same as we already have nokaslr available. >> If we want the secondary kernel to boot with *nokaslr*, we can pass >> the same to the secondary via the command line arguments. >> >> BTW, I also tried the behaviour with --dtb being passed while invoking >> the 'kexec -l' with the patch in question and the resulting behaviour >> is correct, i.e. we see that if the secondary kernel supports >> CONFIG_RANDOMIZE_BASE=y, we get the resulting randomization in module >> load address (for e.g.): >> >> # kexec -l /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname >> -r`.img --command-line="$(cat /proc/cmdline)" --dtb /sys/firmware/fdt >> -d >> >> # kexec -e >> >> On successive kexec warm reboots I see that '/proc/kallsyms' and >> '/proc/modules' have randomized addresses. >> >>>> result = -EINVAL; >>>> goto on_error; >>>> } >>>> >>>> - if (!cells_size_fitted(address_cells, size_cells, >>>> - &crash_reserved_mem)) { >>>> - fprintf(stderr, >>>> - "kexec: usable memory range doesn't fit cells-size.\n"); >>>> + /* >>>> + * Invoke the getrandom system call with >>>> + * GRND_NONBLOCK, to make sure we >>>> + * have a valid random seed to pass to the >>>> + * secondary kernel. >>>> + */ >>>> + result = syscall(SYS_getrandom, &fdt_val64, >>>> + sizeof(fdt_val64), >>>> + GRND_NONBLOCK); >>> >>> Why do you use syscall() here? >> >> I found that the standard way to invokde a getrandom() call is via a >> SYSCALL (please see >> <https://nikmav.blogspot.in/2016/10/random-generator-linux.html>). >> >>>> + >>>> + if(result == -1) { >>>> + dbgprintf("%s: Reading random bytes failed.\n", >>>> + __func__); >>>> result = -EINVAL; >>>> goto on_error; >>>> } >>>> >>>> - /* duplicate dt blob */ >>>> - range_len = sizeof(uint32_t) * (address_cells + size_cells); >>>> - new_size = fdt_totalsize(dtb->buf) >>>> - + fdt_prop_len(PROP_ELFCOREHDR, range_len) >>>> - + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len); >>>> - >>>> - new_buf = xmalloc(new_size); >>>> - result = fdt_open_into(dtb->buf, new_buf, new_size); >>>> + nodeoffset = fdt_path_offset(new_buf, "/chosen"); >>>> + result = fdt_setprop_inplace(new_buf, >>>> + nodeoffset, "kaslr-seed", >>>> + &fdt_val64, sizeof(fdt_val64)); >>>> if (result) { >>>> - dbgprintf("%s: fdt_open_into failed: %s\n", __func__, >>>> - fdt_strerror(result)); >>>> - result = -ENOSPC; >>>> + dbgprintf("%s: fdt_setprop failed: %s\n", >>>> + __func__, fdt_strerror(result)); >>>> + result = -EINVAL; >>>> goto on_error; >>>> } >>>> + } >>>> >>>> + if (on_crash) { >>>> /* add linux,elfcorehdr */ >>>> nodeoffset = fdt_path_offset(new_buf, "/chosen"); >>>> result = fdt_setprop_range(new_buf, nodeoffset, >>>> @@ -455,7 +514,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>>> address_cells, size_cells); >>>> if (result) { >>>> dbgprintf("%s: fdt_setprop failed: %s\n", __func__, >>>> - fdt_strerror(result)); >>>> + fdt_strerror(result)); >>>> result = -EINVAL; >>>> goto on_error; >>>> } >>>> @@ -467,23 +526,23 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash) >>>> address_cells, size_cells); >>>> if (result) { >>>> dbgprintf("%s: fdt_setprop failed: %s\n", __func__, >>>> - fdt_strerror(result)); >>>> + fdt_strerror(result)); >>>> result = -EINVAL; >>>> goto on_error; >>>> } >>>> - >>>> - fdt_pack(new_buf); >>>> - dtb->buf = new_buf; >>>> - dtb->size = fdt_totalsize(new_buf); >>>> } >>>> >>>> - dump_reservemap(dtb); >>>> + fdt_pack(new_buf); >>>> + dtb->buf = new_buf; >>>> + dtb->size = fdt_totalsize(new_buf); >>>> >>>> + dump_reservemap(dtb); >>>> >>>> return result; >>>> >>>> on_error: >>>> fprintf(stderr, "kexec: %s failed.\n", __func__); >>>> +free_new_buf: >>> >>> Well, technically correct, but it looks odd as it is placed >>> on *error* return path. >> >> I agree. I was not too comfortable with placing this label here. >> I will try to find a better approach in v2. >> >>> You also miss dump_reservemap(). >> >> Oops. Sure will fix this in v2. >> >> Regards, >> Bhupesh > > I was about to send a v2 for this feature and was wondering if you > have any further comments on the comments I shared last on the review > comments you had for the v1. If yes, I can try and include them in the > v2. I have no more comments for now. Thanks, -Takahiro AKASHI > Please let me know. > > Thanks, > Bhupesh _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2018-04-24 9:41 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-04-14 20:19 [PATCH] arm64: Add support to supply 'kaslr-seed' to secondary kernel Bhupesh Sharma 2018-04-16 2:30 ` AKASHI Takahiro 2018-04-16 19:05 ` Bhupesh Sharma 2018-04-23 11:05 ` Bhupesh Sharma 2018-04-24 9:40 ` AKASHI, Takahiro
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox