Skip to content

Commit b33f778

Browse files
dtcccctorvalds
authored andcommitted
kfence: alloc kfence_pool after system startup
Allow enabling KFENCE after system startup by allocating its pool via the page allocator. This provides the flexibility to enable KFENCE even if it wasn't enabled at boot time. Link: https://lkml.kernel.org/r/20220307074516.6920-3-dtcccc@linux.alibaba.com Signed-off-by: Tianchen Ding <dtcccc@linux.alibaba.com> Reviewed-by: Marco Elver <elver@google.com> Tested-by: Peng Liu <liupeng256@huawei.com> Cc: Alexander Potapenko <glider@google.com> Cc: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 698361b commit b33f778

File tree

1 file changed

+90
-21
lines changed

1 file changed

+90
-21
lines changed

mm/kfence/core.c

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ static unsigned long kfence_skip_covered_thresh __read_mostly = 75;
9696
module_param_named(skip_covered_thresh, kfence_skip_covered_thresh, ulong, 0644);
9797

9898
/* The pool of pages used for guard pages and objects. */
99-
char *__kfence_pool __ro_after_init;
99+
char *__kfence_pool __read_mostly;
100100
EXPORT_SYMBOL(__kfence_pool); /* Export for test modules. */
101101

102102
/*
@@ -537,17 +537,19 @@ static void rcu_guarded_free(struct rcu_head *h)
537537
kfence_guarded_free((void *)meta->addr, meta, false);
538538
}
539539

540-
static bool __init kfence_init_pool(void)
540+
/*
541+
* Initialization of the KFENCE pool after its allocation.
542+
* Returns 0 on success; otherwise returns the address up to
543+
* which partial initialization succeeded.
544+
*/
545+
static unsigned long kfence_init_pool(void)
541546
{
542547
unsigned long addr = (unsigned long)__kfence_pool;
543548
struct page *pages;
544549
int i;
545550

546-
if (!__kfence_pool)
547-
return false;
548-
549551
if (!arch_kfence_init_pool())
550-
goto err;
552+
return addr;
551553

552554
pages = virt_to_page(addr);
553555

@@ -565,7 +567,7 @@ static bool __init kfence_init_pool(void)
565567

566568
/* Verify we do not have a compound head page. */
567569
if (WARN_ON(compound_head(&pages[i]) != &pages[i]))
568-
goto err;
570+
return addr;
569571

570572
__SetPageSlab(&pages[i]);
571573
}
@@ -578,7 +580,7 @@ static bool __init kfence_init_pool(void)
578580
*/
579581
for (i = 0; i < 2; i++) {
580582
if (unlikely(!kfence_protect(addr)))
581-
goto err;
583+
return addr;
582584

583585
addr += PAGE_SIZE;
584586
}
@@ -595,7 +597,7 @@ static bool __init kfence_init_pool(void)
595597

596598
/* Protect the right redzone. */
597599
if (unlikely(!kfence_protect(addr + PAGE_SIZE)))
598-
goto err;
600+
return addr;
599601

600602
addr += 2 * PAGE_SIZE;
601603
}
@@ -608,9 +610,21 @@ static bool __init kfence_init_pool(void)
608610
*/
609611
kmemleak_free(__kfence_pool);
610612

611-
return true;
613+
return 0;
614+
}
615+
616+
static bool __init kfence_init_pool_early(void)
617+
{
618+
unsigned long addr;
619+
620+
if (!__kfence_pool)
621+
return false;
622+
623+
addr = kfence_init_pool();
624+
625+
if (!addr)
626+
return true;
612627

613-
err:
614628
/*
615629
* Only release unprotected pages, and do not try to go back and change
616630
* page attributes due to risk of failing to do so as well. If changing
@@ -623,6 +637,26 @@ static bool __init kfence_init_pool(void)
623637
return false;
624638
}
625639

640+
static bool kfence_init_pool_late(void)
641+
{
642+
unsigned long addr, free_size;
643+
644+
addr = kfence_init_pool();
645+
646+
if (!addr)
647+
return true;
648+
649+
/* Same as above. */
650+
free_size = KFENCE_POOL_SIZE - (addr - (unsigned long)__kfence_pool);
651+
#ifdef CONFIG_CONTIG_ALLOC
652+
free_contig_range(page_to_pfn(virt_to_page(addr)), free_size / PAGE_SIZE);
653+
#else
654+
free_pages_exact((void *)addr, free_size);
655+
#endif
656+
__kfence_pool = NULL;
657+
return false;
658+
}
659+
626660
/* === DebugFS Interface ==================================================== */
627661

628662
static int stats_show(struct seq_file *seq, void *v)
@@ -771,31 +805,66 @@ void __init kfence_alloc_pool(void)
771805
pr_err("failed to allocate pool\n");
772806
}
773807

808+
static void kfence_init_enable(void)
809+
{
810+
if (!IS_ENABLED(CONFIG_KFENCE_STATIC_KEYS))
811+
static_branch_enable(&kfence_allocation_key);
812+
WRITE_ONCE(kfence_enabled, true);
813+
queue_delayed_work(system_unbound_wq, &kfence_timer, 0);
814+
pr_info("initialized - using %lu bytes for %d objects at 0x%p-0x%p\n", KFENCE_POOL_SIZE,
815+
CONFIG_KFENCE_NUM_OBJECTS, (void *)__kfence_pool,
816+
(void *)(__kfence_pool + KFENCE_POOL_SIZE));
817+
}
818+
774819
void __init kfence_init(void)
775820
{
821+
stack_hash_seed = (u32)random_get_entropy();
822+
776823
/* Setting kfence_sample_interval to 0 on boot disables KFENCE. */
777824
if (!kfence_sample_interval)
778825
return;
779826

780-
stack_hash_seed = (u32)random_get_entropy();
781-
if (!kfence_init_pool()) {
827+
if (!kfence_init_pool_early()) {
782828
pr_err("%s failed\n", __func__);
783829
return;
784830
}
785831

786-
if (!IS_ENABLED(CONFIG_KFENCE_STATIC_KEYS))
787-
static_branch_enable(&kfence_allocation_key);
788-
WRITE_ONCE(kfence_enabled, true);
789-
queue_delayed_work(system_unbound_wq, &kfence_timer, 0);
790-
pr_info("initialized - using %lu bytes for %d objects at 0x%p-0x%p\n", KFENCE_POOL_SIZE,
791-
CONFIG_KFENCE_NUM_OBJECTS, (void *)__kfence_pool,
792-
(void *)(__kfence_pool + KFENCE_POOL_SIZE));
832+
kfence_init_enable();
833+
}
834+
835+
static int kfence_init_late(void)
836+
{
837+
const unsigned long nr_pages = KFENCE_POOL_SIZE / PAGE_SIZE;
838+
#ifdef CONFIG_CONTIG_ALLOC
839+
struct page *pages;
840+
841+
pages = alloc_contig_pages(nr_pages, GFP_KERNEL, first_online_node, NULL);
842+
if (!pages)
843+
return -ENOMEM;
844+
__kfence_pool = page_to_virt(pages);
845+
#else
846+
if (nr_pages > MAX_ORDER_NR_PAGES) {
847+
pr_warn("KFENCE_NUM_OBJECTS too large for buddy allocator\n");
848+
return -EINVAL;
849+
}
850+
__kfence_pool = alloc_pages_exact(KFENCE_POOL_SIZE, GFP_KERNEL);
851+
if (!__kfence_pool)
852+
return -ENOMEM;
853+
#endif
854+
855+
if (!kfence_init_pool_late()) {
856+
pr_err("%s failed\n", __func__);
857+
return -EBUSY;
858+
}
859+
860+
kfence_init_enable();
861+
return 0;
793862
}
794863

795864
static int kfence_enable_late(void)
796865
{
797866
if (!__kfence_pool)
798-
return -EINVAL;
867+
return kfence_init_late();
799868

800869
WRITE_ONCE(kfence_enabled, true);
801870
queue_delayed_work(system_unbound_wq, &kfence_timer, 0);

0 commit comments

Comments
 (0)