From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B49AC83F1A for ; Fri, 11 Jul 2025 01:57:15 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id E55BA8D0005; Thu, 10 Jul 2025 21:57:14 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id E07048D0001; Thu, 10 Jul 2025 21:57:14 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id CF6368D0005; Thu, 10 Jul 2025 21:57:14 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0010.hostedemail.com [216.40.44.10]) by kanga.kvack.org (Postfix) with ESMTP id BCA818D0001 for ; Thu, 10 Jul 2025 21:57:14 -0400 (EDT) Received: from smtpin13.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay04.hostedemail.com (Postfix) with ESMTP id 8FA141A12CA for ; Fri, 11 Jul 2025 01:57:14 +0000 (UTC) X-FDA: 83650321188.13.BA075F8 Received: from nyc.source.kernel.org (nyc.source.kernel.org [147.75.193.91]) by imf11.hostedemail.com (Postfix) with ESMTP id DEE4740013 for ; Fri, 11 Jul 2025 01:57:12 +0000 (UTC) Authentication-Results: imf11.hostedemail.com; dkim=pass header.d=kernel.org header.s=k20201202 header.b=NHhCnKgm; dmarc=pass (policy=quarantine) header.from=kernel.org; spf=pass (imf11.hostedemail.com: domain of alx@kernel.org designates 147.75.193.91 as permitted sender) smtp.mailfrom=alx@kernel.org ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1752199032; a=rsa-sha256; cv=none; b=HnOKqU/F6lmjWyuWuHBeVLa3lB2uXCixzr9qZjpBt+XlK5u+pCDu9FmB9fm2DKvpeEt4GS yt1dDGtkkV1qYdx+JN4cCxOQQQi+TpInjcSPfmHUpwQ/jjATzW0eIidcL7GRd9BnEyQUew M9jBMjM4u/BfWALnv5Xot3qVc/GV/zA= ARC-Authentication-Results: i=1; imf11.hostedemail.com; dkim=pass header.d=kernel.org header.s=k20201202 header.b=NHhCnKgm; dmarc=pass (policy=quarantine) header.from=kernel.org; spf=pass (imf11.hostedemail.com: domain of alx@kernel.org designates 147.75.193.91 as permitted sender) smtp.mailfrom=alx@kernel.org ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1752199032; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=++evECV9nwKSxaMYRtG3Dl+zFkiyxCKX7uNVyUkPMKg=; b=Lsvjd6tj7XHBiPBL5lhjfQCjoowT3TRDDTxMbtxTGNPI34vG7eZVLxhmTtlzpdoTVCQ+Mi j+PNx8TTUSUj5MDATGtB//uQqsXKJ3zm0vP7JdgR3HwPQZkVIcehRdJ+LsQLb/q61mmSIj 81REp+tmMqL4IMZUmGuzxTjghgSoiI8= Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by nyc.source.kernel.org (Postfix) with ESMTP id 36E3EA54B34; Fri, 11 Jul 2025 01:57:12 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3E2D8C4CEE3; Fri, 11 Jul 2025 01:57:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1752199031; bh=stTpsWR5B9qnU4lcgeg0tYr2I54PzkVTHHPWz1Iz+X8=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=NHhCnKgmG9jHKzaW9+iAljhm/h50l3wWsWTMssrurSEQzbFcDMsxQYw4EGK69U+3G Uajs6mZ0vvgIt/Z/z3/GwqaDBuFliwl9W5xbEnqkZn3i8s0YsrwJ5pm4usNyvvzjpt acUWRGkJNk/ilaTNM0FkzzxXz8g/21TFz7iak2WywEYviNC+/jTYtEyclexGZ+iu6K YG1GeVk1GfNVCTmGVZxm28IWh27RONOXcQKTM4FspjeAvPNvEktbDlbWMhqO31GnsJ 9x/E8KUXayy9rWiJspo+aFLL7b9PKiPeEfws3rcunNSfy89u6+yNUz8TbY9A22uZGy ikR2FuLZZ9JYg== Date: Fri, 11 Jul 2025 03:57:04 +0200 From: Alejandro Colomar To: linux-mm@kvack.org, linux-hardening@vger.kernel.org Cc: Alejandro Colomar , Kees Cook , Christopher Bazley , shadow <~hallyn/shadow@lists.sr.ht>, linux-kernel@vger.kernel.org, Andrew Morton , kasan-dev@googlegroups.com, Dmitry Vyukov , Alexander Potapenko , Marco Elver , Christoph Lameter , David Rientjes , Vlastimil Babka , Roman Gushchin , Harry Yoo , Andrew Clayton , Rasmus Villemoes , Michal Hocko , Linus Torvalds , Al Viro , Martin Uecker , Sam James , Andrew Pinski , Sven Schnelle , Heiko Carstens , Tvrtko Ursulin , Christophe JAILLET , Hyeonggon Yoo <42.hyeyoo@gmail.com>, Chao Yu Subject: [RFC v6 5/8] mm: Use sprintf_end() instead of less ergonomic APIs Message-ID: <8a0ffc1bf43d60e1654c10546b9f615c38f604f0.1752193588.git.alx@kernel.org> X-Mailer: git-send-email 2.50.0 References: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: X-Rspamd-Server: rspam11 X-Rspam-User: X-Stat-Signature: 8yggx8z7ezeqxmxy6gwssnaf59s4gsp4 X-Rspamd-Queue-Id: DEE4740013 X-HE-Tag: 1752199032-574152 X-HE-Meta: U2FsdGVkX19XOljGwoQuczp4K4TxI0ajPpQ/yXelRMe0/VBkh0aHf/de04VikycMj6OQtcLQZbIANKD2NCQ/LAVxqk65T6DD6Q1jieZA9+IBuOekWIkVjEl5aMbEa1nRiKlLdzNj1qGTc3SmHE7+NLDpEBYD88f/myp3uFIrY8lIvyc9PzgSH6C2u3P/pWzjTSj+uW0jlxR6nlA9n49H967QQlp8thZwsZ9+0c1MIAWeLC6hP0tkIBZ0WnYIUwGxYmuIaPsqHd5YWtlCLBrKcuI23q9hrNqaFpuBe1IA2Zmp8todE64yPYC09u9OqbiMujMedETsix8pa/JEp+uwvxF3zVkE/+RIXvv0HVTEmrp6o2uE+gmgw70Fc0dPvIgllqdrf7/i/SFA5+rjASbckmP2XX01r/sjs4/zUipEn1Bo5CCje1l6101iUBvTPF/bewOvkZqj9jDO5ieSVorwlJyXLK2gHoXZ18+cx05xsN20CfJXixEopB5YYDiuj2rjS7hz1i9Pl9LbSmcjNAKB5Xq+t3i6vfKZcWn7mRz1/VeQzWDNoyY8n9bncihIbRKURJftcSYskDg5yJfjuAr2QBsWVcOuzyEGTkStYQnuYgNxaC0bZxvne2tXGCMR2DqDImSJ+Xd9glifQJlpVu0CQalKD2A8VQ49HOE0ZgIAPorpQSjNpIUAsJRh4GnGw+fwcxHep6lGaEMpiIuQbFrwL5epgYI75gERzKnhRIE58cuN0Xv61Mc04kOcJ2fvC9SiucW3j+KgXGW5vvo9fXG4gcL8fvPG44bNEGt3nhC4s57nAfKl8lVQLrlNEUanJP0u3p6R2qtdNc5Bk3S2+5mo7HAzcExvOqC6FZ/by4D9bMnJOFfHArSDW2GfwbdSnSn7wRARi3/JzqDTUPCN0kXi/URgEPbqCzTgvvhU9osKXBSGZj1eD/DQWGcOwkUz6iOO0H+bxGk2k8D2+8EMaX7 mF1i7WyZ Q8uGsTR4kHUcP0hpkq9hLYrxpBle1MZvLmpV8rWllZISBLx+uyCAKFabXswA6xaymO8zpLXmGQJb0VntsesC7bYL6Bawo7qlBRICs3bB9xRL9jlLcmPw+d6eXqGjIbfQ1qpW5cuYarjcDx0rnaj6lUTLqxjBnVLpRyye1oJnIEd96FbMAVYVXpgqwUe9RO9C+NHsFN6Gb6JugVR2vES2Lh9JLCbVzCJJXnmOZcsRiDiTdJpj4gay48BNoXfdbzfkCzL8a6i+0PrMt5QmgEe1xSraQhU6auy6nakKFJCaLw6xlZ3GN8I7ShMEljPESWarGBXhw9xfgWGXtXRrTcLFXKjzOK4NG1Nz8nVfpkAylNUw4FlAlZI/wrS3M5sMk+PpbzfaZxq9bZ2/mP3XWaX1tgYINmLDMpGHrzfKzUHWMCzB2u7I21Bi1azdUJquob9K0dFWuWz4OyG/RAIDz5pR4fhyp/Kc4N4cF8Uyy8DN+Z2K8Wx4i1EvGvzMOthO8CFkiZmEQncY/lZ7a5AC1c9YtCKd39KdNsfDz1Mf+VQNeDU/ZPr8ymkSBZXW1ntj0Tql7grdvPhHKhQLqXNnXLUOkXyOfseIwn5AHRyjohYJLVTLeIQqna1kp640bR4ZrF8lcjh/S09uyA0EqSQ/v1+fk/jLB2nmgcigIi3mCUzf0/Xrib3s9QuMG0kkpCw== X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: While doing this, I detected some anomalies in the existing code: mm/kfence/kfence_test.c: - The last call to scnprintf() did increment 'cur', but it's unused after that, so it was dead code. I've removed the dead code in this patch. - 'end' is calculated as end = &expect[0][sizeof(expect[0] - 1)]; However, the '-1' doesn't seem to be necessary. When passing $2 to scnprintf(), the size was specified as 'end - cur'. And scnprintf() --just like snprintf(3)--, won't write more than $2 bytes (including the null byte). That means that scnprintf() wouldn't write more than &expect[0][sizeof(expect[0]) - 1] - expect[0] which simplifies to sizeof(expect[0]) - 1 bytes. But we have sizeof(expect[0]) bytes available, so we're wasting one byte entirely. This is a benign off-by-one bug. The two occurrences of this bug will be fixed in a following patch in this series. mm/kmsan/kmsan_test.c: The same benign off-by-one bug calculating the remaining size. mm/mempolicy.c: This file uses the 'p += snprintf()' anti-pattern. That will overflow the pointer on truncation, which has undefined behavior. Using sprintf_end(), this bug is fixed. As in the previous file, here there was also dead code in the last scnprintf() call, by incrementing a pointer that is not used after the call. I've removed the dead code. mm/page_owner.c: Within print_page_owner(), there are some calls to scnprintf(), which do report truncation. And then there are other calls to snprintf(), where we handle errors (there are two 'goto err'). I've kept the existing error handling, as I trust it's there for a good reason (i.e., we may want to avoid calling print_page_owner_memcg() if we truncated before). Please review if this amount of error handling is the right one, or if we want to add or remove some. For sprintf_end(), a single test for null after the last call is enough to detect truncation. mm/slub.c: Again, the 'p += snprintf()' anti-pattern. This is UB, and by using sprintf_end() we've fixed the bug. Cc: Kees Cook Cc: Christopher Bazley Cc: Sven Schnelle Cc: Marco Elver Cc: Heiko Carstens Cc: Tvrtko Ursulin Cc: Andrew Morton Cc: Linus Torvalds Cc: David Rientjes Cc: Christophe JAILLET Cc: Hyeonggon Yoo <42.hyeyoo@gmail.com> Cc: Chao Yu Cc: Vlastimil Babka Cc: Rasmus Villemoes Cc: Michal Hocko Cc: Al Viro Signed-off-by: Alejandro Colomar --- mm/kfence/kfence_test.c | 24 ++++++++++++------------ mm/kmsan/kmsan_test.c | 4 ++-- mm/mempolicy.c | 18 +++++++++--------- mm/page_owner.c | 32 +++++++++++++++++--------------- mm/slub.c | 5 +++-- 5 files changed, 43 insertions(+), 40 deletions(-) diff --git a/mm/kfence/kfence_test.c b/mm/kfence/kfence_test.c index 00034e37bc9f..bae382eca4ab 100644 --- a/mm/kfence/kfence_test.c +++ b/mm/kfence/kfence_test.c @@ -113,26 +113,26 @@ static bool report_matches(const struct expect_report *r) end = &expect[0][sizeof(expect[0]) - 1]; switch (r->type) { case KFENCE_ERROR_OOB: - cur += scnprintf(cur, end - cur, "BUG: KFENCE: out-of-bounds %s", + cur = sprintf_end(cur, end, "BUG: KFENCE: out-of-bounds %s", get_access_type(r)); break; case KFENCE_ERROR_UAF: - cur += scnprintf(cur, end - cur, "BUG: KFENCE: use-after-free %s", + cur = sprintf_end(cur, end, "BUG: KFENCE: use-after-free %s", get_access_type(r)); break; case KFENCE_ERROR_CORRUPTION: - cur += scnprintf(cur, end - cur, "BUG: KFENCE: memory corruption"); + cur = sprintf_end(cur, end, "BUG: KFENCE: memory corruption"); break; case KFENCE_ERROR_INVALID: - cur += scnprintf(cur, end - cur, "BUG: KFENCE: invalid %s", + cur = sprintf_end(cur, end, "BUG: KFENCE: invalid %s", get_access_type(r)); break; case KFENCE_ERROR_INVALID_FREE: - cur += scnprintf(cur, end - cur, "BUG: KFENCE: invalid free"); + cur = sprintf_end(cur, end, "BUG: KFENCE: invalid free"); break; } - scnprintf(cur, end - cur, " in %pS", r->fn); + sprintf_end(cur, end, " in %pS", r->fn); /* The exact offset won't match, remove it; also strip module name. */ cur = strchr(expect[0], '+'); if (cur) @@ -144,26 +144,26 @@ static bool report_matches(const struct expect_report *r) switch (r->type) { case KFENCE_ERROR_OOB: - cur += scnprintf(cur, end - cur, "Out-of-bounds %s at", get_access_type(r)); + cur = sprintf_end(cur, end, "Out-of-bounds %s at", get_access_type(r)); addr = arch_kfence_test_address(addr); break; case KFENCE_ERROR_UAF: - cur += scnprintf(cur, end - cur, "Use-after-free %s at", get_access_type(r)); + cur = sprintf_end(cur, end, "Use-after-free %s at", get_access_type(r)); addr = arch_kfence_test_address(addr); break; case KFENCE_ERROR_CORRUPTION: - cur += scnprintf(cur, end - cur, "Corrupted memory at"); + cur = sprintf_end(cur, end, "Corrupted memory at"); break; case KFENCE_ERROR_INVALID: - cur += scnprintf(cur, end - cur, "Invalid %s at", get_access_type(r)); + cur = sprintf_end(cur, end, "Invalid %s at", get_access_type(r)); addr = arch_kfence_test_address(addr); break; case KFENCE_ERROR_INVALID_FREE: - cur += scnprintf(cur, end - cur, "Invalid free of"); + cur = sprintf_end(cur, end, "Invalid free of"); break; } - cur += scnprintf(cur, end - cur, " 0x%p", (void *)addr); + sprintf_end(cur, end, " 0x%p", (void *)addr); spin_lock_irqsave(&observed.lock, flags); if (!report_available()) diff --git a/mm/kmsan/kmsan_test.c b/mm/kmsan/kmsan_test.c index 9733a22c46c1..e48ca1972ff3 100644 --- a/mm/kmsan/kmsan_test.c +++ b/mm/kmsan/kmsan_test.c @@ -107,9 +107,9 @@ static bool report_matches(const struct expect_report *r) cur = expected_header; end = &expected_header[sizeof(expected_header) - 1]; - cur += scnprintf(cur, end - cur, "BUG: KMSAN: %s", r->error_type); + cur = sprintf_end(cur, end, "BUG: KMSAN: %s", r->error_type); - scnprintf(cur, end - cur, " in %s", r->symbol); + sprintf_end(cur, end, " in %s", r->symbol); /* The exact offset won't match, remove it; also strip module name. */ cur = strchr(expected_header, '+'); if (cur) diff --git a/mm/mempolicy.c b/mm/mempolicy.c index b28a1e6ae096..6beb2710f97c 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -3359,6 +3359,7 @@ int mpol_parse_str(char *str, struct mempolicy **mpol) void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol) { char *p = buffer; + char *e = buffer + maxlen; nodemask_t nodes = NODE_MASK_NONE; unsigned short mode = MPOL_DEFAULT; unsigned short flags = 0; @@ -3384,33 +3385,32 @@ void mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol) break; default: WARN_ON_ONCE(1); - snprintf(p, maxlen, "unknown"); + sprintf_end(p, e, "unknown"); return; } - p += snprintf(p, maxlen, "%s", policy_modes[mode]); + p = sprintf_end(p, e, "%s", policy_modes[mode]); if (flags & MPOL_MODE_FLAGS) { - p += snprintf(p, buffer + maxlen - p, "="); + p = sprintf_end(p, e, "="); /* * Static and relative are mutually exclusive. */ if (flags & MPOL_F_STATIC_NODES) - p += snprintf(p, buffer + maxlen - p, "static"); + p = sprintf_end(p, e, "static"); else if (flags & MPOL_F_RELATIVE_NODES) - p += snprintf(p, buffer + maxlen - p, "relative"); + p = sprintf_end(p, e, "relative"); if (flags & MPOL_F_NUMA_BALANCING) { if (!is_power_of_2(flags & MPOL_MODE_FLAGS)) - p += snprintf(p, buffer + maxlen - p, "|"); - p += snprintf(p, buffer + maxlen - p, "balancing"); + p = sprintf_end(p, e, "|"); + p = sprintf_end(p, e, "balancing"); } } if (!nodes_empty(nodes)) - p += scnprintf(p, buffer + maxlen - p, ":%*pbl", - nodemask_pr_args(&nodes)); + sprintf_end(p, e, ":%*pbl", nodemask_pr_args(&nodes)); } #ifdef CONFIG_SYSFS diff --git a/mm/page_owner.c b/mm/page_owner.c index cc4a6916eec6..c00b3be01540 100644 --- a/mm/page_owner.c +++ b/mm/page_owner.c @@ -496,7 +496,7 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m, /* * Looking for memcg information and print it out */ -static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, +static inline char *print_page_owner_memcg(char *p, const char end[0], struct page *page) { #ifdef CONFIG_MEMCG @@ -511,8 +511,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, goto out_unlock; if (memcg_data & MEMCG_DATA_OBJEXTS) - ret += scnprintf(kbuf + ret, count - ret, - "Slab cache page\n"); + p = sprintf_end(p, end, "Slab cache page\n"); memcg = page_memcg_check(page); if (!memcg) @@ -520,7 +519,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, online = (memcg->css.flags & CSS_ONLINE); cgroup_name(memcg->css.cgroup, name, sizeof(name)); - ret += scnprintf(kbuf + ret, count - ret, + p = sprintf_end(p, end, "Charged %sto %smemcg %s\n", PageMemcgKmem(page) ? "(via objcg) " : "", online ? "" : "offline ", @@ -529,7 +528,7 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret, rcu_read_unlock(); #endif /* CONFIG_MEMCG */ - return ret; + return p; } static ssize_t @@ -538,14 +537,16 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn, depot_stack_handle_t handle) { int ret, pageblock_mt, page_mt; - char *kbuf; + char *kbuf, *p, *e; count = min_t(size_t, count, PAGE_SIZE); kbuf = kmalloc(count, GFP_KERNEL); if (!kbuf) return -ENOMEM; - ret = scnprintf(kbuf, count, + p = kbuf; + e = kbuf + count; + p = sprintf_end(p, e, "Page allocated via order %u, mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu ns\n", page_owner->order, page_owner->gfp_mask, &page_owner->gfp_mask, page_owner->pid, @@ -555,7 +556,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn, /* Print information relevant to grouping pages by mobility */ pageblock_mt = get_pageblock_migratetype(page); page_mt = gfp_migratetype(page_owner->gfp_mask); - ret += scnprintf(kbuf + ret, count - ret, + p = sprintf_end(p, e, "PFN 0x%lx type %s Block %lu type %s Flags %pGp\n", pfn, migratetype_names[page_mt], @@ -563,22 +564,23 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn, migratetype_names[pageblock_mt], &page->flags); - ret += stack_depot_snprint(handle, kbuf + ret, count - ret, 0); - if (ret >= count) - goto err; + p = stack_depot_sprint_end(handle, p, e, 0); + if (p == NULL) + goto err; // XXX: Should we remove this error handling? if (page_owner->last_migrate_reason != -1) { - ret += scnprintf(kbuf + ret, count - ret, + p = sprintf_end(p, e, "Page has been migrated, last migrate reason: %s\n", migrate_reason_names[page_owner->last_migrate_reason]); } - ret = print_page_owner_memcg(kbuf, count, ret, page); + p = print_page_owner_memcg(p, e, page); - ret += snprintf(kbuf + ret, count - ret, "\n"); - if (ret >= count) + p = sprintf_end(p, e, "\n"); + if (p == NULL) goto err; + ret = p - kbuf; if (copy_to_user(buf, kbuf, ret)) ret = -EFAULT; diff --git a/mm/slub.c b/mm/slub.c index be8b09e09d30..dcc857676857 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -7451,6 +7451,7 @@ static char *create_unique_id(struct kmem_cache *s) { char *name = kmalloc(ID_STR_LENGTH, GFP_KERNEL); char *p = name; + char *e = name + ID_STR_LENGTH; if (!name) return ERR_PTR(-ENOMEM); @@ -7475,9 +7476,9 @@ static char *create_unique_id(struct kmem_cache *s) *p++ = 'A'; if (p != name + 1) *p++ = '-'; - p += snprintf(p, ID_STR_LENGTH - (p - name), "%07u", s->size); + p = sprintf_end(p, e, "%07u", s->size); - if (WARN_ON(p > name + ID_STR_LENGTH - 1)) { + if (WARN_ON(p == NULL)) { kfree(name); return ERR_PTR(-EINVAL); } -- 2.50.0