[PATCH] jfs: bound Unicode name conversion to the dirent buffer

Pengpeng Hou posted 1 patch 1 week, 4 days ago
fs/jfs/jfs_dtree.c   | 23 +++++++++++++++++++----
fs/jfs/jfs_unicode.c | 21 ++++++++++++++++-----
fs/jfs/jfs_unicode.h |  3 ++-
3 files changed, 37 insertions(+), 10 deletions(-)
[PATCH] jfs: bound Unicode name conversion to the dirent buffer
Posted by Pengpeng Hou 1 week, 4 days ago
jfs_readdir() only checks that d->namlen characters fit in the page,
but jfs_strfromUCS_le() can expand each UCS-2 character into multiple
output bytes when a codepage is active and then always appends a NUL.
That lets a long multi-byte name run past the end of the temporary
dirent page.

Make jfs_strfromUCS_le() size-aware and stop the readdir walk when the
converted name no longer fits in the remaining dirent buffer space.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 fs/jfs/jfs_dtree.c   | 23 +++++++++++++++++++----
 fs/jfs/jfs_unicode.c | 21 ++++++++++++++++-----
 fs/jfs/jfs_unicode.h |  3 ++-
 3 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
index 9ab3f2fc61d1..62c1ff7a2a38 100644
--- a/fs/jfs/jfs_dtree.c
+++ b/fs/jfs/jfs_dtree.c
@@ -2964,8 +2964,15 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 			}
 
 			/* copy the name of head/only segment */
-			outlen = jfs_strfromUCS_le(name_ptr, d->name, len,
-						   codepage);
+			outlen = jfs_strfromUCS_le(name_ptr,
+						   dirent_buf + PAGE_SIZE -
+						   (unsigned long)name_ptr,
+						   d->name, len, codepage);
+			if (outlen < 0) {
+				index = i;
+				overflow = 1;
+				break;
+			}
 			jfs_dirent->name_len = outlen;
 
 			/* copy name in the additional segment(s) */
@@ -2984,8 +2991,16 @@ int jfs_readdir(struct file *file, struct dir_context *ctx)
 					goto skip_one;
 				}
 				len = min(d_namleft, DTSLOTDATALEN);
-				outlen = jfs_strfromUCS_le(name_ptr, t->name,
-							   len, codepage);
+				outlen = jfs_strfromUCS_le(name_ptr,
+							   dirent_buf + PAGE_SIZE -
+							   (unsigned long)name_ptr,
+							   t->name, len,
+							   codepage);
+				if (outlen < 0) {
+					index = i;
+					overflow = 1;
+					break;
+				}
 				jfs_dirent->name_len += outlen;
 
 				next = t->next;
diff --git a/fs/jfs/jfs_unicode.c b/fs/jfs/jfs_unicode.c
index 0c1e9027245a..3ac6fd88a7eb 100644
--- a/fs/jfs/jfs_unicode.c
+++ b/fs/jfs/jfs_unicode.c
@@ -16,7 +16,7 @@
  * FUNCTION:	Convert little-endian unicode string to character string
  *
  */
-int jfs_strfromUCS_le(char *to, const __le16 * from,
+int jfs_strfromUCS_le(char *to, size_t to_size, const __le16 *from,
 		      int len, struct nls_table *codepage)
 {
 	int i;
@@ -24,13 +24,22 @@ int jfs_strfromUCS_le(char *to, const __le16 * from,
 	static int warn_again = 5;	/* Only warn up to 5 times total */
 	int warn = !!warn_again;	/* once per string */
 
+	if (!to_size)
+		return -ENAMETOOLONG;
+
 	if (codepage) {
 		for (i = 0; (i < len) && from[i]; i++) {
 			int charlen;
+
+			if (outlen >= to_size - 1)
+				return -ENAMETOOLONG;
+
 			charlen =
 			    codepage->uni2char(le16_to_cpu(from[i]),
 					       &to[outlen],
-					       NLS_MAX_CHARSET_SIZE);
+					       min_t(size_t,
+						     NLS_MAX_CHARSET_SIZE,
+						     to_size - outlen - 1));
 			if (charlen > 0)
 				outlen += charlen;
 			else
@@ -38,8 +47,11 @@ int jfs_strfromUCS_le(char *to, const __le16 * from,
 		}
 	} else {
 		for (i = 0; (i < len) && from[i]; i++) {
+			if (outlen >= to_size - 1)
+				return -ENAMETOOLONG;
+
 			if (unlikely(le16_to_cpu(from[i]) & 0xff00)) {
-				to[i] = '?';
+				to[outlen++] = '?';
 				if (unlikely(warn)) {
 					warn--;
 					warn_again--;
@@ -52,9 +64,8 @@ int jfs_strfromUCS_le(char *to, const __le16 * from,
 
 			}
 			else
-				to[i] = (char) (le16_to_cpu(from[i]));
+				to[outlen++] = (char)(le16_to_cpu(from[i]));
 		}
-		outlen = i;
 	}
 	to[outlen] = 0;
 	return outlen;
diff --git a/fs/jfs/jfs_unicode.h b/fs/jfs/jfs_unicode.h
index b6a78d4aef1b..39b5710891ef 100644
--- a/fs/jfs/jfs_unicode.h
+++ b/fs/jfs/jfs_unicode.h
@@ -12,7 +12,8 @@
 #include "jfs_types.h"
 
 extern int get_UCSname(struct component_name *, struct dentry *);
-extern int jfs_strfromUCS_le(char *, const __le16 *, int, struct nls_table *);
+extern int jfs_strfromUCS_le(char *to, size_t to_size, const __le16 *from,
+			     int len, struct nls_table *codepage);
 
 #define free_UCSname(COMP) kfree((COMP)->name)
 
-- 
2.50.1 (Apple Git-155)