linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jessica Yu <jeyu@redhat.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk>,
	Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
	Kees Cook <keescook@chromium.org>,
	linux-kernel@vger.kernel.org, Jessica Yu <jeyu@redhat.com>
Subject: [PATCH 0/1] Implement character sets for sscanf()
Date: Fri, 19 Feb 2016 20:20:58 -0500	[thread overview]
Message-ID: <1455931259-27117-1-git-send-email-jeyu@redhat.com> (raw)

Hi,

This patch adds support for the '%[' conversion specifier for sscanf().
Since functions that calculate substring lengths based on accepted or
rejected characters already exist in the kernel (namely strspn() and
strcspn()), it's not much of a stretch to add some basic support for the
bracket '%[' conversion specifier for sscanf(). This is useful in cases
where we'd like to match substrings delimited by something other than
spaces. The original motivation for this patch actually came from livepatch
(https://lkml.org/lkml/2016/2/8/790), where we were trying to come up with
a clean way to parse symbol names with substrings delimited by periods and
commas.

Patch based on linux-next-20160219.

Here are some test cases:
---
sscanf_tests.c
---
#include <linux/module.h>
#include <linux/kernel.h>

#define FAIL -1
#define PASS 0

static int test1(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("this is a test\n", "%[^\n]", buf);
	pr_info("test1:\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 1)
		return FAIL;

	return PASS;
}

static int test2(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("this is a test\n", "%10[^\n]", buf);
	pr_info("test2:\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 1 && strlen(buf) != 10)
		return FAIL;

	return PASS;
}

static int test3(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("this is a test\n", "%[^ ]", buf);
	pr_info("test3:\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 1)
		return FAIL;

	return PASS;
}

static int test4(void)
{
	int ret;
	char buf[3][32] = { 0 };

	ret = sscanf("this is a test\n", "%[^ ] %[^ ] %[^ ]",
		     buf[0], buf[1], buf[2]);
	pr_info("test4:\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n\tbuf3: %s\n",
		 ret, buf[0], buf[1], buf[2]);
	if (ret != 3)
		return FAIL;

	return PASS;
}

/* semicolon delimiters */
static int test5(void)
{
	int ret;
	char buf[2][32] = { 0 };

	ret = sscanf("Hello;World;\n", "%[^;];%[^;]", buf[0], buf[1]);
	pr_info("test5:\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n",
		 ret, buf[0], buf[1]);
	if (ret != 2)
		return FAIL;

	return PASS;
}

/* colon delimiters */
static int test6(void)
{
	int ret;
	char buf[4][32] = { 0 };

	ret = sscanf("this:is:a:test", "%[^:]:%[^:]:%[^:]:%[^:]",
		     buf[0], buf[1], buf[2], buf[3]);
	pr_info("test6:\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n\tbuf3: %s\n\tbuf4: %s\n",
	       ret, buf[0], buf[1], buf[2], buf[3]);
	if (ret != 4)
		return FAIL;

	return PASS;

}

/* tab delimiters */
static int test7(void)
{
	int ret;
	char buf[4][32] = { 0 };

	ret = sscanf("this\tis\ta\ttest",
		     "%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]",
		     buf[0], buf[1], buf[2], buf[3]);
	pr_info("test7\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n\tbuf3: %s\n\tbuf4: %s\n",
		 ret, buf[0], buf[1], buf[2], buf[3]);
	if (ret != 4)
		return FAIL;

	return PASS;
}

/* error condition: empty char set */
static int test8(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("this is a test", "this%[] is a test", buf);
	pr_info("test8\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 0)
		return FAIL;

	return PASS;
}

static int test9(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("aaaccbbzabccbaa", "%6[abc]", buf);
	pr_info("test9\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 1 && strlen(buf) != 6)
		return FAIL;

	return PASS;
}

static int test10(void)
{
	int ret;
	char buf[3][32] = { 0 };

	ret = sscanf("aaaccbbxyzzcbabb",
		     "%[abc]%[^abc]%[abc]",
		     buf[0], buf[1], buf[2]);
	pr_info("test10\n\tret: %d\n\tbuf0: %s\n\tbuf1: %s\n\tbuf2: %s\n",
		ret, buf[0], buf[1], buf[2]);
	if (ret != 3)
		return FAIL;

	return PASS;
}

/* Intended usecase for livepatch symbol name parsing */
static int test11(void)
{
	int ret;
	char *sym = ".klp.sym.vmlinux.perf_ibs_event_update.isra.1,2";
	char buf[2][32] = { 0 };
	unsigned int pos;

	ret = sscanf(sym, ".klp.sym.%[^.].%[^,],%u",
		     buf[0], buf[1], &pos);
	pr_info("test11\n\tret: %d\n\tobjname: %s\n\tsymname: %s\n\tsympos: %u\n",
		ret, buf[0], buf[1], pos);

	if (ret != 3)
		return FAIL;

	return PASS;
}

/* error condition: empty set / cannot match ] bracket */
static int test12(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("aaaa]bbbb", "%[^]]", buf);
	pr_info("test12\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 0)
		return FAIL;

	return PASS;
}

/* no matching characters from the set */
static int test13(void)
{
	int ret;
	char buf[32] = { 0 };

	ret = sscanf("this is a test\n", "%[123]", buf);
	pr_info("test13\n\tret: %d\n\tbuf0: %s\n", ret, buf);
	if (ret != 0)
		return FAIL;

	return PASS;
}

static int test14(void)
{
	int ret;
	char buf[3][32] = { 0 };

	ret = sscanf("cbcd:091555:yzyz",
		     "%[abcde]:%[0123456789]:%[xyz]",
		     buf[0], buf[1], buf[2]);
	pr_info("test13\n\tret: %d\n\tbuf0: %s\n\tbuf1: %s\n\tbuf2: %s\n",
		ret, buf[0], buf[1], buf[2]);
	if (ret != 3)
		return FAIL;

	return PASS;
}

static int __init sscanf_tests_init(void)
{
	int ret;
	int fail = 0;

	ret = test1();
	if (ret) {
		pr_err("sscanf_tests: test1 FAILED");
		fail++;
	}

	ret = test2();
	if (ret) {
		pr_err("sscanf_tests: test2 FAILED");
		fail++;
	}

	ret = test3();
	if (ret) {
		pr_err("sscanf_tests: test3 FAILED");
		fail++;
	}

	ret = test4();
	if (ret) {
		pr_err("sscanf_tests: test4 FAILED");
		fail++;
	}

	ret = test5();
	if (ret) {
		pr_err("sscanf_tests: test5 FAILED");
		fail++;
	}

	ret = test6();
	if (ret) {
		pr_err("sscanf_tests: test6 FAILED");
		fail++;
	}

	ret = test7();
	if (ret) {
		pr_err("sscanf_tests: test7 FAILED");
		fail++;
	}

	ret = test8();
	if (ret) {
		pr_err("sscanf_tests: test8 FAILED");
		fail++;
	}

	ret = test9();
	if (ret) {
		pr_err("sscanf_tests: test9 FAILED");
		fail++;
	}

	ret = test10();
	if (ret) {
		pr_err("sscanf_tests: test10 FAILED");
		fail++;
	}

	ret = test11();
	if (ret) {
		pr_err("sscanf_tests: test11 FAILED");
		fail++;
	}

	ret = test12();
	if (ret) {
		pr_err("sscanf_tests: test12 FAILED");
		fail++;
	}

	ret = test13();
	if (ret) {
		pr_err("sscanf_tests: test13 FAILED");
		fail++;
	}

	ret = test14();
	if (ret) {
		pr_err("sscanf_tests: test14 FAILED");
		fail++;
	}

	if (!fail)
		pr_info("sscanf_tests: ALL TESTS PASSED\n");
	else
		pr_info("sscanf_tests: %d TESTS FAILED\n", fail);

	return 0;
}

static void __exit sscanf_tests_exit(void)
{
}

module_init(sscanf_tests_init);
module_exit(sscanf_tests_exit);
---

Thanks,
Jessica

Jessica Yu (1):
  sscanf: implement basic character sets

 lib/vsprintf.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

-- 
2.4.3

             reply	other threads:[~2016-02-20  1:21 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-02-20  1:20 Jessica Yu [this message]
2016-02-22  9:27 ` [PATCH 0/1] Implement character sets for sscanf() Andy Shevchenko
2016-02-22 19:39   ` Jessica Yu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1455931259-27117-1-git-send-email-jeyu@redhat.com \
    --to=jeyu@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=keescook@chromium.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@rasmusvillemoes.dk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).