From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-oa1-f46.google.com (mail-oa1-f46.google.com [209.85.160.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B587B37CD5D for ; Mon, 13 Apr 2026 05:03:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.46 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776056640; cv=none; b=Pk/6uvwCaQOo021ovxbFscCsBbZw6mGY5n1YKfYOKAdJvRrsIaQ/QC0PjNNeEEdRSZTKDj+dp7ra3nXhLqpqm6i6wshr+hxidlF8N8tT7PSbKWZ9GZ4kSD9h6oj5QuO05imZK3H+QDFtzux9+ymgtb2Kocf0fAUsOzlmW39xxvY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776056640; c=relaxed/simple; bh=OQ6hdsMJ6OrPRbLxy6ljDUYOIgQRfQDJJtue2M+K47s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Xxl7qDFpE2Uu6+YueCDndvcT1PUg/FJKXUynA6Gx5YNZcRJlPtvG8t+t41eyoAv93uuKxWtS3Ln8YP1/TZ8HOz+6GRMFWN8chgzteyvQjHIc/hKEMU7aE8A84gFLbEHyU1bfrIHCP5xn6Ik3zN3RoznFlJ7Lbp5TYdyhJnDoxd8= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=neuling.org; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GTZfkQA1; arc=none smtp.client-ip=209.85.160.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=neuling.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GTZfkQA1" Received: by mail-oa1-f46.google.com with SMTP id 586e51a60fabf-40ede943bf0so2461423fac.2 for ; Sun, 12 Apr 2026 22:03:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1776056631; x=1776661431; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=iJtvTDoUk7JRiWCtxkRey09tcg0WizGiEgNC74/hO5k=; b=GTZfkQA1dEuU4x4EsOPdRm5a3rnLnIrWxC8Jjgj1wRaeR7YF9r1EB9aZCyEG+hl+bP AMCY0pQp7D2d5fH2NxGGA8JAn0OhhqLbTXB6JrJ2zf1Z9pdM9DEcy3zW9Q5OhWwBJWuA FNJfyt59J2V9WbjOFNLBhFlOqim4X1hL/rtVey3xeJTOKkXa8C4nwzRAHA2+dVOcw3Qj PuFDSUvss1PaMgpSe8qtLwBpdZ8dBgIMo2rD/ZA/L19pmAe68e13IEowQSps9dAjTTHq cxN0PAwaSMzFTHO7ssoQQgCNRDKP0JA82hZQIwdc97ENV+NMMzLtX8blgE/j5iJGDTG4 45QA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776056631; x=1776661431; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=iJtvTDoUk7JRiWCtxkRey09tcg0WizGiEgNC74/hO5k=; b=O2LxkfdYqQ4LDi/TnkvyRlItRAgUDBxTnEhyFeFqG1QBsBG0eaCq66kf0RVu+9gncc kQ/jET3Xqvk3vCgVZcLr9hfv8AWSECciJlIVJtjeqQL74H8Rh+rbnxwzOwxClYFu8mGt Mn23lbjy5fny05ixdsWYDgGF5/4RMr+L4hdYTGZ8HC8j41vzx1voQTPr50HvolFCsWLt VB/V5WEwn/UOBypoTQe8vZ2263+ckRtJkUO/JOsDPMyW4PspKpHsF7iVEWFF4RCRDARa P3djvEyktDZfksIIurxXeJNfiItSC8WrooQCsvCgniXz7z5KRzBwWsRRFvUKUZTDJX6b VCWg== X-Forwarded-Encrypted: i=1; AJvYcCUToF6GKnp5RFc99U8IY7zDCaH4YYQyXlJxGQJ+FRNrddeIPE6uX514J/Du3CXshvllwdJzVZ46C46/bMk=@vger.kernel.org X-Gm-Message-State: AOJu0Yx5hMU89Vq/yUhyll9w62mXMCtXTYcKb+AcLlGgPAzUdxTAvrlm 8NLtREvoya4BujC5JWXSqqUQyr7lsxy41afAlI5zcOBjLrrkaYOoTeCdLt812EKiG3w= X-Gm-Gg: AeBDiesz0sUXmEvK15PB+E87vojQOcXPpTt2rEEXIQ7OAI4gXi6g4VUln8KwCGDxhq1 ClGON6xlenCfUMzzTtA75nAdWr7TYCjPHVPKLITdmYxT6tnnwx9s4PFQta0ctfnpdcYPUwp9PQs Cyp+fRXhgswOP84cXL2VcjA4GsWVZ6hTC08Vi+2SmEZsUyivvIy7rcsInT1lbrwFKUVKiIPu0rI mvytgsyKxdYOe8stCakDTvtcVYHOnmGO9cvWQuxBLEaX681WHiJ6mXJtF9u+FbC9dIww3nJiZpv 312Ip8xFuoQa4sUVIsPYXl/oLdEEX+zA87mlBfu21svlNquMCkivBkwcPCB/q4/9xQ2hhPdElu5 hLHJtB2XWhS/kGbTPmvmnqRY2NK1ftEO6t7zbFC5xSJ1TOkuuc6M0O72u/CJhpX2vBLjULEq5Si 0o59LqK9Y4BTDIJFyhq6Jll2BXzuDZR/P01w== X-Received: by 2002:a05:6871:8211:b0:417:304f:5011 with SMTP id 586e51a60fabf-423e0ec53ebmr6544681fac.17.1776056630936; Sun, 12 Apr 2026 22:03:50 -0700 (PDT) Received: from ird-aus2.tenstorrent.com ([38.104.49.66]) by smtp.gmail.com with ESMTPSA id 586e51a60fabf-423ddb9c461sm8363376fac.16.2026.04.12.22.03.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 12 Apr 2026 22:03:50 -0700 (PDT) Sender: Michael Neuling From: Michael Neuling To: jiangfeng@kylinos.cn Cc: alex@ghiti.fr, aou@eecs.berkeley.edu, linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, mikey@neuling.org, palmer@dabbelt.com, pjw@kernel.org Subject: Re: [PATCH] riscv: lib: Fix ZBB strnlen reading past count boundary Date: Mon, 13 Apr 2026 05:02:55 +0000 Message-ID: <20260413050257.1708848-1-mikey@neuling.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <5cc377eb-3e6b-44c7-a935-359c5b9d64a2@kylinos.cn> References: <5cc377eb-3e6b-44c7-a935-359c5b9d64a2@kylinos.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > Thanks for catching and fixing this! Your analysis is spot on—that > "load-before-check" logic was indeed an oversight on my part, especially > regarding the page boundary edge case. No worries. > The test case you provided is extremely helpful. Since you've already > built this reproducer, would you be interested in helping to improve > the KUnit test string_test_strnlen() in lib/tests/string_kunit.c as well? > Currently, it mainly tests strings with NUL terminators and lacks coverage > for these kinds of non-terminated boundary scenarios. The below is from Claude. I gave it a test under qemu riscv with and without the patch and it seems to catch the failure. Feel free to use it as you see fit. [ 19.042129] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 [ 19.043133] Oops [#1] [ 66.197273] ok 5 string_test_strnlen [ 66.197855] Unable to handle kernel paging request at virtual address ff20000000096000 [ 66.198980] Oops [#2] [ 66.199867] ra : string_test_strnlen_page_boundary+0xba/0x244 [ 66.204025] # string_test_strnlen_page_boundary: try faulted: last line seen lib/tests/string_kunit.c:195 [ 66.204391] # string_test_strnlen_page_boundary: internal error occurred preventing test case from running: -4 [ 66.205133] not ok 6 string_test_strnlen_page_boundary [ 66.227546] # string: pass:24 fail:1 skip:4 total:29 [ 66.227879] not ok 74 string Mikey >From b4933270b53e3acccea707b6dced352ee525828f Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 13 Apr 2026 04:17:56 +0000 Subject: [PATCH] lib/string_kunit: add strnlen page boundary test Add a kunit test that exercises strnlen with count reaching exactly to a page boundary and no NUL terminator in the buffer. This catches implementations that speculatively read past the count boundary (e.g. a word-at-a-time loop that loads before checking the limit). The test uses vmap of a single page so the next page is an unmapped guard page. A buggy strnlen that reads past count will fault. Three cases are tested: - No NUL in buffer, count 1-128 reaching page end (the primary trigger) - NUL present near the page boundary (correctness check) - count=0 with pointer at the page boundary (should not read at all) Signed-off-by: Michael Neuling Signed-off-by: Claude Opus 4.6 (1M context) --- lib/tests/string_kunit.c | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/lib/tests/string_kunit.c b/lib/tests/string_kunit.c index 0819ace5b0..917ff8edef 100644 --- a/lib/tests/string_kunit.c +++ b/lib/tests/string_kunit.c @@ -176,6 +176,52 @@ static void string_test_strnlen(struct kunit *test) vfree(buf); } +/* + * Test strnlen with count reaching a page boundary and no NUL terminator + * in the buffer. A buggy implementation that reads past the count boundary + * (e.g. a word-at-a-time loop that loads before checking) will fault on + * the unmapped guard page that vmap places after the mapping. + */ +static void string_test_strnlen_page_boundary(struct kunit *test) +{ + struct page *page; + char *buf; + size_t count; + + page = alloc_page(GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, page); + + buf = vmap(&page, 1, VM_MAP, PAGE_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, buf); + + memset(buf, 'A', PAGE_SIZE); + + /* Count reaches exactly to the page boundary, no NUL in buffer. */ + for (count = 1; count <= 128; count++) { + char *s = buf + PAGE_SIZE - count; + + KUNIT_EXPECT_EQ_MSG(test, strnlen(s, count), count, + "count:%zu offset_from_end:%zu", count, count); + } + + /* Also test with NUL present within the buffer near the boundary. */ + for (count = 2; count <= 128; count++) { + char *s = buf + PAGE_SIZE - count; + size_t nul_pos = count / 2; + + s[nul_pos] = '\0'; + KUNIT_EXPECT_EQ_MSG(test, strnlen(s, count), nul_pos, + "count:%zu nul_pos:%zu", count, nul_pos); + s[nul_pos] = 'A'; + } + + /* count = 0 should not read at all, even at the page boundary. */ + KUNIT_EXPECT_EQ(test, strnlen(buf + PAGE_SIZE, 0), (size_t)0); + + vunmap(buf); + __free_page(page); +} + static void string_test_strchr(struct kunit *test) { const char *test_string = "abcdefghijkl"; @@ -887,6 +933,7 @@ static struct kunit_case string_test_cases[] = { KUNIT_CASE(string_test_memset64), KUNIT_CASE(string_test_strlen), KUNIT_CASE(string_test_strnlen), + KUNIT_CASE(string_test_strnlen_page_boundary), KUNIT_CASE(string_test_strchr), KUNIT_CASE(string_test_strnchr), KUNIT_CASE(string_test_strrchr), -- 2.43.0