Received: by 2002:a05:7412:a9a2:b0:e2:908c:2ebd with SMTP id o34csp16303rdh; Wed, 25 Oct 2023 14:39:29 -0700 (PDT) X-Google-Smtp-Source: AGHT+IECD218esjxnSghhTXweC5AXXKml7YMtQGBwqZJhbNEmXiCR9zVvvpFUulFAISJP8xoMBPe X-Received: by 2002:a05:690c:f8d:b0:5a7:b9b1:c0bd with SMTP id df13-20020a05690c0f8d00b005a7b9b1c0bdmr19528748ywb.11.1698269969077; Wed, 25 Oct 2023 14:39:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698269969; cv=none; d=google.com; s=arc-20160816; b=QslRKQPS4VF4t70ezm55LFZWdX00G7mDjB8yGP1eMlNc/hlcuoBeswWrYnIKR8Un93 JQyTm3sKQtpiRTmbgQ/PMncy7ajYlQXSlA8uam9fnjSBmaqHo3xMFXCc67kp3ilwXc8y W0BCcbE39K6tItpgDGvGmQBIpIoVWvkc6LD9y/kdkpI7TzQ4IOOVO9P1DmQXJYPE6evq 0qgBY0x8G1DXQYrE3lkDql/mCqJP7JSInUYAgNy9onLpBjuW6gPq1lmCY+GufRe650As eru2XPQF2FkoA56NdvYb02NhaP4P12+nLfkXQ7HuvNz0gEWrrXNAPNEJO5hnzs0Nku06 zWxg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:message-id:content-transfer-encoding :mime-version:subject:date:from:dkim-signature; bh=zXP1WKDtEBgcaJWBtUcGxE1VhtguZ3Lb29YKMsV+dJI=; fh=un5mFvVRlLDh/Iryd2LEk7PxFbRndB1NuLZaPSbmDp8=; b=sBOcAJk0ptApI9wLxu019vsilmNmb2MdX71xH3zpd7Dv6hvStRGX6PwqHFsrEyaOTZ hbUbYWd0AB0W0oCbxWUzC+ul/V9KgU65P14YYAdlpzsTqjptc2DUxmplRQOy7j32GB5K b8EoGixEWmKDb6F57sIvti61cMz6ic/u6UdO7DGJMMeMBCvu/qYQhlcVvhql8l8r/x+U ru6tWQgop9fDxHVrz527arcgy5yoFS1hy1kU+Mqn4wgAIVpuhFP5o6d3v85I5hNq+Pgu Slo7ldK4ZKvBXLhRevo0aqLT8BsFVC6+c2n8tMqoIF4Lf8wDmCNBc9mWhEqwwWYOlIt3 xdXA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=Ht77Mgbl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from snail.vger.email (snail.vger.email. [23.128.96.37]) by mx.google.com with ESMTPS id h2-20020a0dde02000000b0059eed9c2eb7si13230962ywe.332.2023.10.25.14.39.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 25 Oct 2023 14:39:29 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) client-ip=23.128.96.37; Authentication-Results: mx.google.com; dkim=pass header.i=@intel.com header.s=Intel header.b=Ht77Mgbl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.37 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 4843581896E8; Wed, 25 Oct 2023 14:39:27 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230236AbjJYVjZ (ORCPT + 99 others); Wed, 25 Oct 2023 17:39:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50800 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229672AbjJYVjY (ORCPT ); Wed, 25 Oct 2023 17:39:24 -0400 Received: from mgamail.intel.com (mgamail.intel.com [134.134.136.24]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DB7C113A for ; Wed, 25 Oct 2023 14:39:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1698269961; x=1729805961; h=from:date:subject:mime-version:content-transfer-encoding: message-id:to:cc; bh=M9YR0XWgWOmEjasdaF5dvVIdmizBqpKx0yyPpSgRQEE=; b=Ht77Mgblu311F+jcWZzC0kO8EdTLQDtqF45MKDUbanFi91w8oda2KYru QTdmRQXWLcuF6YlRhc0zaYMOIPE+81cqAOTr2FhK5mQrvmOnYvd/Yj3xg 24v6oFnlSlKmMWmAmCaiAAYpRZoOq4IsoQ3+h9SPDxB82jkhA91pXLwKA 4L1eJ4MYUP0PRraWdYGREzf28lcEcz+fWesBNxEO4NV5kEjWLO1nxsDqm h9tsVktIRAbeibVwCc3ZMK5AIL0Dmn568Jhd7iCw8RkL3wsEDew/ZHaGO c9RLkDzGeFKAZgXS0WG3lcVOgzilcxr35S6a073ep7/nuC/Quix2b0/0h w==; X-IronPort-AV: E=McAfee;i="6600,9927,10874"; a="390263662" X-IronPort-AV: E=Sophos;i="6.03,250,1694761200"; d="scan'208";a="390263662" Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Oct 2023 14:39:19 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10874"; a="849677457" X-IronPort-AV: E=Sophos;i="6.03,250,1694761200"; d="scan'208";a="849677457" Received: from lab-ah.igk.intel.com ([10.102.138.202]) by fmsmga003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 Oct 2023 14:39:16 -0700 From: Andrzej Hajda Date: Wed, 25 Oct 2023 23:39:07 +0200 Subject: [PATCH v3] debugobjects: stop accessing objects after releasing spinlock MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20231025-debugobjects_fix-v3-1-2bc3bf7084c2@intel.com> X-B4-Tracking: v=1; b=H4sIAPqKOWUC/z3MTQ7CIBCG4as0rKUWKK115T2MMQNMC40FA/UnN r271IXLdzLfs5CE0WEix2IhEZ8uueBziF1BtAU/IHUmN+EVF6zikhpUjyGoEfWcrr1706ZByTs uZatrkmf3iPn8I8+X3H0ME51tRPhDVX5nggnZlbyrDwfeUkbBm/jBsbQwGjg5P+Ot1GHaSAUJq Yrgtd0IVe9foBRZ1y/zpBUyvwAAAA== To: linux-kernel@vger.kernel.org, intel-gfx@lists.freedesktop.org, linux-mm@kvack.org Cc: Thomas Gleixner , Nirmoy Das , Andi Shyti , Janusz Krzysztofik , Andrzej Hajda X-Mailer: b4 0.12.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=11404; i=andrzej.hajda@intel.com; h=from:subject:message-id; bh=M9YR0XWgWOmEjasdaF5dvVIdmizBqpKx0yyPpSgRQEE=; b=owEB7QES/pANAwAKASNispPeEP3XAcsmYgBlOYsDrBMPiLwxsaT9p7frP5Jp6GZUcLUmVQuloKaH 0KwwZ0OJAbMEAAEKAB0WIQT8qEQxNN2/XeF/A00jYrKT3hD91wUCZTmLAwAKCRAjYrKT3hD910unC/ 9guaFzM2bfO+yD/g91t70H8/PquAzFoHsNO9DiLcYCUy0lVikzyP0xydf1mfrpC1M4zua8pdhsmkUJ DJ4o1CJ2ofv4Z8kBXJ2P2J/BCmzIfcl6dDYWFJ3D5TOwdG09AC1ftUET1OtRXh1xPV8YjH9+EMGdcK L3s+dap7EOqC5DCXYAp8mk6VYiZOCf9dJzmq9TpW619cEnqhfKT4XA2fXXMwyYSmLtxok3kAqvS+8B F3AqbqvoljTNxSnGZ/IOCk7jaBlA8gjbbI3IQLSSpmxHFlhuWMIRn6aCePS2LhSlKt4pe+uyM+uQZC MZKBMP/FnZp6xFB1GaCpid0yfnrkYdFw+N0iHDJ4e6W/dMHFZ94VsZDcFW0bsH9c3x4/I2gEUhgoWB EQYr09yu9uASGfdmEQxZWs5wXdRZnu62jQQ+kQbr97HgfSHv28i8GJAU+Mn1y1KdZEE5pVegvlwLqI kIFmWqUEu5IhaIRv7gpQFpOnqgX+5V7Hvwnl75KH25b1Y= X-Developer-Key: i=andrzej.hajda@intel.com; a=openpgp; fpr=FCA8443134DDBF5DE17F034D2362B293DE10FDD7 X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (snail.vger.email [0.0.0.0]); Wed, 25 Oct 2023 14:39:27 -0700 (PDT) After spinlock release object can be modified/freed by concurrent thread. Using it in such case is error prone, even for printing object state. To avoid such situation local copy of the object is created if necessary. Sample buggy scenario: 1. Thread tries to deactivate destroyed object, debugobjects detects it, spin lock is released, thread is preempted. 2. Other thread frees debugobject, then allocates new one on the same memory location, ie 'obj' variable from 1st thread point to it - it is possible because there is no locking. 3. Then preemption occurs, and 1st thread reports error for wrong object. Signed-off-by: Andrzej Hajda --- v2: add missing switch breaks v3: abandon single-point-of-unlock approach --- lib/debugobjects.c | 196 +++++++++++++++++++++-------------------------------- 1 file changed, 77 insertions(+), 119 deletions(-) diff --git a/lib/debugobjects.c b/lib/debugobjects.c index a517256a270b71..c074dbbec084a6 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -620,9 +620,8 @@ static void debug_objects_fill_pool(void) static void __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack) { - enum debug_obj_state state; struct debug_bucket *db; - struct debug_obj *obj; + struct debug_obj *obj, o; unsigned long flags; debug_objects_fill_pool(); @@ -643,24 +642,18 @@ __debug_object_init(void *addr, const struct debug_obj_descr *descr, int onstack case ODEBUG_STATE_INIT: case ODEBUG_STATE_INACTIVE: obj->state = ODEBUG_STATE_INIT; - break; - - case ODEBUG_STATE_ACTIVE: - state = obj->state; raw_spin_unlock_irqrestore(&db->lock, flags); - debug_print_object(obj, "init"); - debug_object_fixup(descr->fixup_init, addr, state); - return; - - case ODEBUG_STATE_DESTROYED: - raw_spin_unlock_irqrestore(&db->lock, flags); - debug_print_object(obj, "init"); return; default: break; } + o = *obj; raw_spin_unlock_irqrestore(&db->lock, flags); + debug_print_object(&o, "init"); + + if (o.state == ODEBUG_STATE_ACTIVE) + debug_object_fixup(descr->fixup_init, addr, o.state); } /** @@ -701,11 +694,9 @@ EXPORT_SYMBOL_GPL(debug_object_init_on_stack); int debug_object_activate(void *addr, const struct debug_obj_descr *descr) { struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr }; - enum debug_obj_state state; struct debug_bucket *db; struct debug_obj *obj; unsigned long flags; - int ret; if (!debug_objects_enabled) return 0; @@ -717,49 +708,38 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr) raw_spin_lock_irqsave(&db->lock, flags); obj = lookup_object_or_alloc(addr, db, descr, false, true); - if (likely(!IS_ERR_OR_NULL(obj))) { - bool print_object = false; - + if (unlikely(!obj)) { + raw_spin_unlock_irqrestore(&db->lock, flags); + debug_objects_oom(); + return 0; + } else if (likely(!IS_ERR(obj))) { switch (obj->state) { - case ODEBUG_STATE_INIT: - case ODEBUG_STATE_INACTIVE: - obj->state = ODEBUG_STATE_ACTIVE; - ret = 0; - break; - case ODEBUG_STATE_ACTIVE: - state = obj->state; - raw_spin_unlock_irqrestore(&db->lock, flags); - debug_print_object(obj, "activate"); - ret = debug_object_fixup(descr->fixup_activate, addr, state); - return ret ? 0 : -EINVAL; - case ODEBUG_STATE_DESTROYED: - print_object = true; - ret = -EINVAL; break; + case ODEBUG_STATE_INIT: + case ODEBUG_STATE_INACTIVE: + obj->state = ODEBUG_STATE_ACTIVE; + fallthrough; default: - ret = 0; - break; + raw_spin_unlock_irqrestore(&db->lock, flags); + return 0; } - raw_spin_unlock_irqrestore(&db->lock, flags); - if (print_object) - debug_print_object(obj, "activate"); - return ret; } + o = *obj; raw_spin_unlock_irqrestore(&db->lock, flags); + debug_print_object(&o, "activate"); - /* If NULL the allocation has hit OOM */ - if (!obj) { - debug_objects_oom(); - return 0; + switch (o.state) { + case ODEBUG_STATE_ACTIVE: + case ODEBUG_STATE_NOTAVAILABLE: + if (debug_object_fixup(descr->fixup_activate, addr, o.state)) + return 0; + fallthrough; + default: + return -EINVAL; } - - /* Object is neither static nor tracked. It's not initialized */ - debug_print_object(&o, "activate"); - ret = debug_object_fixup(descr->fixup_activate, addr, ODEBUG_STATE_NOTAVAILABLE); - return ret ? 0 : -EINVAL; } EXPORT_SYMBOL_GPL(debug_object_activate); @@ -770,10 +750,10 @@ EXPORT_SYMBOL_GPL(debug_object_activate); */ void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr) { + struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr }; struct debug_bucket *db; struct debug_obj *obj; unsigned long flags; - bool print_object = false; if (!debug_objects_enabled) return; @@ -785,33 +765,24 @@ void debug_object_deactivate(void *addr, const struct debug_obj_descr *descr) obj = lookup_object(addr, db); if (obj) { switch (obj->state) { + case ODEBUG_STATE_DESTROYED: + break; case ODEBUG_STATE_INIT: case ODEBUG_STATE_INACTIVE: case ODEBUG_STATE_ACTIVE: - if (!obj->astate) - obj->state = ODEBUG_STATE_INACTIVE; - else - print_object = true; - break; - - case ODEBUG_STATE_DESTROYED: - print_object = true; - break; + if (obj->astate) + break; + obj->state = ODEBUG_STATE_INACTIVE; + fallthrough; default: - break; + raw_spin_unlock_irqrestore(&db->lock, flags); + return; } + o = *obj; } raw_spin_unlock_irqrestore(&db->lock, flags); - if (!obj) { - struct debug_obj o = { .object = addr, - .state = ODEBUG_STATE_NOTAVAILABLE, - .descr = descr }; - - debug_print_object(&o, "deactivate"); - } else if (print_object) { - debug_print_object(obj, "deactivate"); - } + debug_print_object(&o, "deactivate"); } EXPORT_SYMBOL_GPL(debug_object_deactivate); @@ -822,11 +793,9 @@ EXPORT_SYMBOL_GPL(debug_object_deactivate); */ void debug_object_destroy(void *addr, const struct debug_obj_descr *descr) { - enum debug_obj_state state; struct debug_bucket *db; - struct debug_obj *obj; + struct debug_obj *obj, o; unsigned long flags; - bool print_object = false; if (!debug_objects_enabled) return; @@ -836,32 +805,31 @@ void debug_object_destroy(void *addr, const struct debug_obj_descr *descr) raw_spin_lock_irqsave(&db->lock, flags); obj = lookup_object(addr, db); - if (!obj) - goto out_unlock; + if (!obj) { + raw_spin_unlock_irqrestore(&db->lock, flags); + return; + } switch (obj->state) { + case ODEBUG_STATE_ACTIVE: + case ODEBUG_STATE_DESTROYED: + break; case ODEBUG_STATE_NONE: case ODEBUG_STATE_INIT: case ODEBUG_STATE_INACTIVE: obj->state = ODEBUG_STATE_DESTROYED; - break; - case ODEBUG_STATE_ACTIVE: - state = obj->state; + fallthrough; + default: raw_spin_unlock_irqrestore(&db->lock, flags); - debug_print_object(obj, "destroy"); - debug_object_fixup(descr->fixup_destroy, addr, state); return; - - case ODEBUG_STATE_DESTROYED: - print_object = true; - break; - default: - break; } -out_unlock: + + o = *obj; raw_spin_unlock_irqrestore(&db->lock, flags); - if (print_object) - debug_print_object(obj, "destroy"); + debug_print_object(&o, "destroy"); + + if (o.state == ODEBUG_STATE_ACTIVE) + debug_object_fixup(descr->fixup_destroy, addr, o.state); } EXPORT_SYMBOL_GPL(debug_object_destroy); @@ -872,9 +840,8 @@ EXPORT_SYMBOL_GPL(debug_object_destroy); */ void debug_object_free(void *addr, const struct debug_obj_descr *descr) { - enum debug_obj_state state; struct debug_bucket *db; - struct debug_obj *obj; + struct debug_obj *obj, o; unsigned long flags; if (!debug_objects_enabled) @@ -885,24 +852,26 @@ void debug_object_free(void *addr, const struct debug_obj_descr *descr) raw_spin_lock_irqsave(&db->lock, flags); obj = lookup_object(addr, db); - if (!obj) - goto out_unlock; + if (!obj) { + raw_spin_unlock_irqrestore(&db->lock, flags); + return; + } switch (obj->state) { case ODEBUG_STATE_ACTIVE: - state = obj->state; - raw_spin_unlock_irqrestore(&db->lock, flags); - debug_print_object(obj, "free"); - debug_object_fixup(descr->fixup_free, addr, state); - return; + break; default: hlist_del(&obj->node); raw_spin_unlock_irqrestore(&db->lock, flags); free_object(obj); return; } -out_unlock: + + o = *obj; raw_spin_unlock_irqrestore(&db->lock, flags); + debug_print_object(&o, "free"); + + debug_object_fixup(descr->fixup_free, addr, o.state); } EXPORT_SYMBOL_GPL(debug_object_free); @@ -954,10 +923,10 @@ void debug_object_active_state(void *addr, const struct debug_obj_descr *descr, unsigned int expect, unsigned int next) { + struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr }; struct debug_bucket *db; struct debug_obj *obj; unsigned long flags; - bool print_object = false; if (!debug_objects_enabled) return; @@ -970,28 +939,20 @@ debug_object_active_state(void *addr, const struct debug_obj_descr *descr, if (obj) { switch (obj->state) { case ODEBUG_STATE_ACTIVE: - if (obj->astate == expect) + if (obj->astate == expect) { obj->astate = next; - else - print_object = true; + raw_spin_unlock_irqrestore(&db->lock, flags); + return; + } break; - default: - print_object = true; break; } + o = *obj; } raw_spin_unlock_irqrestore(&db->lock, flags); - if (!obj) { - struct debug_obj o = { .object = addr, - .state = ODEBUG_STATE_NOTAVAILABLE, - .descr = descr }; - - debug_print_object(&o, "active_state"); - } else if (print_object) { - debug_print_object(obj, "active_state"); - } + debug_print_object(&o, "active_state"); } EXPORT_SYMBOL_GPL(debug_object_active_state); @@ -999,11 +960,9 @@ EXPORT_SYMBOL_GPL(debug_object_active_state); static void __debug_check_no_obj_freed(const void *address, unsigned long size) { unsigned long flags, oaddr, saddr, eaddr, paddr, chunks; - const struct debug_obj_descr *descr; - enum debug_obj_state state; struct debug_bucket *db; struct hlist_node *tmp; - struct debug_obj *obj; + struct debug_obj *obj, o; int cnt, objs_checked = 0; saddr = (unsigned long) address; @@ -1026,12 +985,11 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) switch (obj->state) { case ODEBUG_STATE_ACTIVE: - descr = obj->descr; - state = obj->state; + o = *obj; raw_spin_unlock_irqrestore(&db->lock, flags); - debug_print_object(obj, "free"); - debug_object_fixup(descr->fixup_free, - (void *) oaddr, state); + debug_print_object(&o, "free"); + debug_object_fixup(o.descr->fixup_free, + (void *) oaddr, o.state); goto repeat; default: hlist_del(&obj->node); --- base-commit: 201c8a7bd1f3f415920a2df4b8a8817e973f42fe change-id: 20231025-debugobjects_fix-66e5292557c4 Best regards, -- Andrzej Hajda