Received: by 10.223.185.116 with SMTP id b49csp4778313wrg; Wed, 7 Mar 2018 00:24:28 -0800 (PST) X-Google-Smtp-Source: AG47ELvmZT0NvhDf/a09lD26g9TOlXgq8AVc23j/nCIvNBddVsoa7/NQFvqoepbE/POVmcwAkznN X-Received: by 10.98.157.199 with SMTP id a68mr21890253pfk.59.1520411068311; Wed, 07 Mar 2018 00:24:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1520411068; cv=none; d=google.com; s=arc-20160816; b=DDxQYsCUMswphHO5gtzxYE0HYVHY1nJFnyAkGo98t9F18Ul+l9EX4t8KBeYN10Y3PR q4fFK7QanCocs/DaG/AwudRcOb5MhY0iteXlq5Vd2idsDfWKYJP7r7y8QtiF20+DDpWD U6HHNVAVmMh3NbkLX7AiFinY8wBmx/ZJRwy3HC9RlTmNdv6YARVe8jhwgqLpONxxwuU6 KVyLE7l0DsdbjflaGIYksJeCYe99olhx1KFNaO908m/3Pd6+ueqSfU1d/S6p5LSrf/Zj eIJehnU7g9h31gKUxMVKy+cJZKHwrphBYdnkcKF8EpFops65+QhuTlxkWt+i6nDVKaI1 01LA== 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=b0SoyB9+F8h6ySBqFWhG1oyHAFe5baXUULh/TUlfKGs=; b=rvg1gVNfKEVk531AZvo8ATLG+ZR1qVyhpNinX2qclZz1RPWD6AYVBIc99TUUKybwAX 9MqrRuJXUdiptFu/9rcFLEQA+087vcm8b2IJbrnxDNL8dhFmTyE9AO/GHUj5xFVe27KA dzs76ZHIxO/TYJ6MbgP4zxDDQtXY9Tc6pHrBP0Q5hJgeqUQ/wCZ4B10NgrRDPlotsEHV KqlB0Fu7LEnf4Pvj2465Y8qDffxmBnvanGrrVc+6rdsVCE5K7KKZAyJ563m1RX15+ydp 8FuhgnizzGlRsyrg4Pdd5Om1QhlGWlB4LF70TwHexhnwadIlEQgIW9aSH32dPfcfPMgR +99Q== 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 n64si13382496pfb.281.2018.03.07.00.24.14; Wed, 07 Mar 2018 00:24:28 -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 S1754139AbeCGIWt (ORCPT + 99 others); Wed, 7 Mar 2018 03:22:49 -0500 Received: from mx2.suse.de ([195.135.220.15]:50131 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751076AbeCGIU7 (ORCPT ); Wed, 7 Mar 2018 03:20:59 -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 E1728AF93; Wed, 7 Mar 2018 08:20:57 +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 v10 03/10] livepatch: Initial support for dynamic structures Date: Wed, 7 Mar 2018 09:20:32 +0100 Message-Id: <20180307082039.10196-4-pmladek@suse.com> X-Mailer: git-send-email 2.13.6 In-Reply-To: <20180307082039.10196-1-pmladek@suse.com> References: <20180307082039.10196-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 no 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 no 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 Acked-by: Miroslav Benes --- include/linux/livepatch.h | 37 +++++++++++- kernel/livepatch/core.c | 141 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 163 insertions(+), 15 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 69bde95e76f8..a47c26147c17 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 = { @@ -653,14 +745,26 @@ static struct kobj_type klp_ktype_func = { .sysfs_ops = &kobj_sysfs_ops, }; -/* Free all funcs that have the kobject initialized. */ -static void klp_free_funcs(struct klp_object *obj) +/* + * 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, + 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); } } @@ -675,22 +779,34 @@ static void klp_free_object_loaded(struct klp_object *obj) func->old_addr = 0; } -/* Free all funcs and objects that have the kobject initialized. */ -static void klp_free_objects(struct klp_patch *patch) +/* + * 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, 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); @@ -771,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; @@ -834,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