Skip to content

Commit

Permalink
powerpc: make feature-fixup tests fortify-safe
Browse files Browse the repository at this point in the history
Testing the fortified string functions[1] would cause a kernel panic on
boot in test_feature_fixups() due to a buffer overflow in memcmp.

This boils down to things like this:

  extern unsigned int ftr_fixup_test1;
  extern unsigned int ftr_fixup_test1_orig;

  check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);

We know that these are asm labels so it is safe to read up to 'size'
bytes at those addresses.

However, because we have passed the address of a single unsigned int to
memcmp, the compiler believes the underlying object is in fact a single
unsigned int.  So if size > sizeof(unsigned int), there will be a panic
at runtime.

We can fix this by changing the types: instead of calling the asm labels
unsigned ints, call them unsigned int[]s.  Therefore the size isn't
incorrectly determined at compile time and we get a regular unsafe
memcmp and no panic.

[1] http://openwall.com/lists/kernel-hardening/2017/05/09/2

Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Daniel Axtens <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Suggested-by: Michael Ellerman <[email protected]>
Tested-by: Andrew Donnellan <[email protected]>
Reviewed-by: Andrew Donnellan <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Daniel Micay <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
daxtens authored and torvalds committed Jul 12, 2017
1 parent 054f367 commit c69a48c
Showing 1 changed file with 90 additions and 90 deletions.
180 changes: 90 additions & 90 deletions arch/powerpc/lib/feature-fixups.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,192 +233,192 @@ static long calc_offset(struct fixup_entry *entry, unsigned int *p)

static void test_basic_patching(void)
{
extern unsigned int ftr_fixup_test1;
extern unsigned int end_ftr_fixup_test1;
extern unsigned int ftr_fixup_test1_orig;
extern unsigned int ftr_fixup_test1_expected;
int size = &end_ftr_fixup_test1 - &ftr_fixup_test1;
extern unsigned int ftr_fixup_test1[];
extern unsigned int end_ftr_fixup_test1[];
extern unsigned int ftr_fixup_test1_orig[];
extern unsigned int ftr_fixup_test1_expected[];
int size = end_ftr_fixup_test1 - ftr_fixup_test1;

fixup.value = fixup.mask = 8;
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test1 + 1);
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test1 + 2);
fixup.start_off = calc_offset(&fixup, ftr_fixup_test1 + 1);
fixup.end_off = calc_offset(&fixup, ftr_fixup_test1 + 2);
fixup.alt_start_off = fixup.alt_end_off = 0;

/* Sanity check */
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);

/* Check we don't patch if the value matches */
patch_feature_section(8, &fixup);
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);

/* Check we do patch if the value doesn't match */
patch_feature_section(0, &fixup);
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);

/* Check we do patch if the mask doesn't match */
memcpy(&ftr_fixup_test1, &ftr_fixup_test1_orig, size);
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_orig, size) == 0);
memcpy(ftr_fixup_test1, ftr_fixup_test1_orig, size);
check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
patch_feature_section(~8, &fixup);
check(memcmp(&ftr_fixup_test1, &ftr_fixup_test1_expected, size) == 0);
check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
}

static void test_alternative_patching(void)
{
extern unsigned int ftr_fixup_test2;
extern unsigned int end_ftr_fixup_test2;
extern unsigned int ftr_fixup_test2_orig;
extern unsigned int ftr_fixup_test2_alt;
extern unsigned int ftr_fixup_test2_expected;
int size = &end_ftr_fixup_test2 - &ftr_fixup_test2;
extern unsigned int ftr_fixup_test2[];
extern unsigned int end_ftr_fixup_test2[];
extern unsigned int ftr_fixup_test2_orig[];
extern unsigned int ftr_fixup_test2_alt[];
extern unsigned int ftr_fixup_test2_expected[];
int size = end_ftr_fixup_test2 - ftr_fixup_test2;

fixup.value = fixup.mask = 0xF;
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test2 + 1);
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test2 + 2);
fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test2_alt);
fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test2_alt + 1);
fixup.start_off = calc_offset(&fixup, ftr_fixup_test2 + 1);
fixup.end_off = calc_offset(&fixup, ftr_fixup_test2 + 2);
fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test2_alt);
fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test2_alt + 1);

/* Sanity check */
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);

/* Check we don't patch if the value matches */
patch_feature_section(0xF, &fixup);
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);

/* Check we do patch if the value doesn't match */
patch_feature_section(0, &fixup);
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);

/* Check we do patch if the mask doesn't match */
memcpy(&ftr_fixup_test2, &ftr_fixup_test2_orig, size);
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_orig, size) == 0);
memcpy(ftr_fixup_test2, ftr_fixup_test2_orig, size);
check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
patch_feature_section(~0xF, &fixup);
check(memcmp(&ftr_fixup_test2, &ftr_fixup_test2_expected, size) == 0);
check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
}

static void test_alternative_case_too_big(void)
{
extern unsigned int ftr_fixup_test3;
extern unsigned int end_ftr_fixup_test3;
extern unsigned int ftr_fixup_test3_orig;
extern unsigned int ftr_fixup_test3_alt;
int size = &end_ftr_fixup_test3 - &ftr_fixup_test3;
extern unsigned int ftr_fixup_test3[];
extern unsigned int end_ftr_fixup_test3[];
extern unsigned int ftr_fixup_test3_orig[];
extern unsigned int ftr_fixup_test3_alt[];
int size = end_ftr_fixup_test3 - ftr_fixup_test3;

fixup.value = fixup.mask = 0xC;
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test3 + 1);
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test3 + 2);
fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test3_alt);
fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test3_alt + 2);
fixup.start_off = calc_offset(&fixup, ftr_fixup_test3 + 1);
fixup.end_off = calc_offset(&fixup, ftr_fixup_test3 + 2);
fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test3_alt);
fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test3_alt + 2);

/* Sanity check */
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);

/* Expect nothing to be patched, and the error returned to us */
check(patch_feature_section(0xF, &fixup) == 1);
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
check(patch_feature_section(0, &fixup) == 1);
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
check(patch_feature_section(~0xF, &fixup) == 1);
check(memcmp(&ftr_fixup_test3, &ftr_fixup_test3_orig, size) == 0);
check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
}

static void test_alternative_case_too_small(void)
{
extern unsigned int ftr_fixup_test4;
extern unsigned int end_ftr_fixup_test4;
extern unsigned int ftr_fixup_test4_orig;
extern unsigned int ftr_fixup_test4_alt;
extern unsigned int ftr_fixup_test4_expected;
int size = &end_ftr_fixup_test4 - &ftr_fixup_test4;
extern unsigned int ftr_fixup_test4[];
extern unsigned int end_ftr_fixup_test4[];
extern unsigned int ftr_fixup_test4_orig[];
extern unsigned int ftr_fixup_test4_alt[];
extern unsigned int ftr_fixup_test4_expected[];
int size = end_ftr_fixup_test4 - ftr_fixup_test4;
unsigned long flag;

/* Check a high-bit flag */
flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
fixup.value = fixup.mask = flag;
fixup.start_off = calc_offset(&fixup, &ftr_fixup_test4 + 1);
fixup.end_off = calc_offset(&fixup, &ftr_fixup_test4 + 5);
fixup.alt_start_off = calc_offset(&fixup, &ftr_fixup_test4_alt);
fixup.alt_end_off = calc_offset(&fixup, &ftr_fixup_test4_alt + 2);
fixup.start_off = calc_offset(&fixup, ftr_fixup_test4 + 1);
fixup.end_off = calc_offset(&fixup, ftr_fixup_test4 + 5);
fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test4_alt);
fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test4_alt + 2);

/* Sanity check */
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);

/* Check we don't patch if the value matches */
patch_feature_section(flag, &fixup);
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);

/* Check we do patch if the value doesn't match */
patch_feature_section(0, &fixup);
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);

/* Check we do patch if the mask doesn't match */
memcpy(&ftr_fixup_test4, &ftr_fixup_test4_orig, size);
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_orig, size) == 0);
memcpy(ftr_fixup_test4, ftr_fixup_test4_orig, size);
check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
patch_feature_section(~flag, &fixup);
check(memcmp(&ftr_fixup_test4, &ftr_fixup_test4_expected, size) == 0);
check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
}

static void test_alternative_case_with_branch(void)
{
extern unsigned int ftr_fixup_test5;
extern unsigned int end_ftr_fixup_test5;
extern unsigned int ftr_fixup_test5_expected;
int size = &end_ftr_fixup_test5 - &ftr_fixup_test5;
extern unsigned int ftr_fixup_test5[];
extern unsigned int end_ftr_fixup_test5[];
extern unsigned int ftr_fixup_test5_expected[];
int size = end_ftr_fixup_test5 - ftr_fixup_test5;

check(memcmp(&ftr_fixup_test5, &ftr_fixup_test5_expected, size) == 0);
check(memcmp(ftr_fixup_test5, ftr_fixup_test5_expected, size) == 0);
}

static void test_alternative_case_with_external_branch(void)
{
extern unsigned int ftr_fixup_test6;
extern unsigned int end_ftr_fixup_test6;
extern unsigned int ftr_fixup_test6_expected;
int size = &end_ftr_fixup_test6 - &ftr_fixup_test6;
extern unsigned int ftr_fixup_test6[];
extern unsigned int end_ftr_fixup_test6[];
extern unsigned int ftr_fixup_test6_expected[];
int size = end_ftr_fixup_test6 - ftr_fixup_test6;

check(memcmp(&ftr_fixup_test6, &ftr_fixup_test6_expected, size) == 0);
check(memcmp(ftr_fixup_test6, ftr_fixup_test6_expected, size) == 0);
}

static void test_cpu_macros(void)
{
extern u8 ftr_fixup_test_FTR_macros;
extern u8 ftr_fixup_test_FTR_macros_expected;
unsigned long size = &ftr_fixup_test_FTR_macros_expected -
&ftr_fixup_test_FTR_macros;
extern u8 ftr_fixup_test_FTR_macros[];
extern u8 ftr_fixup_test_FTR_macros_expected[];
unsigned long size = ftr_fixup_test_FTR_macros_expected -
ftr_fixup_test_FTR_macros;

/* The fixups have already been done for us during boot */
check(memcmp(&ftr_fixup_test_FTR_macros,
&ftr_fixup_test_FTR_macros_expected, size) == 0);
check(memcmp(ftr_fixup_test_FTR_macros,
ftr_fixup_test_FTR_macros_expected, size) == 0);
}

static void test_fw_macros(void)
{
#ifdef CONFIG_PPC64
extern u8 ftr_fixup_test_FW_FTR_macros;
extern u8 ftr_fixup_test_FW_FTR_macros_expected;
unsigned long size = &ftr_fixup_test_FW_FTR_macros_expected -
&ftr_fixup_test_FW_FTR_macros;
extern u8 ftr_fixup_test_FW_FTR_macros[];
extern u8 ftr_fixup_test_FW_FTR_macros_expected[];
unsigned long size = ftr_fixup_test_FW_FTR_macros_expected -
ftr_fixup_test_FW_FTR_macros;

/* The fixups have already been done for us during boot */
check(memcmp(&ftr_fixup_test_FW_FTR_macros,
&ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
check(memcmp(ftr_fixup_test_FW_FTR_macros,
ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
#endif
}

static void test_lwsync_macros(void)
{
extern u8 lwsync_fixup_test;
extern u8 end_lwsync_fixup_test;
extern u8 lwsync_fixup_test_expected_LWSYNC;
extern u8 lwsync_fixup_test_expected_SYNC;
unsigned long size = &end_lwsync_fixup_test -
&lwsync_fixup_test;
extern u8 lwsync_fixup_test[];
extern u8 end_lwsync_fixup_test[];
extern u8 lwsync_fixup_test_expected_LWSYNC[];
extern u8 lwsync_fixup_test_expected_SYNC[];
unsigned long size = end_lwsync_fixup_test -
lwsync_fixup_test;

/* The fixups have already been done for us during boot */
if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
check(memcmp(&lwsync_fixup_test,
&lwsync_fixup_test_expected_LWSYNC, size) == 0);
check(memcmp(lwsync_fixup_test,
lwsync_fixup_test_expected_LWSYNC, size) == 0);
} else {
check(memcmp(&lwsync_fixup_test,
&lwsync_fixup_test_expected_SYNC, size) == 0);
check(memcmp(lwsync_fixup_test,
lwsync_fixup_test_expected_SYNC, size) == 0);
}
}

Expand Down

0 comments on commit c69a48c

Please sign in to comment.