[PATCH 2/4] reiserfs: use snprintf for buffer formatting

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Jeff Mahoney <jeffm@xxxxxxxx>

We use a fixed-length buffer for formatting messages but don't do
any bounds checking on it.  This patch makes proper use of snprintf
and bounds tracking to ensure that we don't overflow.

Signed-off-by: Jeff Mahoney <jeffm@xxxxxxxx>
---
 fs/reiserfs/prints.c | 213 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 138 insertions(+), 75 deletions(-)

diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c
index 2f6a7b42fa31..1af3febb0419 100644
--- a/fs/reiserfs/prints.c
+++ b/fs/reiserfs/prints.c
@@ -6,6 +6,7 @@
 #include <linux/fs.h>
 #include "reiserfs.h"
 #include <linux/string.h>
+#include <linux/ctype.h>
 #include <linux/buffer_head.h>
 
 #include <stdarg.h>
@@ -76,83 +77,111 @@ static char *le_type(struct reiserfs_key *key)
 }
 
 /* %k */
-static void sprintf_le_key(char *buf, struct reiserfs_key *key)
+static int snprintf_le_key(char *buf, size_t size, struct reiserfs_key *key)
 {
-	if (key)
-		sprintf(buf, "[%d %d %s %s]", le32_to_cpu(key->k_dir_id),
+	if (!key)
+		return snprintf(buf, size, "[NULL]");
+
+	return snprintf(buf, size, "[%d %d %s %s]", le32_to_cpu(key->k_dir_id),
 			le32_to_cpu(key->k_objectid), le_offset(key),
 			le_type(key));
-	else
-		sprintf(buf, "[NULL]");
 }
 
 /* %K */
-static void sprintf_cpu_key(char *buf, struct cpu_key *key)
+static int snprintf_cpu_key(char *buf, size_t size, struct cpu_key *key)
 {
-	if (key)
-		sprintf(buf, "[%d %d %s %s]", key->on_disk_key.k_dir_id,
+	if (!key)
+		return snprintf(buf, size, "[NULL]");
+
+	return snprintf(buf, size, "[%d %d %s %s]", key->on_disk_key.k_dir_id,
 			key->on_disk_key.k_objectid, reiserfs_cpu_offset(key),
 			cpu_type(key));
-	else
-		sprintf(buf, "[NULL]");
 }
 
-static void sprintf_de_head(char *buf, struct reiserfs_de_head *deh)
+static int snprintf_de_head(char *buf, size_t size,
+			    struct reiserfs_de_head *deh)
 {
-	if (deh)
-		sprintf(buf,
-			"[offset=%d dir_id=%d objectid=%d location=%d state=%04x]",
+	if (!deh)
+		return snprintf(buf, size, "[NULL]");
+
+	return snprintf(buf, size,
+		        "[offset=%d dir_id=%d objectid=%d location=%d state=%04x]",
 			deh_offset(deh), deh_dir_id(deh), deh_objectid(deh),
 			deh_location(deh), deh_state(deh));
-	else
-		sprintf(buf, "[NULL]");
-
 }
 
-static void sprintf_item_head(char *buf, struct item_head *ih)
+static int snprintf_item_head(char *buf, size_t size, struct item_head *ih)
 {
-	if (ih) {
-		strcpy(buf,
-		       (ih_version(ih) == KEY_FORMAT_3_6) ? "*3.6* " : "*3.5*");
-		sprintf_le_key(buf + strlen(buf), &(ih->ih_key));
-		sprintf(buf + strlen(buf), ", item_len %d, item_location %d, "
-			"free_space(entry_count) %d",
-			ih_item_len(ih), ih_location(ih), ih_free_space(ih));
-	} else
-		sprintf(buf, "[NULL]");
+	const char *key_format;
+	int used = 0;
+	if (!ih)
+		return snprintf(buf, size, "[NULL]");
+
+	key_format = (ih_version(ih) == KEY_FORMAT_3_6) ? "*3.6*" : "*3.5*";
+	used += snprintf(buf, size, "%s", key_format);
+	if (used > size)
+		return used;
+
+	used += snprintf_le_key(buf + used, size - used, &(ih->ih_key));
+	if (used > size)
+		return used;
+
+	used += snprintf(buf + used, size - used,
+			 ", item_len %d, item_location %d, free_space(entry_count) %d",
+			 ih_item_len(ih), ih_location(ih), ih_free_space(ih));
+	return used;
 }
 
-static void sprintf_direntry(char *buf, struct reiserfs_dir_entry *de)
+static int snprintf_direntry(char *buf, size_t size,
+			     struct reiserfs_dir_entry *de)
 {
 	char name[20];
 
-	memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen);
-	name[de->de_namelen > 19 ? 19 : de->de_namelen] = 0;
-	sprintf(buf, "\"%s\"==>[%d %d]", name, de->de_dir_id, de->de_objectid);
+	if (!de)
+		return snprintf(buf, size, "[NULL]");
+
+	if (de->de_name) {
+		memcpy(name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen);
+		name[de->de_namelen > 19 ? 19 : de->de_namelen] = 0;
+	} else
+		strcpy(name, "[NULL]");
+	return snprintf(buf, size, "\"%s\"==>[%d %d]",
+			name, de->de_dir_id, de->de_objectid);
 }
 
-static void sprintf_block_head(char *buf, struct buffer_head *bh)
+static int snprintf_block_head(char *buf, size_t size, struct buffer_head *bh)
 {
-	sprintf(buf, "level=%d, nr_items=%d, free_space=%d rdkey ",
-		B_LEVEL(bh), B_NR_ITEMS(bh), B_FREE_SPACE(bh));
+	if (!bh)
+		return snprintf(buf, size, "[NULL]");
+
+	return snprintf(buf, size,
+			"level=%d, nr_items=%d, free_space=%d rdkey ",
+			B_LEVEL(bh), B_NR_ITEMS(bh), B_FREE_SPACE(bh));
 }
 
-static void sprintf_buffer_head(char *buf, struct buffer_head *bh)
+static int snprintf_buffer_head(char *buf, size_t size, struct buffer_head *bh)
 {
-	sprintf(buf,
-		"dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)",
-		bh->b_bdev, bh->b_size,
-		(unsigned long long)bh->b_blocknr, atomic_read(&(bh->b_count)),
-		bh->b_state, bh->b_page,
-		buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE",
-		buffer_dirty(bh) ? "DIRTY" : "CLEAN",
-		buffer_locked(bh) ? "LOCKED" : "UNLOCKED");
+	if (!bh)
+		return snprintf(buf, size, "[NULL]");
+
+	return snprintf(buf, size,
+			"dev %pg, size %zd, blocknr %llu, count %d, state 0x%lx, page %p, (%s, %s, %s)",
+			bh->b_bdev, bh->b_size,
+			(unsigned long long)bh->b_blocknr,
+			atomic_read(&bh->b_count),
+			bh->b_state, bh->b_page,
+			buffer_uptodate(bh) ? "UPTODATE" : "!UPTODATE",
+			buffer_dirty(bh) ? "DIRTY" : "CLEAN",
+			buffer_locked(bh) ? "LOCKED" : "UNLOCKED");
 }
 
-static void sprintf_disk_child(char *buf, struct disk_child *dc)
+static int snprintf_disk_child(char *buf, size_t size, struct disk_child *dc)
 {
-	sprintf(buf, "[dc_number=%d, dc_size=%u]", dc_block_number(dc),
-		dc_size(dc));
+	if (!dc)
+		return snprintf(buf, size, "[NULL]");
+
+	return snprintf(buf, size, "[dc_number=%d, dc_size=%u]",
+			dc_block_number(dc), dc_size(dc));
 }
 
 static char *is_there_reiserfs_struct(char *fmt, int *what)
@@ -160,8 +189,14 @@ static char *is_there_reiserfs_struct(char *fmt, int *what)
 	char *k = fmt;
 
 	while ((k = strchr(k, '%')) != NULL) {
-		if (k[1] == 'k' || k[1] == 'K' || k[1] == 'h' || k[1] == 't' ||
-		    k[1] == 'z' || k[1] == 'b' || k[1] == 'y' || k[1] == 'a') {
+		if (k[1] == 'k' || k[1] == 'K' || k[1] == 't' ||
+		    k[1] == 'b' || k[1] == 'y' || k[1] == 'a') {
+			*what = k[1];
+			break;
+		}
+
+		/* We can't gobble up normal qualifiers */
+		if ((k[1] == 'z' || k[1] == 'h') && !isalpha(k[2])) {
 			*what = k[1];
 			break;
 		}
@@ -190,6 +225,8 @@ static void prepare_error_buf(const char *fmt, va_list args)
 	char *k;
 	char *p = error_buf;
 	int what;
+	int left = sizeof(error_buf);
+	int bytes = 0;
 
 	spin_lock(&error_lock);
 
@@ -198,46 +235,72 @@ static void prepare_error_buf(const char *fmt, va_list args)
 	while ((k = is_there_reiserfs_struct(fmt1, &what)) != NULL) {
 		*k = 0;
 
-		p += vsprintf(p, fmt1, args);
+		bytes = vsnprintf(p, left, fmt1, args);
+		if (bytes >= left)
+			goto out;
+
+		p += bytes;
+		left -= bytes;
 
 		switch (what) {
-		case 'k':
-			sprintf_le_key(p, va_arg(args, struct reiserfs_key *));
-			break;
-		case 'K':
-			sprintf_cpu_key(p, va_arg(args, struct cpu_key *));
+		case 'k': {
+			struct reiserfs_key *key;
+			key = va_arg(args, struct reiserfs_key *);
+			bytes = snprintf_le_key(p, left, key);
 			break;
-		case 'h':
-			sprintf_item_head(p, va_arg(args, struct item_head *));
+		}
+		case 'K': {
+			struct cpu_key *key;
+			key = va_arg(args, struct cpu_key *);
+			bytes = snprintf_cpu_key(p, left, key);
 			break;
-		case 't':
-			sprintf_direntry(p,
-					 va_arg(args,
-						struct reiserfs_dir_entry *));
+		}
+		case 'h': {
+			struct item_head *ih;
+			ih = va_arg(args, struct item_head *);
+			bytes = snprintf_item_head(p, left, ih);
 			break;
-		case 'y':
-			sprintf_disk_child(p,
-					   va_arg(args, struct disk_child *));
+		}
+		case 't': {
+			struct reiserfs_dir_entry *de;
+			de = va_arg(args, struct reiserfs_dir_entry *);
+			bytes = snprintf_direntry(p, left, de);
 			break;
-		case 'z':
-			sprintf_block_head(p,
-					   va_arg(args, struct buffer_head *));
+		}
+		case 'y': {
+			struct disk_child *dc;
+			dc = va_arg(args, struct disk_child *);
+			bytes = snprintf_disk_child(p, left, dc);
 			break;
-		case 'b':
-			sprintf_buffer_head(p,
-					    va_arg(args, struct buffer_head *));
+		}
+		case 'z': {
+			struct buffer_head *bh;
+			bh = va_arg(args, struct buffer_head *);
+			bytes = snprintf_block_head(p, left, bh);
 			break;
-		case 'a':
-			sprintf_de_head(p,
-					va_arg(args,
-					       struct reiserfs_de_head *));
+		}
+		case 'b': {
+			struct buffer_head *bh;
+			bh = va_arg(args, struct buffer_head *);
+			bytes = snprintf_buffer_head(p, left, bh);
 			break;
 		}
+		case 'a': {
+			struct reiserfs_de_head *deh;
+			deh = va_arg(args, struct reiserfs_de_head *);
+			bytes = snprintf_de_head(p, left, deh);
+			break;
+		}};
+
+		if (bytes >= left)
+			goto out;
 
-		p += strlen(p);
+		p += bytes;
+		left -= bytes;
 		fmt1 = k + 2;
 	}
-	vsprintf(p, fmt1, args);
+out:
+	vsnprintf(p, left, fmt1, args);
 	spin_unlock(&error_lock);
 
 }
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe reiserfs-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux File System Development]     [Linux BTRFS]     [Linux NFS]     [Linux Filesystems]     [Ext4 Filesystem]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Device Mapper]     [Linux Resources]

  Powered by Linux