Skip to contentSkip to conversation

Btrfs-progs use safe string manipulation functions

PATCH In review Eduardo Silva 6 participants 19 messages Feb 7, 2011 – Feb 11, 2011 1 message not in the archive Btrfs file system

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Btrfs-progs use safe string manipulation functions

Please find the attached patch which replace unsafe strcpy(3) by strncpy(3) functions.

regards,

Eduardo Silva

Quoted 1 line

From 5fc888c71981e4b74ba29dd53bf0d5a65b3ee504 Mon Sep 17 00:00:00 2001

From: Eduardo Silva <[email protected]>
Date: Mon, 7 Feb 2011 08:55:04 -0300
Subject: [PATCH] [PATCH] Btrfs-progs use safe string manipulation functions

---
 btrfs_cmds.c |   14 +++++++-------
 btrfsctl.c   |    2 +-
 convert.c    |    2 +-
 utils.c      |    9 +++++----
 4 files changed, 14 insertions(+), 13 deletions(-)
btrfs_cmds.c +7 -7
index 8031c58..fffb423 100644
@@ -375,7 +375,7 @@ int do_clone(int argc, char **argv)
 	printf("Create a snapshot of '%s' in '%s/%s'\n",
 	       subvol, dstdir, newname);
 	args.fd = fd;
-	strcpy(args.name, newname);
+	strncpy(args.name, newname, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args);
 
 	close(fd);
@@ -436,7 +436,7 @@ int do_delete_subvolume(int argc, char **argv)
 	}
 
 	printf("Delete subvolume '%s/%s'\n", dname, vname);
-	strcpy(args.name, vname);
+	strncpy(args.name, vname, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
 
 	close(fd);
@@ -490,7 +490,7 @@ int do_create_subvol(int argc, char **argv)
 	}
 
 	printf("Create subvolume '%s/%s'\n", dstdir, newname);
-	strcpy(args.name, newname);
+	strncpy(args.name, newname, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
 
 	close(fddst);
@@ -553,7 +553,7 @@ int do_scan(int argc, char **argv)
 
 		printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]);
 
-		strcpy(args.name, argv[i]);
+		strncpy(args.name, argv[i], BTRFS_PATH_NAME_MAX);
 		/*
 		 * FIXME: which are the error code returned by this ioctl ?
 		 * it seems that is impossible to understand if there no is
@@ -593,7 +593,7 @@ int do_resize(int argc, char **argv)
 	}
 
 	printf("Resize '%s' of '%s'\n", path, amount);
-	strcpy(args.name, amount);
+	strncpy(args.name, amount, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
 	close(fd);
 	if( res < 0 ){
@@ -736,7 +736,7 @@ int do_add_volume(int nargs, char **args)
 		}
 		close(devfd);
 
-		strcpy(ioctl_args.name, args[i]);
+		strncpy(ioctl_args.name, args[i], BTRFS_PATH_NAME_MAX);
 		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
 		if(res<0){
 			fprintf(stderr, "ERROR: error adding the device '%s'\n", args[i]);
@@ -792,7 +792,7 @@ int do_remove_volume(int nargs, char **args)
 		struct	btrfs_ioctl_vol_args arg;
 		int	res;
 
-		strcpy(arg.name, args[i]);
+		strncpy(arg.name, args[i], BTRFS_PATH_NAME_MAX);
 		res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
 		if(res<0){
 			fprintf(stderr, "ERROR: error removing the device '%s'\n", args[i]);
btrfsctl.c +1 -1
index 92bdf39..adfa519 100644
@@ -237,7 +237,7 @@ int main(int ac, char **av)
 	 }
 
 	if (name)
-		strcpy(args.name, name);
+                strncpy(args.name, name, BTRFS_PATH_NAME_MAX + 1);
 	else
 		args.name[0] = '\0';
 
convert.c +1 -1
index d037c98..fbcf4a3 100644
@@ -857,7 +857,7 @@ static int copy_single_xattr(struct btrfs_trans_handle *trans,
 		data = databuf;
 		datalen = bufsize;
 	}
-	strcpy(namebuf, xattr_prefix_table[name_index]);
+	strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
 	strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
 	if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
 	    sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
utils.c +5 -4
index fd894f3..96ef94d 100644
@@ -108,7 +108,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
 	btrfs_set_super_chunk_root_generation(&super, 1);
 	if (label)
-		strcpy(super.label, label);
+		strncpy(super.label, label, BTRFS_LABEL_SIZE - 1);
 
 	buf = malloc(sizeof(*buf) + max(sectorsize, leafsize));
 
@@ -828,7 +828,7 @@ void btrfs_register_one_device(char *fname)
 			"skipping device registration\n");
 		return;
 	}
-	strcpy(args.name, fname);
+	strncpy(args.name, fname, BTRFS_PATH_NAME_MAX);
 	ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
 	close(fd);
 }
@@ -971,6 +971,7 @@ static char *size_strs[] = { "", "KB", "MB", "GB", "TB",
 char *pretty_sizes(u64 size)
 {
 	int num_divs = 0;
+        int pretty_len = 16;
 	u64 last_size = size;
 	u64 fract_size = size;
 	float fraction;
@@ -988,8 +989,8 @@ char *pretty_sizes(u64 size)
 		return NULL;
 
 	fraction = (float)fract_size / 1024;
-	pretty = malloc(16);
-	sprintf(pretty, "%.2f%s", fraction, size_strs[num_divs-1]);
+	pretty = malloc(pretty_len);
+	snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs-1]);
 	return pretty;
 }
 
Signature · 1 line
1.7.1
#1 lore raw
Quoted 6 lines · Eduardo Silva

Please find the attached patch which replace unsafe strcpy(3) by strncpy(3) functions.

regards,

Eduardo Silva

Hi Eduardo,

even though some "strncpy" are unneeded because a check is performed before, I fully agree that "strncpy" is better than a simple strcpy. However I have to highlight that truncating a path without noticing may be lead to a problem (think a delete operation applied to a shorter path...).
So can I ask you to add some checks to your patch, in order to warn the user that the path is too long ?

Regards
G.Baroncelli

#2 lore raw
In reply to Eduardo Silva
Quoted 2 lines · Eduardo Silva

Please find the attached patch which replace unsafe strcpy(3) by strncpy(3) functions.

strncpy() doesn't NUL-terminate the destination buffer if the maximum length is reached. And as far as I can see, there is no other initialization of those buffers to zeroes, except for super.label in make_btrfs() in utils.c.

So please change those strncpy() calls to something like:

    strncpy(args.name, source, BTRFS_PATH_NAME_MAX);
    args.name[BTRFS_PATH_NAME_MAX] = '\0';

(Note that the name member of struct btrfs_ioctl_vol_args is BTRFS_PATH_NAME_MAX + 1 long, so the above is correct for that field.)

And of course similarly in those cases where you copy to something other than a struct btrfs_ioctl_vol_args.

There were also a two places where you used spaces instead of tabs for indentation (in main() in btrfsctl.c, and the declaration of pretty_len in pretty_sizes() in utils.c).

/Bellman

#3 lore raw

On Thu, Feb 10, 2011 at 12:08 PM, Thomas Bellman <[email protected]> w= rote:

Quoted 2 lines

=C2=A0 =C2=A0strncpy(args.name, source, BTRFS_PATH_NAME_MAX); =C2=A0 =C2=A0args.name[BTRFS_PATH_NAME_MAX] =3D '\0';

That's silly. Isn't there a sane safe variant of strcpy?

Olaf
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" = in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#4 lore raw
Quoted 6 lines · Olaf van der Spek
Quoted 2 lines · Thomas Bellman

strncpy(args.name, source, BTRFS_PATH_NAME_MAX);
args.name[BTRFS_PATH_NAME_MAX] = '\0';

That's silly. Isn't there a sane safe variant of strcpy?

There's strlcpy, but it's not in glibc because of possible truncation errors!

http://en.wikipedia.org/wiki/Strlcpy

Of course C++ strings would be much better... :-)

Jeremy

#5 lore raw
Quoted 11 lines · Jeremy Sanders
Quoted 6 lines · Olaf van der Spek
Quoted 2 lines · Thomas Bellman

strncpy(args.name, source, BTRFS_PATH_NAME_MAX);
args.name[BTRFS_PATH_NAME_MAX] = '\0';

That's silly. Isn't there a sane safe variant of strcpy?

There's strlcpy, but it's not in glibc because of possible truncation errors!

Then use a private wrapper.

Signature · 1 line
Olaf
#6 lore raw
Quoted 9 lines · Olaf van der Spek
Quoted 7 lines · Jeremy Sanders
Quoted 5 lines · Olaf van der Spek
Quoted 2 lines · Thomas Bellman

strncpy(args.name, source, BTRFS_PATH_NAME_MAX);
args.name[BTRFS_PATH_NAME_MAX] = '\0';

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

That's silly. Isn't there a sane safe variant of strcpy?

There's strlcpy, but it's not in glibc because of possible truncation errors!

Then use a private wrapper.

Here's the new patch:

----
[PATCH] Add safe string manipulation functions

Deprecate direct use of strcpy(3)
The following string manipulation function has been added:

- string_copy() : wrapper of strcpy(3)
- string_ncopy(): wrapper of strncpy(3)

both function compose safe NULL terminated strings.
----

I check that the code most of the time raise an error if the path is too long, so the new wrappers should be ok...

best,

Eduardo Silva

Quoted 1 line

From 6e551f4d9482a438beb336c4ec3a54735a15b76c Mon Sep 17 00:00:00 2001

From: Eduardo Silva <[email protected]>
Date: Thu, 10 Feb 2011 10:25:15 -0300
Subject: [PATCH] Add safe string manipulation functions

Deprecate direct use of strcpy(3)
The following string manipulation function has been added:

- string_copy() : wrapper of strcpy(3)
- string_ncopy(): wrapper of strncpy(3)

both function compose safe NULL terminated strings.

---
 btrfs-list.c |    4 ++--
 btrfs-vol.c  |    2 +-
 btrfs.c      |    3 ++-
 btrfs_cmds.c |   14 +++++++-------
 btrfsctl.c   |    2 +-
 convert.c    |    4 ++--
 utils.c      |   38 ++++++++++++++++++++++++++++++++------
 utils.h      |    5 +++++
 8 files changed, 52 insertions(+), 20 deletions(-)
btrfs-list.c +2 -2
index 93766a8..ede51a4 100644
@@ -291,7 +291,7 @@ static int lookup_ino_path(int fd, struct root_info *ri)
 			perror("malloc failed");
 			exit(1);
 		}
-		strcpy(ri->path, args.name);
+		string_copy(ri->path, args.name);
 		strcat(ri->path, ri->name);
 	} else {
 		/* we're at the root of ref_tree */
@@ -448,7 +448,7 @@ char *build_name(char *dirid, char *name)
 	full = malloc(strlen(dirid) + strlen(name) + 1);
 	if (!full)
 		return NULL;
-	strcpy(full, dirid);
+	string_copy(full, dirid);
 	strcat(full, name);
 	return full;
 }
btrfs-vol.c +1 -1
index 4ed799d..60361f6 100644
@@ -151,7 +151,7 @@ int main(int ac, char **av)
 	}
 	fd = dirfd(dirstream);
 	if (device)
-		strcpy(args.name, device);
+		string_copy(args.name, device);
 	else
 		args.name[0] = '\0';
 
btrfs.c +2 -1
index 46314cf..ddf4960 100644
@@ -21,6 +21,7 @@
 
 #include "kerncompat.h"
 #include "btrfs_cmds.h"
+#include "utils.h"
 #include "version.h"
 
 typedef int (*CommandFunction)(int argc, char **argv);
@@ -241,7 +242,7 @@ static int prepare_args(int *ac, char ***av, char *prgname, struct Command *cmd
 	for(i=0; i < *ac ; i++ )
 		ret[i+1] = (*av)[i];
 
-	strcpy(newname, prgname);
+	string_copy(newname, prgname);
 	strcat(newname, " ");
 	strcat(newname, cmd->verb);
 
btrfs_cmds.c +7 -7
index 8031c58..f4be8c2 100644
@@ -375,7 +375,7 @@ int do_clone(int argc, char **argv)
 	printf("Create a snapshot of '%s' in '%s/%s'\n",
 	       subvol, dstdir, newname);
 	args.fd = fd;
-	strcpy(args.name, newname);
+	string_ncopy(args.name, newname, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE, &args);
 
 	close(fd);
@@ -436,7 +436,7 @@ int do_delete_subvolume(int argc, char **argv)
 	}
 
 	printf("Delete subvolume '%s/%s'\n", dname, vname);
-	strcpy(args.name, vname);
+	string_ncopy(args.name, vname, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
 
 	close(fd);
@@ -490,7 +490,7 @@ int do_create_subvol(int argc, char **argv)
 	}
 
 	printf("Create subvolume '%s/%s'\n", dstdir, newname);
-	strcpy(args.name, newname);
+	string_ncopy(args.name, newname, BTRFS_PATH_NAME_MAX);
 	res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
 
 	close(fddst);
@@ -553,7 +553,7 @@ int do_scan(int argc, char **argv)
 
 		printf("Scanning for Btrfs filesystems in '%s'\n", argv[i]);
 
-		strcpy(args.name, argv[i]);
+		string_ncopy(args.name, argv[i], BTRFS_PATH_NAME_MAX);
 		/*
 		 * FIXME: which are the error code returned by this ioctl ?
 		 * it seems that is impossible to understand if there no is
@@ -593,7 +593,7 @@ int do_resize(int argc, char **argv)
 	}
 
 	printf("Resize '%s' of '%s'\n", path, amount);
-	strcpy(args.name, amount);
+	string_ncopy(args.name, amount, BTRFS_VOL_NAME_MAX);
 	res = ioctl(fd, BTRFS_IOC_RESIZE, &args);
 	close(fd);
 	if( res < 0 ){
@@ -736,7 +736,7 @@ int do_add_volume(int nargs, char **args)
 		}
 		close(devfd);
 
-		strcpy(ioctl_args.name, args[i]);
+		string_ncopy(ioctl_args.name, args[i], BTRFS_PATH_NAME_MAX);
 		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
 		if(res<0){
 			fprintf(stderr, "ERROR: error adding the device '%s'\n", args[i]);
@@ -792,7 +792,7 @@ int do_remove_volume(int nargs, char **args)
 		struct	btrfs_ioctl_vol_args arg;
 		int	res;
 
-		strcpy(arg.name, args[i]);
+		string_ncopy(arg.name, args[i], BTRFS_PATH_NAME_MAX);
 		res = ioctl(fdmnt, BTRFS_IOC_RM_DEV, &arg);
 		if(res<0){
 			fprintf(stderr, "ERROR: error removing the device '%s'\n", args[i]);
btrfsctl.c +1 -1
index 92bdf39..adfa519 100644
@@ -237,7 +237,7 @@ int main(int ac, char **av)
 	 }
 
 	if (name)
-		strcpy(args.name, name);
+                strncpy(args.name, name, BTRFS_PATH_NAME_MAX + 1);
 	else
 		args.name[0] = '\0';
 
convert.c +2 -2
index d037c98..67706f3 100644
@@ -857,7 +857,7 @@ static int copy_single_xattr(struct btrfs_trans_handle *trans,
 		data = databuf;
 		datalen = bufsize;
 	}
-	strcpy(namebuf, xattr_prefix_table[name_index]);
+	strncpy(namebuf, xattr_prefix_table[name_index], XATTR_NAME_MAX);
 	strncat(namebuf, EXT2_EXT_ATTR_NAME(entry), entry->e_name_len);
 	if (name_len + datalen > BTRFS_LEAF_DATA_SIZE(root) -
 	    sizeof(struct btrfs_item) - sizeof(struct btrfs_dir_item)) {
@@ -1465,7 +1465,7 @@ struct btrfs_root *link_subvol(struct btrfs_root *root, const char *base,
 	key.offset = (u64)-1;
 	key.type = BTRFS_ROOT_ITEM_KEY;
 
-	strcpy(buf, base);
+	string_copy(buf, base);
 	for (i = 0; i < 1024; i++) {
 		ret = btrfs_insert_dir_item(trans, root, buf, strlen(buf),
 					    dirid, &key, BTRFS_FT_DIR, index);
utils.c +32 -6
index fd894f3..0c052c1 100644
@@ -108,7 +108,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 	btrfs_set_super_csum_type(&super, BTRFS_CSUM_TYPE_CRC32);
 	btrfs_set_super_chunk_root_generation(&super, 1);
 	if (label)
-		strcpy(super.label, label);
+		strncpy(super.label, label, BTRFS_LABEL_SIZE - 1);
 
 	buf = malloc(sizeof(*buf) + max(sectorsize, leafsize));
 
@@ -828,7 +828,7 @@ void btrfs_register_one_device(char *fname)
 			"skipping device registration\n");
 		return;
 	}
-	strcpy(args.name, fname);
+	strncpy(args.name, fname, BTRFS_PATH_NAME_MAX);
 	ret = ioctl(fd, BTRFS_IOC_SCAN_DEV, &args);
 	close(fd);
 }
@@ -853,7 +853,7 @@ int btrfs_scan_one_dir(char *dirname, int run_ioctl)
 	pending = malloc(sizeof(*pending));
 	if (!pending)
 		return -ENOMEM;
-	strcpy(pending->name, dirname);
+	string_copy(pending->name, dirname);
 
 again:
 	dirname_len = strlen(pending->name);
@@ -894,7 +894,7 @@ again:
 				ret = -ENOMEM;
 				goto fail;
 			}
-			strcpy(next->name, fullpath);
+			string_copy(next->name, fullpath);
 			list_add_tail(&next->list, &pending_list);
 		}
 		if (!S_ISBLK(st.st_mode)) {
@@ -971,6 +971,7 @@ static char *size_strs[] = { "", "KB", "MB", "GB", "TB",
 char *pretty_sizes(u64 size)
 {
 	int num_divs = 0;
+        int pretty_len = 16;
 	u64 last_size = size;
 	u64 fract_size = size;
 	float fraction;
@@ -988,8 +989,33 @@ char *pretty_sizes(u64 size)
 		return NULL;
 
 	fraction = (float)fract_size / 1024;
-	pretty = malloc(16);
-	sprintf(pretty, "%.2f%s", fraction, size_strs[num_divs-1]);
+	pretty = malloc(pretty_len);
+	snprintf(pretty, pretty_len, "%.2f%s", fraction, size_strs[num_divs-1]);
 	return pretty;
 }
 
+char *string_copy(char *dest, const char *src)
+{
+	if (!dest || !src) {
+		fprintf(stderr, "ERROR: invalid string_copy() parameters");
+		exit(EXIT_FAILURE);
+	}
+
+	memset(dest, '\0', sizeof(dest));
+	return strcpy(dest, src);
+}
+
+char *string_ncopy(char *dest, const char *src, size_t n)
+{
+	/* Just a basic test to avoid silly bugs */
+	if (!dest || !src || n <= 0) {
+		fprintf(stderr, "ERROR: invalid string_ncopy() parameters\n");
+		exit(EXIT_FAILURE);
+	}
+
+	strncpy(dest, src, n);
+	dest[n] = '\0';
+	
+	return dest;
+}
+
utils.h +5 -0
index 9dce5b0..39c8455 100644
@@ -19,6 +19,8 @@
 #ifndef __UTILS__
 #define __UTILS__
 
+#include "ctree.h"
+
 #define BTRFS_MKFS_SYSTEM_GROUP_SIZE (4 * 1024 * 1024)
 
 int make_btrfs(int fd, const char *device, const char *label,
@@ -40,4 +42,7 @@ int check_mounted(const char *devicename);
 int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
 				 int super_offset);
 char *pretty_sizes(u64 size);
+
+char *string_copy(char *dest, const char *src);
+char *string_ncopy(char *dest, const char *src, size_t n);
 #endif
Signature · 1 line
1.7.1
#7 lore raw
Quoted 1 line · Eduardo Silva
Quoted 1 line
Quoted 1 line

There's strlcpy, but it's not in glibc because of possible truncat=

ion

Quoted 13 lines
Quoted 4 lines
Quoted 1 line

errors!

Then use a private wrapper.

Here's the new patch:

----
[PATCH] Add safe string manipulation functions

Deprecate direct use of strcpy(3)
The following string manipulation function has been added:

Quoted 7 lines

=C2=A0 - string_copy() : wrapper of strcpy(3)
=C2=A0 - string_ncopy(): wrapper of strncpy(3)

both function compose safe NULL terminated strings.
----

I check that the code most of the time raise an error if the path is =

too

Quoted 1 line

long, so the new wrappers should be ok...

string_copy seems pointless, it's kinda equivalent to strcpy. if (!dest || !src) should include an assert so it's easier to break in the debugger.

--=20
Olaf
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" = in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#8 lore raw
Quoted 18 lines · Olaf van der Spek
Quoted 17 lines · Eduardo Silva
Quoted 5 lines
Quoted 2 lines

There's strlcpy, but it's not in glibc because of possible truncation errors!

Then use a private wrapper.

Here's the new patch:

----
[PATCH] Add safe string manipulation functions

Deprecate direct use of strcpy(3)
The following string manipulation function has been added:

- string_copy() : wrapper of strcpy(3)
- string_ncopy(): wrapper of strncpy(3)

Quoted 7 lines
Quoted 5 lines

both function compose safe NULL terminated strings.
----

I check that the code most of the time raise an error if the path is too long, so the new wrappers should be ok...

string_copy seems pointless, it's kinda equivalent to strcpy.

got your point, but If we are creating wrappers for string manipulation, let's do it for the most common functions used.

Quoted 3 lines

if (!dest || !src) should include an assert so it's easier to break in the debugger.

good one

regards,

Ed.-

A message that is not in the archive (sent off-list or not yet received): 1297345079.28159.14.camel@monotop

#10 lore raw
Quoted 4 lines · Eduardo Silva

string_copy seems pointless, it's kinda equivalent to strcpy.

Yeah, but if we are thinking into write some wrappers let's create a couple for the major string manipulation used...

A wrapper should have a benefit, your string_copy doesn't have any.

Signature · 1 line
Olaf
#11 lore raw
Quoted 7 lines · Olaf van der Spek
Quoted 4 lines · Eduardo Silva

string_copy seems pointless, it's kinda equivalent to strcpy.

Yeah, but if we are thinking into write some wrappers let's create a couple for the major string manipulation used...

A wrapper should have a benefit, your string_copy doesn't have any.

It have it, take this example:

	char *a;
        a = malloc(1024);
        strcpy(a, NULL);

at least with the wrapper you will get a notice about what's going on...

#12 lore raw
Quoted 2 lines · Eduardo Silva
Quoted 1 line · Olaf van der Spek

On Thu, Feb 10, 2011 at 2:37 PM, Eduardo Silva <eduardo.silva@oracle=

Quoted 3 lines · =2Ecom> wrote:
Quoted 3 lines
Quoted 3 lines

string_copy seems pointless, it's kinda equivalent to strcpy.

Yeah, but if we are thinking into write some wrappers let's create=

a couple

Quoted 4 lines
Quoted 3 lines
Quoted 1 line

for the major string manipulation used...

A wrapper should have a benefit, your string_copy doesn't have any.

Quoted 8 lines

It have it, take this example:

=C2=A0 =C2=A0 =C2=A0 =C2=A0char *a;
=C2=A0 =C2=A0 =C2=A0 =C2=A0a =3D malloc(1024);
=C2=A0 =C2=A0 =C2=A0 =C2=A0strcpy(a, NULL);

at least with the wrapper you will get a notice about what's going on...

The debugger shows you what's going on without wrapper.

--=20
Olaf
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" = in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#13 lore raw
In reply to Eduardo Silva
Quoted 32 lines · Eduardo Silva
Quoted 9 lines · Olaf van der Spek
Quoted 7 lines · Jeremy Sanders
Quoted 5 lines · Olaf van der Spek
Quoted 2 lines · Thomas Bellman

strncpy(args.name, source, BTRFS_PATH_NAME_MAX);
args.name[BTRFS_PATH_NAME_MAX] = '\0';

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

That's silly. Isn't there a sane safe variant of strcpy?

There's strlcpy, but it's not in glibc because of possible truncation errors!

Then use a private wrapper.

Here's the new patch:

----
[PATCH] Add safe string manipulation functions

Deprecate direct use of strcpy(3)
The following string manipulation function has been added:

- string_copy() : wrapper of strcpy(3)
- string_ncopy(): wrapper of strncpy(3)

both function compose safe NULL terminated strings.
----

Quoted 7 lines

I check that the code most of the time raise an error if the path is too long, so the new wrappers should be ok...

best,

Eduardo Silva

[...]
+char *string_copy(char *dest, const char *src)
+{
+ if (!dest || !src) {
+ fprintf(stderr, "ERROR: invalid string_copy() parameters");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(dest, '\0', sizeof(dest));

What is the purpose of the line above ? sizeof(dest) is a const value (typically 4 or 8) !

I agree with Olaf that string_copy() is usefulness.

I suggest you to improve the check of the string length before the copy (not in the copy function), and raising an error when the length of the string is too long.

Regards
G.Baroncelli

#14 lore raw
In reply to Eduardo Silva
Quoted 9 lines · Eduardo Silva

[PATCH] Add safe string manipulation functions

Deprecate direct use of strcpy(3)
The following string manipulation function has been added:

- string_copy() : wrapper of strcpy(3)
- string_ncopy(): wrapper of strncpy(3)

both function compose safe NULL terminated strings.

I'd like make some comments, which I hope will be acceptable.

Firstly, calling strcpy dangerous is, to me, rather overblown. It is easy to make mistakes, but it is not at all dangerous the way, for example, gets(3) is dangerous. strcpy can be used safely, gets cannot. Also, if you consider strcpy to be dangerous, then strcat should be dangerous too. However, given the risk of overwriting a buffer with strcpy, I agree that it's good to see if an alternative can be found.

Secondly, if you're going to make wrappers or helper functions for string handling in C, you need to decide several things right from the start:

* do you do static or dynamic allocation?
* how do you handle errors?
* do you want a minimal wrapper or replacement, or a whole new library?

I am not familiar enough with the btrfs-progs code base to give any strong recommendations, but off the top of my head I would suggest these, for this patch:

* make use of fairly minimal wrappers/replacements (at least for now)
* handle errors by calling abort or exit
* don't allocate data dynamically (or else it's not a minimal wrapper)

For error handling, there are two kinds of things that can happen:
normal run-time errors (malloc returns NULL, writing to a file fails, etc), and programming errors (wrong parameters to functions). If we're doing a minimal wrapper without dynamic memory allocation, the only thing string functions should need to worry about is programming errors. For those, abort(3) is the appropriate way to terminate the program, since it causes a core dump, which can be inspected with a debugger. Since btrfs-progs are non-interactive command line tools, this should be OK.

For checking function arguments, the assert macro is appropriate. It calls abort if the test fails. I am not sure I would check for parameters being non-NULL, though, since the kernel will trap such usage and cause a segfault, which, again, can be analyzed with a debugger.

For things like string copying, another problem to consider is what to do if the target array is not large enough? The two possibilities is to silently truncate the output string, return an error code of some sort, or to abort. The error code is a bit tedious, since it requires the caller to check for it, and do something sensible if it's not enough. For btrfs-progs, I would suggest aborting.

Taking all of these together, my suggestion for a "safer strcpy" would be along these lines (outline only, not tested code):

        void safer_strcpy(char *target, size_t tsize, const char
        *source)
        {
            size_t n;
        
            n = snprintf(target, tsize, "%s", source);
            assert(n < tsize);
        }
        
        void safer_strncpy(char *tgt, size_t tsize, const char *src, size_t n)
        {
            assert(n < tsize); /* There must be space for the '\0'. */
            memset(tgt, '\0', tsize);
            strncpy(tgt, src, n);
        }

Note that for any reasonable error checking to be possible the safety functions need to know the size of the target memory area. Otherwise no sensible checks can be done -- you have to rely on the caller to check that the target array is big enough, and then you're nowhere better than with plain strcpy.

(Also note that I did not call the function string_copy, since global names starting with str are reserved to the C implementation.)

Your function fills in the target array with zero bytes. Is that necessary? If it is, then the memset call needs to be added to safer_strcpy.

(I don't find it useful to return the target array as the return value of the function, so I didn't do that.)

Signature · 2 lines
Blog/wiki/website hosting with ikiwiki (free for free software):
http://www.branchable.com/
#15 lore raw
In reply to Jeremy Sanders
Quoted 2 lines · Jeremy Sanders

There's strlcpy, but it's not in glibc because of possible truncation errors!

snprintf is standard, and should be about as safe as it gets with the glibc functions.

#16 lore raw
Quoted 6 lines · Lars Wirzenius
Quoted 2 lines · Jeremy Sanders

There's strlcpy, but it's not in glibc because of possible truncation errors!

snprintf is standard, and should be about as safe as it gets with the glibc functions.

But snprintf is not like strlcpy.

Olaf

#17 lore raw
Quoted 6 lines · Olaf van der Spek
Quoted 2 lines · Lars Wirzenius

snprintf is standard, and should be about as safe as it gets with the glibc functions.

But snprintf is not like strlcpy.

It is indeed uglier to write 'snprintf(dst, size, "%s", src)' instead of 'strlcpy(dst, src, size)', but both the effect and the return value should be identical.

/Bellman

#19 lore raw
In reply to Thomas Bellman
Quoted 10 lines · Thomas Bellman
Quoted 2 lines · Eduardo Silva

Please find the attached patch which replace unsafe strcpy(3) by strncpy(3) functions.

strncpy() doesn't NUL-terminate the destination buffer if the maximum length is reached. And as far as I can see, there is no other initialization of those buffers to zeroes, except for super.label in make_btrfs() in utils.c.

Quoted 5 lines

So please change those strncpy() calls to something like:

    strncpy(args.name, source, BTRFS_PATH_NAME_MAX);
    args.name[BTRFS_PATH_NAME_MAX] = '\0';

Seems like a string manipulation function is the way to go, will send a new patch shortly,

best,

Eduardo