Skip to contentSkip to conversation

mark most percpu globals as __ro_after_init

PATCH In review Daniel Micay 3 participants 6 messages May 10, 2017

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 mark most percpu globals as __ro_after_init

Moving pcpu_base_addr to this section comes from PaX where it's part of KERNEXEC. This extends it to the rest of the globals only written by the init code.

---
 mm/percpu.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)
mm/percpu.c +18 -18
index e0aa8ae7bde7..c03753054099 100644
@@ -121,35 +121,35 @@ struct pcpu_chunk {
 	unsigned long		populated[];	/* populated bitmap */
 };
 
-static int pcpu_unit_pages __read_mostly;
-static int pcpu_unit_size __read_mostly;
-static int pcpu_nr_units __read_mostly;
-static int pcpu_atom_size __read_mostly;
-static int pcpu_nr_slots __read_mostly;
-static size_t pcpu_chunk_struct_size __read_mostly;
+static int pcpu_unit_pages __ro_after_init;
+static int pcpu_unit_size __ro_after_init;
+static int pcpu_nr_units __ro_after_init;
+static int pcpu_atom_size __ro_after_init;
+static int pcpu_nr_slots __ro_after_init;
+static size_t pcpu_chunk_struct_size __ro_after_init;
 
 /* cpus with the lowest and highest unit addresses */
-static unsigned int pcpu_low_unit_cpu __read_mostly;
-static unsigned int pcpu_high_unit_cpu __read_mostly;
+static unsigned int pcpu_low_unit_cpu __ro_after_init;
+static unsigned int pcpu_high_unit_cpu __ro_after_init;
 
 /* the address of the first chunk which starts with the kernel static area */
-void *pcpu_base_addr __read_mostly;
+void *pcpu_base_addr __ro_after_init;
 EXPORT_SYMBOL_GPL(pcpu_base_addr);
 
-static const int *pcpu_unit_map __read_mostly;		/* cpu -> unit */
-const unsigned long *pcpu_unit_offsets __read_mostly;	/* cpu -> unit offset */
+static const int *pcpu_unit_map __ro_after_init;		/* cpu -> unit */
+const unsigned long *pcpu_unit_offsets __ro_after_init;	/* cpu -> unit offset */
 
 /* group information, used for vm allocation */
-static int pcpu_nr_groups __read_mostly;
-static const unsigned long *pcpu_group_offsets __read_mostly;
-static const size_t *pcpu_group_sizes __read_mostly;
+static int pcpu_nr_groups __ro_after_init;
+static const unsigned long *pcpu_group_offsets __ro_after_init;
+static const size_t *pcpu_group_sizes __ro_after_init;
 
 /*
  * The first chunk which always exists.  Note that unlike other
  * chunks, this one can be allocated and mapped in several different
  * ways and thus often doesn't live in the vmalloc area.
  */
-static struct pcpu_chunk *pcpu_first_chunk;
+static struct pcpu_chunk *pcpu_first_chunk __ro_after_init;
 
 /*
  * Optional reserved chunk.  This chunk reserves part of the first
@@ -158,13 +158,13 @@ static struct pcpu_chunk *pcpu_first_chunk;
  * area doesn't exist, the following variables contain NULL and 0
  * respectively.
  */
-static struct pcpu_chunk *pcpu_reserved_chunk;
-static int pcpu_reserved_chunk_limit;
+static struct pcpu_chunk *pcpu_reserved_chunk __ro_after_init;
+static int pcpu_reserved_chunk_limit __ro_after_init;
 
 static DEFINE_SPINLOCK(pcpu_lock);	/* all internal data structures */
 static DEFINE_MUTEX(pcpu_alloc_mutex);	/* chunk create/destroy, [de]pop, map ext */
 
-static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+static struct list_head *pcpu_slot __ro_after_init; /* chunk list slots */
 
 /* chunks which need their map areas extended, protected by pcpu_lock */
 static LIST_HEAD(pcpu_map_extend_chunks);
Signature · 1 line
2.12.2
maintainer
#1 lore raw

Hello, Daniel.

Quoted 3 lines · Daniel Micay

Moving pcpu_base_addr to this section comes from PaX where it's part of KERNEXEC. This extends it to the rest of the globals only written by the init code.

How did you test the patch?

mm/percpu.c static struct pcpu_chunk *pcpu_first_chunk; · line 167

Quoted 2 lines
mm/percpu.c +1 -1
-static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+static struct list_head *pcpu_slot __ro_after_init; /* chunk list slots */

At least this one isn't read only.

Thanks.

Signature · 1 line
tejun
#2 lore raw
Quoted 10 lines · Tejun Heo

Hello, Daniel.

Quoted 5 lines · Daniel Micay

Moving pcpu_base_addr to this section comes from PaX where it's part of
KERNEXEC. This extends it to the rest of the globals only written by the
init code.

How did you test the patch?

Booted / did some stuff on x86 (it's running right now), and currently building it for a 3.18 arm64 kernel to test there.

Quoted 7 lines
Quoted 5 lines

-static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+static struct list_head *pcpu_slot __ro_after_init; /* chunk list slots */

At least this one isn't read only.

It's the array it points to being modified after it gets assigned to during init with pcpu_slot = memblock_virt_alloc(...), not the pointer variable itself. The references after init are all pcpu_slot[...] including taking references to slots in the array so there's always a dereference happening first.

#3 lore raw
Quoted 2 lines

Booted / did some stuff on x86 (it's running right now), and currently building it for a 3.18 arm64 kernel to test there.

Yeah, confirmed to work on an Android 3.18 arm64 kernel (Pixel phone) too in addition to 4.11 and master for x86_64.

maintainer
#4 lore raw
In reply to Daniel Micay
Quoted 23 lines · Daniel Micay
Quoted 10 lines · Tejun Heo

Hello, Daniel.

Quoted 5 lines · Daniel Micay

Moving pcpu_base_addr to this section comes from PaX where it's part of
KERNEXEC. This extends it to the rest of the globals only written by the
init code.

How did you test the patch?

Booted / did some stuff on x86 (it's running right now), and currently building it for a 3.18 arm64 kernel to test there.

Quoted 7 lines
Quoted 5 lines

-static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+static struct list_head *pcpu_slot __ro_after_init; /* chunk list slots */

At least this one isn't read only.

Quoted 5 lines

It's the array it points to being modified after it gets assigned to during init with pcpu_slot = memblock_virt_alloc(...), not the pointer variable itself. The references after init are all pcpu_slot[...] including taking references to slots in the array so there's always a dereference happening first.

Ah, right. Cool, applying to percpu/for-4.13.

Thanks.

Signature · 1 line
tejun
Acked-by on v1 1/1
#5 lore raw
In reply to Daniel Micay
Quoted 5 lines · Daniel Micay

Moving pcpu_base_addr to this section comes from PaX where it's part of KERNEXEC. This extends it to the rest of the globals only written by the init code.

Excellent, thanks!

-Kees

Quoted 79 lines
---
 mm/percpu.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)
mm/percpu.c +18 -19
index e0aa8ae7bde7..c03753054099 100644
@@ -121,35 +121,35 @@ struct pcpu_chunk {
        unsigned long           populated[];    /* populated bitmap */
 };
 
-static int pcpu_unit_pages __read_mostly;
-static int pcpu_unit_size __read_mostly;
-static int pcpu_nr_units __read_mostly;
-static int pcpu_atom_size __read_mostly;
-static int pcpu_nr_slots __read_mostly;
-static size_t pcpu_chunk_struct_size __read_mostly;
+static int pcpu_unit_pages __ro_after_init;
+static int pcpu_unit_size __ro_after_init;
+static int pcpu_nr_units __ro_after_init;
+static int pcpu_atom_size __ro_after_init;
+static int pcpu_nr_slots __ro_after_init;
+static size_t pcpu_chunk_struct_size __ro_after_init;
 
 /* cpus with the lowest and highest unit addresses */
-static unsigned int pcpu_low_unit_cpu __read_mostly;
-static unsigned int pcpu_high_unit_cpu __read_mostly;
+static unsigned int pcpu_low_unit_cpu __ro_after_init;
+static unsigned int pcpu_high_unit_cpu __ro_after_init;
 
 /* the address of the first chunk which starts with the kernel static area */
-void *pcpu_base_addr __read_mostly;
+void *pcpu_base_addr __ro_after_init;
 EXPORT_SYMBOL_GPL(pcpu_base_addr);
 
-static const int *pcpu_unit_map __read_mostly;         /* cpu -> unit */
-const unsigned long *pcpu_unit_offsets __read_mostly;  /* cpu -> unit offset */
+static const int *pcpu_unit_map __ro_after_init;               /* cpu -> unit */
+const unsigned long *pcpu_unit_offsets __ro_after_init;        /* cpu -> unit offset */
 
 /* group information, used for vm allocation */
-static int pcpu_nr_groups __read_mostly;
-static const unsigned long *pcpu_group_offsets __read_mostly;
-static const size_t *pcpu_group_sizes __read_mostly;
+static int pcpu_nr_groups __ro_after_init;
+static const unsigned long *pcpu_group_offsets __ro_after_init;
+static const size_t *pcpu_group_sizes __ro_after_init;
 
 /*
  * The first chunk which always exists.  Note that unlike other
  * chunks, this one can be allocated and mapped in several different
  * ways and thus often doesn't live in the vmalloc area.
  */
-static struct pcpu_chunk *pcpu_first_chunk;
+static struct pcpu_chunk *pcpu_first_chunk __ro_after_init;
 
 /*
  * Optional reserved chunk.  This chunk reserves part of the first
@@ -158,13 +158,13 @@ static struct pcpu_chunk *pcpu_first_chunk;
  * area doesn't exist, the following variables contain NULL and 0
  * respectively.
  */
-static struct pcpu_chunk *pcpu_reserved_chunk;
-static int pcpu_reserved_chunk_limit;
+static struct pcpu_chunk *pcpu_reserved_chunk __ro_after_init;
+static int pcpu_reserved_chunk_limit __ro_after_init;
 
 static DEFINE_SPINLOCK(pcpu_lock);     /* all internal data structures */
 static DEFINE_MUTEX(pcpu_alloc_mutex); /* chunk create/destroy, [de]pop, map ext */
 
-static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */
+static struct list_head *pcpu_slot __ro_after_init; /* chunk list slots */
 
 /* chunks which need their map areas extended, protected by pcpu_lock */
 static LIST_HEAD(pcpu_map_extend_chunks);
--

2.12.2

Signature · 2 lines
Kees Cook
Pixel Security