diff options
| -rw-r--r-- | kernel/livepatch/core.c | 67 |
1 files changed, 17 insertions, 50 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index ff7f47d026ac..26df09d56f7c 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
| @@ -322,32 +322,20 @@ static void notrace klp_ftrace_handler(unsigned long ip, | |||
| 322 | klp_arch_set_pc(regs, (unsigned long)func->new_func); | 322 | klp_arch_set_pc(regs, (unsigned long)func->new_func); |
| 323 | } | 323 | } |
| 324 | 324 | ||
| 325 | static int klp_disable_func(struct klp_func *func) | 325 | static void klp_disable_func(struct klp_func *func) |
| 326 | { | 326 | { |
| 327 | struct klp_ops *ops; | 327 | struct klp_ops *ops; |
| 328 | int ret; | ||
| 329 | |||
| 330 | if (WARN_ON(func->state != KLP_ENABLED)) | ||
| 331 | return -EINVAL; | ||
| 332 | 328 | ||
| 333 | if (WARN_ON(!func->old_addr)) | 329 | WARN_ON(func->state != KLP_ENABLED); |
| 334 | return -EINVAL; | 330 | WARN_ON(!func->old_addr); |
| 335 | 331 | ||
| 336 | ops = klp_find_ops(func->old_addr); | 332 | ops = klp_find_ops(func->old_addr); |
| 337 | if (WARN_ON(!ops)) | 333 | if (WARN_ON(!ops)) |
| 338 | return -EINVAL; | 334 | return; |
| 339 | 335 | ||
| 340 | if (list_is_singular(&ops->func_stack)) { | 336 | if (list_is_singular(&ops->func_stack)) { |
| 341 | ret = unregister_ftrace_function(&ops->fops); | 337 | WARN_ON(unregister_ftrace_function(&ops->fops)); |
| 342 | if (ret) { | 338 | WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0)); |
| 343 | pr_err("failed to unregister ftrace handler for function '%s' (%d)\n", | ||
| 344 | func->old_name, ret); | ||
| 345 | return ret; | ||
| 346 | } | ||
| 347 | |||
| 348 | ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0); | ||
| 349 | if (ret) | ||
| 350 | pr_warn("function unregister succeeded but failed to clear the filter\n"); | ||
| 351 | 339 | ||
| 352 | list_del_rcu(&func->stack_node); | 340 | list_del_rcu(&func->stack_node); |
| 353 | list_del(&ops->node); | 341 | list_del(&ops->node); |
| @@ -357,8 +345,6 @@ static int klp_disable_func(struct klp_func *func) | |||
| 357 | } | 345 | } |
| 358 | 346 | ||
| 359 | func->state = KLP_DISABLED; | 347 | func->state = KLP_DISABLED; |
| 360 | |||
| 361 | return 0; | ||
| 362 | } | 348 | } |
| 363 | 349 | ||
| 364 | static int klp_enable_func(struct klp_func *func) | 350 | static int klp_enable_func(struct klp_func *func) |
| @@ -419,23 +405,15 @@ err: | |||
| 419 | return ret; | 405 | return ret; |
| 420 | } | 406 | } |
| 421 | 407 | ||
| 422 | static int klp_disable_object(struct klp_object *obj) | 408 | static void klp_disable_object(struct klp_object *obj) |
| 423 | { | 409 | { |
| 424 | struct klp_func *func; | 410 | struct klp_func *func; |
| 425 | int ret; | ||
| 426 | 411 | ||
| 427 | for (func = obj->funcs; func->old_name; func++) { | 412 | for (func = obj->funcs; func->old_name; func++) |
| 428 | if (func->state != KLP_ENABLED) | 413 | if (func->state == KLP_ENABLED) |
| 429 | continue; | 414 | klp_disable_func(func); |
| 430 | |||
| 431 | ret = klp_disable_func(func); | ||
| 432 | if (ret) | ||
| 433 | return ret; | ||
| 434 | } | ||
| 435 | 415 | ||
| 436 | obj->state = KLP_DISABLED; | 416 | obj->state = KLP_DISABLED; |
| 437 | |||
| 438 | return 0; | ||
| 439 | } | 417 | } |
| 440 | 418 | ||
| 441 | static int klp_enable_object(struct klp_object *obj) | 419 | static int klp_enable_object(struct klp_object *obj) |
| @@ -451,22 +429,19 @@ static int klp_enable_object(struct klp_object *obj) | |||
| 451 | 429 | ||
| 452 | for (func = obj->funcs; func->old_name; func++) { | 430 | for (func = obj->funcs; func->old_name; func++) { |
| 453 | ret = klp_enable_func(func); | 431 | ret = klp_enable_func(func); |
| 454 | if (ret) | 432 | if (ret) { |
| 455 | goto unregister; | 433 | klp_disable_object(obj); |
| 434 | return ret; | ||
| 435 | } | ||
| 456 | } | 436 | } |
| 457 | obj->state = KLP_ENABLED; | 437 | obj->state = KLP_ENABLED; |
| 458 | 438 | ||
| 459 | return 0; | 439 | return 0; |
| 460 | |||
| 461 | unregister: | ||
| 462 | WARN_ON(klp_disable_object(obj)); | ||
| 463 | return ret; | ||
| 464 | } | 440 | } |
| 465 | 441 | ||
| 466 | static int __klp_disable_patch(struct klp_patch *patch) | 442 | static int __klp_disable_patch(struct klp_patch *patch) |
| 467 | { | 443 | { |
| 468 | struct klp_object *obj; | 444 | struct klp_object *obj; |
| 469 | int ret; | ||
| 470 | 445 | ||
| 471 | /* enforce stacking: only the last enabled patch can be disabled */ | 446 | /* enforce stacking: only the last enabled patch can be disabled */ |
| 472 | if (!list_is_last(&patch->list, &klp_patches) && | 447 | if (!list_is_last(&patch->list, &klp_patches) && |
| @@ -476,12 +451,8 @@ static int __klp_disable_patch(struct klp_patch *patch) | |||
| 476 | pr_notice("disabling patch '%s'\n", patch->mod->name); | 451 | pr_notice("disabling patch '%s'\n", patch->mod->name); |
| 477 | 452 | ||
| 478 | for (obj = patch->objs; obj->funcs; obj++) { | 453 | for (obj = patch->objs; obj->funcs; obj++) { |
| 479 | if (obj->state != KLP_ENABLED) | 454 | if (obj->state == KLP_ENABLED) |
| 480 | continue; | 455 | klp_disable_object(obj); |
| 481 | |||
| 482 | ret = klp_disable_object(obj); | ||
| 483 | if (ret) | ||
| 484 | return ret; | ||
| 485 | } | 456 | } |
| 486 | 457 | ||
| 487 | patch->state = KLP_DISABLED; | 458 | patch->state = KLP_DISABLED; |
| @@ -931,7 +902,6 @@ static void klp_module_notify_going(struct klp_patch *patch, | |||
| 931 | { | 902 | { |
| 932 | struct module *pmod = patch->mod; | 903 | struct module *pmod = patch->mod; |
| 933 | struct module *mod = obj->mod; | 904 | struct module *mod = obj->mod; |
| 934 | int ret; | ||
| 935 | 905 | ||
| 936 | if (patch->state == KLP_DISABLED) | 906 | if (patch->state == KLP_DISABLED) |
| 937 | goto disabled; | 907 | goto disabled; |
| @@ -939,10 +909,7 @@ static void klp_module_notify_going(struct klp_patch *patch, | |||
| 939 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | 909 | pr_notice("reverting patch '%s' on unloading module '%s'\n", |
| 940 | pmod->name, mod->name); | 910 | pmod->name, mod->name); |
| 941 | 911 | ||
| 942 | ret = klp_disable_object(obj); | 912 | klp_disable_object(obj); |
| 943 | if (ret) | ||
| 944 | pr_warn("failed to revert patch '%s' on module '%s' (%d)\n", | ||
| 945 | pmod->name, mod->name, ret); | ||
| 946 | 913 | ||
| 947 | disabled: | 914 | disabled: |
| 948 | klp_free_object_loaded(obj); | 915 | klp_free_object_loaded(obj); |
