[PATCH 3/3] Add btrfs ioctl support.

Dmitry V. Levin ldv at altlinux.org
Fri Apr 1 03:33:26 UTC 2016


On Thu, Mar 31, 2016 at 12:34:18AM -0400, Jeff Mahoney wrote:
> +	case BTRFS_IOC_DEV_REPLACE: { /* RW */
> +		struct btrfs_ioctl_dev_replace_args args;
> +
> +		if (entering(tcp))
> +			tprints(", ");
> +
> +		if (umove_or_printaddr(tcp, arg, &args))
> +			break;
> +
> +		if (entering(tcp)) {
> +			tprints("{cmd=");
> +			printxvals(args.cmd, "BTRFS_IOCTL_DEV_REPLACE_CMD_???",
> +				   btrfs_dev_replace_cmds, NULL);
> +			if (args.cmd == BTRFS_IOCTL_DEV_REPLACE_CMD_START) {
> +				tprintf(", start={srcdevid=%" PRI__u64 ", "
> +				   "cont_reading_from_srcdev_mode=%" PRI__u64
> +				   ", srcdev_name=\"%s\", tgtdev_name=\"%s\"}",

Please do not print user input this way, it can contain arbitrary data,
not necessarily NUL-terminated.  It's easy to reproduce on entering syscall.

There is a function called print_quoted_string() that does the right thing
in cases like this, it also does proper quoting, see e.g. block.c

There are more cases in btrfs.c where user input is printed
using %s format, please check and correct them.

> +				   args.start.srcdevid,
> +				   args.start.cont_reading_from_srcdev_mode,
> +				   args.start.srcdev_name,
> +				   args.start.tgtdev_name);
> +			}
> +			tprints("}");
> +			return 0;
> +		}
> +
> +		tprints(" => {result=");

In most of RW cases (e.g. BTRFS_IOC_DEV_INFO) you did it right,
but here you can get something like this:

	BTRFS_IOC_DEV_REPLACE, {cmd=...}0xdeadbeef

instead of expected

	BTRFS_IOC_DEV_REPLACE, {cmd=...} => 0xdeadbeef

> +		printxvals(args.result, "BTRFS_IOCTL_DEV_REPLACE_RESULT_???",
> +			   btrfs_dev_replace_results, NULL);
> +		if (args.cmd == BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS) {
> +			char buf[10];

sizeof("HH:MM:SS")?

> +			time_t time;
> +			tprints(", ");
> +			printxvals(args.status.replace_state,
> +				   "BTRFS_IOCTL_DEV_REPLACE_STATE_???",
> +				   btrfs_dev_replace_state, NULL);
> +			snprintf(buf, sizeof(buf), ", %" PRI__u64

What this comma is doing here?

> +				 ".%" PRI__u64 "%%",
> +				 args.status.progress_1000 / 10,
> +				 args.status.progress_1000 % 10);
> +			tprintf(", progress_1000=[%" PRI__u64 "(%s)], ",
> +				args.status.progress_1000, buf);

Let's print percentage inline, without temporary buffer.

> +	case BTRFS_IOC_INO_PATHS: { /* RW */
> +		struct btrfs_ioctl_ino_path_args args;
> +		struct btrfs_data_container fspath;
> +		uint64_t i;
> +
> +		if (entering(tcp))
> +			tprints(", ");
> +		else if (syserror(tcp))
> +			break;
> +		else
> +			tprints(" => ");
> +
> +		if (umove_or_printaddr(tcp, arg, &args))
> +			break;
> +
> +		tprints("{");
> +
> +		if (entering(tcp)) {
> +			tprintf("inum=%" PRI__u64 ", size=%" PRI__u64 "}",
> +				args.inum, args.size);
> +			return 0;
> +		}
> +
> +		if (umoven_or_printaddr(tcp, args.fspath, args.size, &fspath))
> +			break;

This might lead to "=> {0xdeadbeef".

> +	case BTRFS_IOC_LOGICAL_INO: { /* RW */
> +		struct btrfs_ioctl_logical_ino_args args;
> +		struct btrfs_data_container *inodes;
> +		uint64_t i;
> +
> +		if (entering(tcp))
> +			tprints(", ");
> +		else if (syserror(tcp))
> +			break;
> +		else
> +			tprints(" => ");
> +
> +		if (umove_or_printaddr(tcp, arg, &args))
> +			break;
> +
> +		tprints("{");
> +
> +		if (entering(tcp)) {
> +			tprintf("logical=%" PRI__u64 ", size=%" PRI__u64 "}",
> +				args.logical, args.size);
> +			return 0;
> +		}
> +
> +		tprints("inodes=");
> +		inodes = malloc(args.size);

I fell somewhat uneasy with malloc of arbitrary 64-bit size even
if it has been returned by the kernel.

> +		if (!inodes) {
> +			tprintf("%#lx", (unsigned long)args.inodes);
> +			break;
> +		}
> +
> +		if (umoven_or_printaddr(tcp, args.inodes, args.size, inodes)) {
> +			free(inodes);
> +			break;
> +		}
> +
> +		tprintf("{bytes_left=%u, bytes_missing=%u, "
> +			"elem_cnt=%u, elem_missed=%u, val=[",
> +			inodes->bytes_left, inodes->bytes_missing,
> +			inodes->elem_cnt, inodes->elem_missed);
> +		for (i = 0; i < inodes->elem_cnt; i += 3) {

elem_cnt has type __u32; do you really want to print the whole array?  It
might be a good idea to clamp printed length to something like max_strlen
at least in abbrev mode.

> +		if (args.progress == -1ULL && abbrev(tcp))
> +			tprints("-1");
> +		else
> +			btrfs_print_objectid(args.progress);

Not sure why here and in other places you prefer printing -1ULL
not as "-1" in non-abbrev mode.

> +		size = sizeof(*info) * args.dest_count;
> +		info = malloc(size);
> +		if (!info) {
> +			tprintf("%#lx}", arg + sizeof(args));
> +			break;
> +		}
> +
> +		if (umoven_or_printaddr(tcp, arg + sizeof(args), size, info)) {
> +			tprints("}");
> +			free(info);
> +			break;
> +		}
> +
> +		if (entering(tcp))  {
> +			tprints("[");
> +			for (i = 0; i < args.dest_count; i++) {
> +				if (i)
> +					tprints(", ");
> +				tprintf("{fd=%" PRI__s64 ", "
> +					"logical_offset=%" PRI__u64 "}",
> +					info[i].fd, info[i].logical_offset);
> +			}
> +			tprints("]");
> +			return 0;

free(info)?

> +	case BTRFS_IOC_SEND: { /* W */
> +		struct btrfs_ioctl_send_args args;
> +		__u64 *sources;
> +		size_t size;
> +		uint64_t i;

That's a strange mix of __u64 and uint64_t.

> +		tprints(", ");
> +		if (umove_or_printaddr(tcp, arg, &args))
> +			break;
> +
> +		tprintf("{send_fd=%" PRI__s64 ", clone_sources_count=%" PRI__u64
> +			", clone_sources=", args.send_fd,
> +			args.clone_sources_count);
> +
> +		size = args.clone_sources_count * sizeof(*args.clone_sources);

As clone_sources_count has type __u64, this is a potential integer
overflow, easily reproducible on entering syscall.

> +		sources = malloc(size);

malloc of arbitrary 64-bit value specified by used on entering syscall?
Please impose some sane limit on this.

> +		if (!sources) {
> +			tprintf("%#lx}", (unsigned long)args.clone_sources);
> +			break;
> +		}
> +
> +		if (umoven_or_printaddr(tcp, (unsigned long)args.clone_sources,
> +					size, sources)) {
> +			tprintf("}");
> +			free(sources);
> +			break;
> +		}
> +
> +		tprints("[");
> +		for (i = 0; i < args.clone_sources_count; i++) {

clone_sources_count has type __u64; do you really want to print the whole
array?  It might be a good idea to clamp printed length to something like
max_strlen.


-- 
ldv
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.strace.io/pipermail/strace-devel/attachments/20160401/82037d7f/attachment.bin>


More information about the Strace-devel mailing list