diff options
| -rw-r--r-- | arch/x86/kernel/kprobes.c | 3 | ||||
| -rw-r--r-- | include/linux/jump_label.h | 8 | ||||
| -rw-r--r-- | kernel/jump_label.c | 83 | ||||
| -rw-r--r-- | kernel/kprobes.c | 3 |
4 files changed, 94 insertions, 3 deletions
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index e05952af5d26..1cbd54c0df99 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c | |||
| @@ -1218,7 +1218,8 @@ static int __kprobes copy_optimized_instructions(u8 *dest, u8 *src) | |||
| 1218 | } | 1218 | } |
| 1219 | /* Check whether the address range is reserved */ | 1219 | /* Check whether the address range is reserved */ |
| 1220 | if (ftrace_text_reserved(src, src + len - 1) || | 1220 | if (ftrace_text_reserved(src, src + len - 1) || |
| 1221 | alternatives_text_reserved(src, src + len - 1)) | 1221 | alternatives_text_reserved(src, src + len - 1) || |
| 1222 | jump_label_text_reserved(src, src + len - 1)) | ||
| 1222 | return -EBUSY; | 1223 | return -EBUSY; |
| 1223 | 1224 | ||
| 1224 | return len; | 1225 | return len; |
diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index de58656d28e0..b72cd9f92c2e 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h | |||
| @@ -20,9 +20,10 @@ extern struct jump_entry __stop___jump_table[]; | |||
| 20 | 20 | ||
| 21 | extern void arch_jump_label_transform(struct jump_entry *entry, | 21 | extern void arch_jump_label_transform(struct jump_entry *entry, |
| 22 | enum jump_label_type type); | 22 | enum jump_label_type type); |
| 23 | extern void arch_jump_label_text_poke_early(jump_label_t addr); | ||
| 23 | extern void jump_label_update(unsigned long key, enum jump_label_type type); | 24 | extern void jump_label_update(unsigned long key, enum jump_label_type type); |
| 24 | extern void jump_label_apply_nops(struct module *mod); | 25 | extern void jump_label_apply_nops(struct module *mod); |
| 25 | extern void arch_jump_label_text_poke_early(jump_label_t addr); | 26 | extern int jump_label_text_reserved(void *start, void *end); |
| 26 | 27 | ||
| 27 | #define enable_jump_label(key) \ | 28 | #define enable_jump_label(key) \ |
| 28 | jump_label_update((unsigned long)key, JUMP_LABEL_ENABLE); | 29 | jump_label_update((unsigned long)key, JUMP_LABEL_ENABLE); |
| @@ -53,6 +54,11 @@ static inline int jump_label_apply_nops(struct module *mod) | |||
| 53 | return 0; | 54 | return 0; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 57 | static inline int jump_label_text_reserved(void *start, void *end) | ||
| 58 | { | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 56 | #endif | 62 | #endif |
| 57 | 63 | ||
| 58 | #endif | 64 | #endif |
diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 460fd40112b3..7be868bf25c6 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c | |||
| @@ -177,6 +177,89 @@ void jump_label_update(unsigned long key, enum jump_label_type type) | |||
| 177 | mutex_unlock(&jump_label_mutex); | 177 | mutex_unlock(&jump_label_mutex); |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | static int addr_conflict(struct jump_entry *entry, void *start, void *end) | ||
| 181 | { | ||
| 182 | if (entry->code <= (unsigned long)end && | ||
| 183 | entry->code + JUMP_LABEL_NOP_SIZE > (unsigned long)start) | ||
| 184 | return 1; | ||
| 185 | |||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | #ifdef CONFIG_MODULES | ||
| 190 | |||
| 191 | static int module_conflict(void *start, void *end) | ||
| 192 | { | ||
| 193 | struct hlist_head *head; | ||
| 194 | struct hlist_node *node, *node_next, *module_node, *module_node_next; | ||
| 195 | struct jump_label_entry *e; | ||
| 196 | struct jump_label_module_entry *e_module; | ||
| 197 | struct jump_entry *iter; | ||
| 198 | int i, count; | ||
| 199 | int conflict = 0; | ||
| 200 | |||
| 201 | for (i = 0; i < JUMP_LABEL_TABLE_SIZE; i++) { | ||
| 202 | head = &jump_label_table[i]; | ||
| 203 | hlist_for_each_entry_safe(e, node, node_next, head, hlist) { | ||
| 204 | hlist_for_each_entry_safe(e_module, module_node, | ||
| 205 | module_node_next, | ||
| 206 | &(e->modules), hlist) { | ||
| 207 | count = e_module->nr_entries; | ||
| 208 | iter = e_module->table; | ||
| 209 | while (count--) { | ||
| 210 | if (addr_conflict(iter, start, end)) { | ||
| 211 | conflict = 1; | ||
| 212 | goto out; | ||
| 213 | } | ||
| 214 | iter++; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | out: | ||
| 220 | return conflict; | ||
| 221 | } | ||
| 222 | |||
| 223 | #endif | ||
| 224 | |||
| 225 | /*** | ||
| 226 | * jump_label_text_reserved - check if addr range is reserved | ||
| 227 | * @start: start text addr | ||
| 228 | * @end: end text addr | ||
| 229 | * | ||
| 230 | * checks if the text addr located between @start and @end | ||
| 231 | * overlaps with any of the jump label patch addresses. Code | ||
| 232 | * that wants to modify kernel text should first verify that | ||
| 233 | * it does not overlap with any of the jump label addresses. | ||
| 234 | * | ||
| 235 | * returns 1 if there is an overlap, 0 otherwise | ||
| 236 | */ | ||
| 237 | int jump_label_text_reserved(void *start, void *end) | ||
| 238 | { | ||
| 239 | struct jump_entry *iter; | ||
| 240 | struct jump_entry *iter_start = __start___jump_table; | ||
| 241 | struct jump_entry *iter_stop = __start___jump_table; | ||
| 242 | int conflict = 0; | ||
| 243 | |||
| 244 | mutex_lock(&jump_label_mutex); | ||
| 245 | iter = iter_start; | ||
| 246 | while (iter < iter_stop) { | ||
| 247 | if (addr_conflict(iter, start, end)) { | ||
| 248 | conflict = 1; | ||
| 249 | goto out; | ||
| 250 | } | ||
| 251 | iter++; | ||
| 252 | } | ||
| 253 | |||
| 254 | /* now check modules */ | ||
| 255 | #ifdef CONFIG_MODULES | ||
| 256 | conflict = module_conflict(start, end); | ||
| 257 | #endif | ||
| 258 | out: | ||
| 259 | mutex_unlock(&jump_label_mutex); | ||
| 260 | return conflict; | ||
| 261 | } | ||
| 262 | |||
| 180 | static __init int init_jump_label(void) | 263 | static __init int init_jump_label(void) |
| 181 | { | 264 | { |
| 182 | int ret; | 265 | int ret; |
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 18904e42a918..ec4210c6501e 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
| @@ -1147,7 +1147,8 @@ int __kprobes register_kprobe(struct kprobe *p) | |||
| 1147 | preempt_disable(); | 1147 | preempt_disable(); |
| 1148 | if (!kernel_text_address((unsigned long) p->addr) || | 1148 | if (!kernel_text_address((unsigned long) p->addr) || |
| 1149 | in_kprobes_functions((unsigned long) p->addr) || | 1149 | in_kprobes_functions((unsigned long) p->addr) || |
| 1150 | ftrace_text_reserved(p->addr, p->addr)) { | 1150 | ftrace_text_reserved(p->addr, p->addr) || |
| 1151 | jump_label_text_reserved(p->addr, p->addr)) { | ||
| 1151 | preempt_enable(); | 1152 | preempt_enable(); |
| 1152 | return -EINVAL; | 1153 | return -EINVAL; |
| 1153 | } | 1154 | } |
