Received: by 10.223.176.5 with SMTP id f5csp499862wra; Tue, 6 Feb 2018 02:37:26 -0800 (PST) X-Google-Smtp-Source: AH8x224Swczrb5lpPrLR6inzlTvel8nDG20uKnBnV288IyaVRfm1MyCmy6dlOgNx0ekNNqYThHY5 X-Received: by 2002:a17:902:4003:: with SMTP id b3-v6mr2007996pld.154.1517913446396; Tue, 06 Feb 2018 02:37:26 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517913446; cv=none; d=google.com; s=arc-20160816; b=mz6xOpLkXfFJILP0FD5+8l3gEnsSukJZdde+N2BEJAhbIxIxwi3rHL7SNdk5//smpU 8ZAXvTahNh29JkRjEZsoC53DyHaJWDHQ9SL3BvzZRpEqtbFfr/GVaV5P8gWkcn6+kchf yK3Mj9Gx6z3DXDpDpgZrJe3SjW526yyDe2RBnQ7nsON8lqboHeXxVhW/bNBoKRpy5GkJ WF4rYfloQh0fzVO0GSwI/W9GAvNwtRZfXp2d2Sl/Ss4qFDjSn68qx2DITTZfBBMpyX2v U8I646P3wppF1TXb4NGZO3QYjrhNyBEEMJXj4/B6J/Lq79ywycc6eFa8Zvfgn3Kibj+G jgEA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=XI/Utc84lEEb1IhY9UBDQt0Oz9U30/r0wXvMz58uqJ0=; b=AfOU8KhWJVXBMqxUh7ZHLQrgSAlpcEkr2p3tzKRqGxIuEBAk54caXMmGBk+JW8Gph/ 75Z35D5mJvD3PY6YEb//a6hDb5qBKCIY/9pf5uz7xUpwk/j2RURtGzonxQIUacGEq0PP 3K5V864I+has74OyKe+HqBUPkegYihvNf3fdaDocUhnxQwgxp4VwvXXFFKKoRsMHxPtF j8uWWhT8iG49DqlFfgSyyz4ZIJsswiFG/1Q1+FIdcYRaYYVazvW1Pimnojz2IApvk11q B6t2CB9pGB0s9czIwyRi5WBSTe70RG3gJsQeOsj88kKy9d/tvCff0Vmw5g2tCZNVtb/J XJvQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id g3-v6si8375541pld.270.2018.02.06.02.37.12; Tue, 06 Feb 2018 02:37:26 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752988AbeBFKgS (ORCPT + 99 others); Tue, 6 Feb 2018 05:36:18 -0500 Received: from mx2.suse.de ([195.135.220.15]:59968 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752801AbeBFKeu (ORCPT ); Tue, 6 Feb 2018 05:34:50 -0500 X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id BE317AC42; Tue, 6 Feb 2018 10:34:48 +0000 (UTC) From: Petr Mladek To: Jiri Kosina , Josh Poimboeuf , Miroslav Benes Cc: Jason Baron , Joe Lawrence , Jessica Yu , Evgenii Shatokhin , live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, Petr Mladek Subject: [PATCH v7 3/7] livepatch: Initial support for dynamic structures Date: Tue, 6 Feb 2018 11:34:20 +0100 Message-Id: <20180206103424.10829-4-pmladek@suse.com> X-Mailer: git-send-email 2.13.6 In-Reply-To: <20180206103424.10829-1-pmladek@suse.com> References: <20180206103424.10829-1-pmladek@suse.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Jason Baron We are going to add a feature called atomic replace. It will allow to create a patch that would replace all already registered patches. For this, we will need to dynamically create funcs and objects for functions that are not longer patched. This patch adds basic framework to handle such dynamic structures. It adds enum klp_func_type that allows to distinguish the dynamically allocated funcs' structures. Note that objects' structures do not have a clear type. Namely the static objects' structures might list both static and dynamic funcs' structures. The function type is then used to limit klp_free() functions. We will want to free the dynamic structures separately when they are not longer needed. At the same time, we also want to make our life easier, and introduce _any_ type that will allow to process all existing structures in one go. We need to be careful here. First, objects' structures must be freed only when all listed funcs' structures are freed. Also we must avoid double free. Both problems are solved by removing the freed structures from the list. Also note that klp_free*() functions are called also in klp_init_patch() error path when only some kobjects have been initialized. The other dynamic structures must be freed immediately by calling the respective klp_free_*_dynamic() functions. Finally, the dynamic objects' structures are generic. The respective klp_allocate_object_dynamic() and klp_free_object_dynamic() can be implemented here. On the other hand, klp_free_func_dynamic() is empty. It must be updated when a particular dynamic klp_func_type is introduced. Signed-off-by: Jason Baron [pmladek@suse.com: Converted into a generic API] Signed-off-by: Petr Mladek Cc: Josh Poimboeuf Cc: Jessica Yu Cc: Jiri Kosina Cc: Miroslav Benes --- include/linux/livepatch.h | 37 +++++++++++- kernel/livepatch/core.c | 139 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 159 insertions(+), 17 deletions(-) diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index e5db2ba7e2a5..7fb76e7d2693 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -35,12 +35,22 @@ #define KLP_UNPATCHED 0 #define KLP_PATCHED 1 +/* + * Function type is used to distinguish dynamically allocated structures + * and limit some operations. + */ +enum klp_func_type { + KLP_FUNC_ANY = -1, /* Substitute any type */ + KLP_FUNC_STATIC = 0, /* Original statically defined structure */ +}; + /** * struct klp_func - function structure for live patching * @old_name: name of the function to be patched * @new_func: pointer to the patched function code * @old_sympos: a hint indicating which symbol position the old function * can be found (optional) + * @ftype: distinguish static and dynamic structures * @old_addr: the address of the function being patched * @kobj: kobject for sysfs resources * @stack_node: list node for klp_ops func_stack list @@ -79,6 +89,7 @@ struct klp_func { unsigned long old_sympos; /* internal */ + enum klp_func_type ftype; unsigned long old_addr; struct kobject kobj; struct list_head stack_node; @@ -164,17 +175,41 @@ struct klp_patch { #define klp_for_each_object_static(patch, obj) \ for (obj = patch->objs; obj->funcs || obj->name; obj++) +#define klp_for_each_object_safe(patch, obj, tmp_obj) \ + list_for_each_entry_safe(obj, tmp_obj, &patch->obj_list, obj_entry) + #define klp_for_each_object(patch, obj) \ list_for_each_entry(obj, &patch->obj_list, obj_entry) +/* Support also dynamically allocated struct klp_object */ #define klp_for_each_func_static(obj, func) \ for (func = obj->funcs; \ - func->old_name || func->new_func || func->old_sympos; \ + func && (func->old_name || func->new_func || func->old_sympos); \ func++) +#define klp_for_each_func_safe(obj, func, tmp_func) \ + list_for_each_entry_safe(func, tmp_func, &obj->func_list, func_entry) + #define klp_for_each_func(obj, func) \ list_for_each_entry(func, &obj->func_list, func_entry) +static inline bool klp_is_object_dynamic(struct klp_object *obj) +{ + return !obj->funcs; +} + +static inline bool klp_is_func_dynamic(struct klp_func *func) +{ + WARN_ON_ONCE(func->ftype == KLP_FUNC_ANY); + return func->ftype != KLP_FUNC_STATIC; +} + +static inline bool klp_is_func_type(struct klp_func *func, + enum klp_func_type ftype) +{ + return ftype == KLP_FUNC_ANY || ftype == func->ftype; +} + int klp_register_patch(struct klp_patch *); int klp_unregister_patch(struct klp_patch *); int klp_enable_patch(struct klp_patch *); diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index ef7c3b5f561b..ab3e13b71bfd 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -124,6 +124,26 @@ static bool klp_initialized(void) return !!klp_root_kobj; } +static struct klp_object *klp_find_object(struct klp_patch *patch, + struct klp_object *old_obj) +{ + struct klp_object *obj; + bool mod = klp_is_module(old_obj); + + klp_for_each_object(patch, obj) { + if (mod) { + if (klp_is_module(obj) && + strcmp(old_obj->name, obj->name) == 0) { + return obj; + } + } else if (!klp_is_module(obj)) { + return obj; + } + } + + return NULL; +} + struct klp_find_arg { const char *objname; const char *name; @@ -621,6 +641,66 @@ static struct attribute *klp_patch_attrs[] = { NULL }; +/* + * Dynamically allocated objects and functions. + */ +static void klp_free_func_dynamic(struct klp_func *func) +{ +} + +static void klp_free_object_dynamic(struct klp_object *obj) +{ + kfree(obj->name); + kfree(obj); +} + +static struct klp_object *klp_alloc_object_dynamic(const char *name) +{ + struct klp_object *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return ERR_PTR(-ENOMEM); + + if (name) { + obj->name = kstrdup(name, GFP_KERNEL); + if (!obj->name) { + kfree(obj); + return ERR_PTR(-ENOMEM); + } + } + + return obj; +} + +static struct klp_object *klp_get_or_add_object(struct klp_patch *patch, + struct klp_object *old_obj) +{ + struct klp_object *obj; + + obj = klp_find_object(patch, old_obj); + if (obj) + return obj; + + obj = klp_alloc_object_dynamic(old_obj->name); + if (IS_ERR(obj)) + return obj; + + klp_init_object_list(patch, obj); + return obj; +} + +/* + * Patch release framework must support the following scenarios: + * + * + Asynchonous release is used when kobjects are initialized. + * + * + Direct release is used in error paths for structures that + * have not had kobj initialized yet. + * + * + Allow to release dynamic structures of the given type when + * they are not longer needed. + */ static void klp_kobj_release_patch(struct kobject *kobj) { struct klp_patch *patch; @@ -637,6 +717,12 @@ static struct kobj_type klp_ktype_patch = { static void klp_kobj_release_object(struct kobject *kobj) { + struct klp_object *obj; + + obj = container_of(kobj, struct klp_object, kobj); + + if (klp_is_object_dynamic(obj)) + klp_free_object_dynamic(obj); } static struct kobj_type klp_ktype_object = { @@ -646,6 +732,12 @@ static struct kobj_type klp_ktype_object = { static void klp_kobj_release_func(struct kobject *kobj) { + struct klp_func *func; + + func = container_of(kobj, struct klp_func, kobj); + + if (klp_is_func_dynamic(func)) + klp_free_func_dynamic(func); } static struct kobj_type klp_ktype_func = { @@ -654,16 +746,25 @@ static struct kobj_type klp_ktype_func = { }; /* - * Free all funcs that have the kobject initialized. Otherwise, - * nothing is needed. + * Free all funcs of the given ftype. Use the kobject when it has already + * been initialized. Otherwise, do it directly. */ -static void klp_free_funcs(struct klp_object *obj) +static void klp_free_funcs(struct klp_object *obj, + enum klp_func_type ftype) { - struct klp_func *func; + struct klp_func *func, *tmp_func; + + klp_for_each_func_safe(obj, func, tmp_func) { + if (!klp_is_func_type(func, ftype)) + continue; + + /* Avoid double free and allow to detect empty objects. */ + list_del(&func->func_entry); - klp_for_each_func(obj, func) { if (func->kobj.state_initialized) kobject_put(&func->kobj); + else if (klp_is_func_dynamic(func)) + klp_free_func_dynamic(func); } } @@ -679,24 +780,33 @@ static void klp_free_object_loaded(struct klp_object *obj) } /* - * Free all funcs and objects that have the kobject initialized. - * Otherwise, nothing is needed. + * Free all linked funcs of the given ftype. Then free empty objects. + * Use the kobject when it has already been initialized. Otherwise, + * do it directly. */ -static void klp_free_objects(struct klp_patch *patch) +void klp_free_objects(struct klp_patch *patch, enum klp_func_type ftype) { - struct klp_object *obj; + struct klp_object *obj, *tmp_obj; - klp_for_each_object(patch, obj) { - klp_free_funcs(obj); + klp_for_each_object_safe(patch, obj, tmp_obj) { + klp_free_funcs(obj, ftype); + + if (!list_empty(&obj->func_list)) + continue; + + /* Avoid freeing the object twice. */ + list_del(&obj->obj_entry); if (obj->kobj.state_initialized) kobject_put(&obj->kobj); + else if (klp_is_object_dynamic(obj)) + klp_free_object_dynamic(obj); } } static void klp_free_patch(struct klp_patch *patch) { - klp_free_objects(patch); + klp_free_objects(patch, KLP_FUNC_ANY); if (!list_empty(&patch->list)) list_del(&patch->list); @@ -777,9 +887,6 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj) int ret; const char *name; - if (!obj->funcs) - return -EINVAL; - obj->patched = false; obj->mod = NULL; @@ -840,7 +947,7 @@ static int klp_init_patch(struct klp_patch *patch) return 0; free: - klp_free_objects(patch); + klp_free_objects(patch, KLP_FUNC_ANY); mutex_unlock(&klp_mutex); -- 2.13.6