lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 19 Feb 2016 20:20:58 -0500
From:	Jessica Yu <jeyu@...hat.com>
To:	Andrew Morton <akpm@...ux-foundation.org>
Cc:	Rasmus Villemoes <linux@...musvillemoes.dk>,
	Andy Shevchenko <andriy.shevchenko@...ux.intel.com>,
	Kees Cook <keescook@...omium.org>,
	linux-kernel@...r.kernel.org, Jessica Yu <jeyu@...hat.com>
Subject: [PATCH 0/1] Implement character sets for sscanf()

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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ