diff options
Diffstat (limited to 'kernel/livepatch/core.c')
| -rw-r--r-- | kernel/livepatch/core.c | 69 |
1 files changed, 17 insertions, 52 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index 3f9f1d6b4c2e..284e2691e380 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
| @@ -335,32 +335,20 @@ unlock: | |||
| 335 | rcu_read_unlock(); | 335 | rcu_read_unlock(); |
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | static int klp_disable_func(struct klp_func *func) | 338 | static void klp_disable_func(struct klp_func *func) |
| 339 | { | 339 | { |
| 340 | struct klp_ops *ops; | 340 | struct klp_ops *ops; |
| 341 | int ret; | ||
| 342 | |||
| 343 | if (WARN_ON(func->state != KLP_ENABLED)) | ||
| 344 | return -EINVAL; | ||
| 345 | 341 | ||
| 346 | if (WARN_ON(!func->old_addr)) | 342 | WARN_ON(func->state != KLP_ENABLED); |
| 347 | return -EINVAL; | 343 | WARN_ON(!func->old_addr); |
| 348 | 344 | ||
| 349 | ops = klp_find_ops(func->old_addr); | 345 | ops = klp_find_ops(func->old_addr); |
| 350 | if (WARN_ON(!ops)) | 346 | if (WARN_ON(!ops)) |
| 351 | return -EINVAL; | 347 | return; |
| 352 | 348 | ||
| 353 | if (list_is_singular(&ops->func_stack)) { | 349 | if (list_is_singular(&ops->func_stack)) { |
| 354 | ret = unregister_ftrace_function(&ops->fops); | 350 | WARN_ON(unregister_ftrace_function(&ops->fops)); |
| 355 | if (ret) { | 351 | WARN_ON(ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0)); |
| 356 | pr_err("failed to unregister ftrace handler for function '%s' (%d)\n", | ||
| 357 | func->old_name, ret); | ||
| 358 | return ret; | ||
| 359 | } | ||
| 360 | |||
| 361 | ret = ftrace_set_filter_ip(&ops->fops, func->old_addr, 1, 0); | ||
| 362 | if (ret) | ||
| 363 | pr_warn("function unregister succeeded but failed to clear the filter\n"); | ||
| 364 | 352 | ||
| 365 | list_del_rcu(&func->stack_node); | 353 | list_del_rcu(&func->stack_node); |
| 366 | list_del(&ops->node); | 354 | list_del(&ops->node); |
| @@ -370,8 +358,6 @@ static int klp_disable_func(struct klp_func *func) | |||
| 370 | } | 358 | } |
| 371 | 359 | ||
| 372 | func->state = KLP_DISABLED; | 360 | func->state = KLP_DISABLED; |
| 373 | |||
| 374 | return 0; | ||
| 375 | } | 361 | } |
| 376 | 362 | ||
| 377 | static int klp_enable_func(struct klp_func *func) | 363 | static int klp_enable_func(struct klp_func *func) |
| @@ -432,23 +418,15 @@ err: | |||
| 432 | return ret; | 418 | return ret; |
| 433 | } | 419 | } |
| 434 | 420 | ||
| 435 | static int klp_disable_object(struct klp_object *obj) | 421 | static void klp_disable_object(struct klp_object *obj) |
| 436 | { | 422 | { |
| 437 | struct klp_func *func; | 423 | struct klp_func *func; |
| 438 | int ret; | ||
| 439 | 424 | ||
| 440 | for (func = obj->funcs; func->old_name; func++) { | 425 | for (func = obj->funcs; func->old_name; func++) |
| 441 | if (func->state != KLP_ENABLED) | 426 | if (func->state == KLP_ENABLED) |
| 442 | continue; | 427 | klp_disable_func(func); |
| 443 | |||
| 444 | ret = klp_disable_func(func); | ||
| 445 | if (ret) | ||
| 446 | return ret; | ||
| 447 | } | ||
| 448 | 428 | ||
| 449 | obj->state = KLP_DISABLED; | 429 | obj->state = KLP_DISABLED; |
| 450 | |||
| 451 | return 0; | ||
| 452 | } | 430 | } |
| 453 | 431 | ||
| 454 | static int klp_enable_object(struct klp_object *obj) | 432 | static int klp_enable_object(struct klp_object *obj) |
| @@ -464,22 +442,19 @@ static int klp_enable_object(struct klp_object *obj) | |||
| 464 | 442 | ||
| 465 | for (func = obj->funcs; func->old_name; func++) { | 443 | for (func = obj->funcs; func->old_name; func++) { |
| 466 | ret = klp_enable_func(func); | 444 | ret = klp_enable_func(func); |
| 467 | if (ret) | 445 | if (ret) { |
| 468 | goto unregister; | 446 | klp_disable_object(obj); |
| 447 | return ret; | ||
| 448 | } | ||
| 469 | } | 449 | } |
| 470 | obj->state = KLP_ENABLED; | 450 | obj->state = KLP_ENABLED; |
| 471 | 451 | ||
| 472 | return 0; | 452 | return 0; |
| 473 | |||
| 474 | unregister: | ||
| 475 | WARN_ON(klp_disable_object(obj)); | ||
| 476 | return ret; | ||
| 477 | } | 453 | } |
| 478 | 454 | ||
| 479 | static int __klp_disable_patch(struct klp_patch *patch) | 455 | static int __klp_disable_patch(struct klp_patch *patch) |
| 480 | { | 456 | { |
| 481 | struct klp_object *obj; | 457 | struct klp_object *obj; |
| 482 | int ret; | ||
| 483 | 458 | ||
| 484 | /* enforce stacking: only the last enabled patch can be disabled */ | 459 | /* enforce stacking: only the last enabled patch can be disabled */ |
| 485 | if (!list_is_last(&patch->list, &klp_patches) && | 460 | if (!list_is_last(&patch->list, &klp_patches) && |
| @@ -489,12 +464,8 @@ static int __klp_disable_patch(struct klp_patch *patch) | |||
| 489 | pr_notice("disabling patch '%s'\n", patch->mod->name); | 464 | pr_notice("disabling patch '%s'\n", patch->mod->name); |
| 490 | 465 | ||
| 491 | for (obj = patch->objs; obj->funcs; obj++) { | 466 | for (obj = patch->objs; obj->funcs; obj++) { |
| 492 | if (obj->state != KLP_ENABLED) | 467 | if (obj->state == KLP_ENABLED) |
| 493 | continue; | 468 | klp_disable_object(obj); |
| 494 | |||
| 495 | ret = klp_disable_object(obj); | ||
| 496 | if (ret) | ||
| 497 | return ret; | ||
| 498 | } | 469 | } |
| 499 | 470 | ||
| 500 | patch->state = KLP_DISABLED; | 471 | patch->state = KLP_DISABLED; |
| @@ -553,8 +524,6 @@ static int __klp_enable_patch(struct klp_patch *patch) | |||
| 553 | pr_notice("enabling patch '%s'\n", patch->mod->name); | 524 | pr_notice("enabling patch '%s'\n", patch->mod->name); |
| 554 | 525 | ||
| 555 | for (obj = patch->objs; obj->funcs; obj++) { | 526 | for (obj = patch->objs; obj->funcs; obj++) { |
| 556 | klp_find_object_module(obj); | ||
| 557 | |||
| 558 | if (!klp_is_object_loaded(obj)) | 527 | if (!klp_is_object_loaded(obj)) |
| 559 | continue; | 528 | continue; |
| 560 | 529 | ||
| @@ -945,7 +914,6 @@ static void klp_module_notify_going(struct klp_patch *patch, | |||
| 945 | { | 914 | { |
| 946 | struct module *pmod = patch->mod; | 915 | struct module *pmod = patch->mod; |
| 947 | struct module *mod = obj->mod; | 916 | struct module *mod = obj->mod; |
| 948 | int ret; | ||
| 949 | 917 | ||
| 950 | if (patch->state == KLP_DISABLED) | 918 | if (patch->state == KLP_DISABLED) |
| 951 | goto disabled; | 919 | goto disabled; |
| @@ -953,10 +921,7 @@ static void klp_module_notify_going(struct klp_patch *patch, | |||
| 953 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | 921 | pr_notice("reverting patch '%s' on unloading module '%s'\n", |
| 954 | pmod->name, mod->name); | 922 | pmod->name, mod->name); |
| 955 | 923 | ||
| 956 | ret = klp_disable_object(obj); | 924 | klp_disable_object(obj); |
| 957 | if (ret) | ||
| 958 | pr_warn("failed to revert patch '%s' on module '%s' (%d)\n", | ||
| 959 | pmod->name, mod->name, ret); | ||
| 960 | 925 | ||
| 961 | disabled: | 926 | disabled: |
| 962 | klp_free_object_loaded(obj); | 927 | klp_free_object_loaded(obj); |
